"""Workflow: Rechnungen filtern nach Lieferant, Zeitraum, Mindestbetrag.

Aufruf:
  python filter_invoices.py --supplier "Telekom" --from 2026-01-01 --to 2026-04-06 --min 100
  python filter_invoices.py --supplier "Telekom" --format json
  python filter_invoices.py --supplier "Amazon" --from 2026-01-01 --summary
  python filter_invoices.py --supplier "Amazon" --by-month

Aggregationen (Phase 2 / Punkt 6a):
  --summary    fasst nur Total und Pro-Currency-Summen aus, ohne Detail-Tabelle
  --by-month   ergaenzt eine Monats-Tabelle (Anzahl + Summe pro Monat, pro Currency)

Aggregationen sind in beiden Output-Formaten (markdown/json) verfuegbar.
Im JSON-Output stehen sie unter den Keys 'totals' und (optional) 'by_month'.
"""
from __future__ import annotations

import argparse
import json
import sys
from collections import defaultdict
from pathlib import Path

try:
    sys.stdout.reconfigure(encoding="utf-8")
except (AttributeError, Exception):
    pass

# Parent-Ordner zum Pfad für gmi_client Import
sys.path.insert(0, str(Path(__file__).parent.parent))

from gmi_client import GmiClient, GmiHttpError, GmiValidationError  # noqa: E402


# ---------- Fetcher ----------

def find_company_ids(client: GmiClient, name: str) -> list[dict]:
    """Sucht Companies, deren Name den Suchbegriff enthält (case-insensitive).

    getCompanyList liefert eine Liste von Company-Dicts direkt zurück (kein Wrapper).
    """
    needle = name.lower()
    matches: list[dict] = []
    response = client.call("getCompanyList")
    companies = response if isinstance(response, list) else []
    for c in companies:
        cname = (c.get("name") or "").lower()
        if needle in cname:
            matches.append(c)
    return matches


def fetch_documents(
    client: GmiClient,
    company_uid: int,
    date_from: str | None,
    date_to: str | None,
) -> list[dict]:
    """Listet Dokumente eines Lieferanten via companyFilter (=companyUid).

    getDocumentList liefert ein Dict mit 'records' (Items), 'totalCount', 'maxPages' etc.
    """
    params: dict = {"companyFilter": company_uid}
    if date_from:
        params["startDateFilter"] = date_from
    if date_to:
        params["endDateFilter"] = date_to
    return list(client.paginate("getDocumentList", items_key="records", page_size=100, **params))


def filter_by_amount(
    docs: list[dict],
    min_amount: float | None,
    max_amount: float | None = None,
) -> list[dict]:
    """Filtert Dokumente nach grossAmount (Brutto).

    `amount` ist Legacy-Fallback; das echte Feld in der API heißt `grossAmount`.
    """
    if min_amount is None and max_amount is None:
        return docs
    out = []
    for d in docs:
        amount = d.get("grossAmount")
        if amount is None:
            amount = d.get("amount")
        if amount is None:
            continue
        try:
            val = float(amount)
        except (TypeError, ValueError):
            continue
        if min_amount is not None and val < min_amount:
            continue
        if max_amount is not None and val > max_amount:
            continue
        out.append(d)
    return out


# ---------- Aggregation ----------

def _doc_amount(doc: dict) -> float | None:
    raw = doc.get("grossAmount")
    if raw is None:
        raw = doc.get("amount")
    if raw is None:
        return None
    try:
        return float(raw)
    except (TypeError, ValueError):
        return None


def _doc_currency(doc: dict) -> str:
    cur = doc.get("currency") or doc.get("currencyCode") or "EUR"
    return str(cur).strip() or "EUR"


def _doc_month(doc: dict) -> str:
    """Liefert YYYY-MM aus documentDate. Bei fehlendem/kaputtem Datum: 'unknown'."""
    raw = doc.get("documentDate") or ""
    if isinstance(raw, str) and len(raw) >= 7 and raw[4] == "-":
        return raw[:7]
    return "unknown"


def aggregate_totals(docs: list[dict]) -> dict:
    """Summen pro Currency + Anzahl + Gesamt-Anzahl.

    Returns: {"count": int, "by_currency": {"EUR": {"sum": float, "count": int}, ...}}
    """
    by_currency: dict[str, dict] = defaultdict(lambda: {"sum": 0.0, "count": 0})
    for d in docs:
        amt = _doc_amount(d)
        cur = _doc_currency(d)
        by_currency[cur]["count"] += 1
        if amt is not None:
            by_currency[cur]["sum"] += amt
    # Runden auf 2 Nachkommastellen für saubere Ausgabe
    for cur in by_currency:
        by_currency[cur]["sum"] = round(by_currency[cur]["sum"], 2)
    return {"count": len(docs), "by_currency": dict(by_currency)}


def aggregate_by_month(docs: list[dict]) -> dict:
    """Summen pro (Monat, Currency).

    Returns: {"YYYY-MM": {"EUR": {"sum": float, "count": int}, ...}, ...}
    Sortiert nach Monat (Keys einer normalen dict-Iteration sind nach Insertion-Order;
    wir sortieren explizit beim Aufbau).
    """
    bucket: dict[str, dict[str, dict]] = {}
    for d in docs:
        month = _doc_month(d)
        cur = _doc_currency(d)
        amt = _doc_amount(d)
        if month not in bucket:
            bucket[month] = {}
        if cur not in bucket[month]:
            bucket[month][cur] = {"sum": 0.0, "count": 0}
        bucket[month][cur]["count"] += 1
        if amt is not None:
            bucket[month][cur]["sum"] += amt
    # Sortierte dict aufbauen, Summen runden
    out: dict[str, dict] = {}
    for month in sorted(bucket.keys()):
        out[month] = {}
        for cur, vals in bucket[month].items():
            out[month][cur] = {"sum": round(vals["sum"], 2), "count": vals["count"]}
    return out


# ---------- Formatter ----------

def format_table_markdown(docs: list[dict]) -> str:
    lines = ["| Datum | Belegnummer | Lieferant | Brutto | Währung | documentUid |",
             "|---|---|---|---|---|---|"]
    for d in docs:
        amt = _doc_amount(d)
        amt_str = f"{amt:.2f}".replace(".", ",") if amt is not None else ""
        lines.append("| {date} | {num} | {supplier} | {amount} | {cur} | {uid} |".format(
            date=d.get("documentDate", ""),
            num=d.get("documentNumber", ""),
            supplier=d.get("companyName", ""),
            amount=amt_str,
            cur=_doc_currency(d),
            uid=d.get("documentUid", ""),
        ))
    return "\n".join(lines)


def format_totals_markdown(totals: dict) -> str:
    lines = ["## Summen", ""]
    lines.append(f"- **Anzahl:** {totals['count']} Rechnungen")
    if not totals["by_currency"]:
        return "\n".join(lines)
    for cur in sorted(totals["by_currency"].keys()):
        v = totals["by_currency"][cur]
        amt = f"{v['sum']:.2f}".replace(".", ",")
        lines.append(f"- **{cur}:** {amt} ({v['count']} Rechnungen)")
    return "\n".join(lines)


def format_by_month_markdown(by_month: dict) -> str:
    lines = ["## Pro Monat", "",
             "| Monat | Währung | Anzahl | Summe |",
             "|---|---|---|---|"]
    for month in by_month:
        for cur in sorted(by_month[month].keys()):
            v = by_month[month][cur]
            amt = f"{v['sum']:.2f}".replace(".", ",")
            lines.append(f"| {month} | {cur} | {v['count']} | {amt} |")
    return "\n".join(lines)


# ---------- CLI ----------

def main() -> int:
    p = argparse.ArgumentParser()
    p.add_argument("--supplier", required=True, help="Lieferantenname (Substring-Match)")
    p.add_argument("--from", dest="date_from", help="Startdatum YYYY-MM-DD")
    p.add_argument("--to", dest="date_to", help="Enddatum YYYY-MM-DD")
    p.add_argument("--min", type=float, help="Mindestbetrag (grossAmount)")
    p.add_argument("--max", type=float, help="Maximalbetrag (grossAmount)")
    p.add_argument("--summary", action="store_true",
                   help="Nur Summen ausgeben, keine Detail-Tabelle")
    p.add_argument("--by-month", dest="by_month", action="store_true",
                   help="Zusätzlich eine Monats-Aggregations-Tabelle ausgeben")
    p.add_argument("--format", choices=["json", "markdown"], default="markdown")
    args = p.parse_args()

    try:
        client = GmiClient()
    except Exception as e:
        print(json.dumps({"error": "config", "detail": str(e)}))
        return 1

    matches = find_company_ids(client, args.supplier)
    if not matches:
        print(json.dumps({"error": "no_supplier", "query": args.supplier}))
        return 1
    if len(matches) > 1:
        print(json.dumps({
            "error": "ambiguous_supplier",
            "query": args.supplier,
            "matches": [{"companyUid": m.get("companyUid"), "name": m.get("name")} for m in matches],
            "hint": "Bitte praezisieren oder companyUid direkt angeben.",
        }, indent=2, ensure_ascii=False))
        return 1

    company = matches[0]
    company_uid = company.get("companyUid")

    try:
        docs = fetch_documents(client, company_uid, args.date_from, args.date_to)
    except (GmiHttpError, GmiValidationError) as e:
        print(json.dumps({"error": "api", "detail": str(e)}))
        return 1

    docs = filter_by_amount(docs, args.min, args.max)
    totals = aggregate_totals(docs)
    by_month = aggregate_by_month(docs) if args.by_month else None

    if args.format == "json":
        out = {
            "supplier": company.get("name"),
            "companyUid": company_uid,
            "count": len(docs),
            "totals": totals,
            "documents": [] if args.summary else docs,
        }
        if by_month is not None:
            out["by_month"] = by_month
        print(json.dumps(out, indent=2, ensure_ascii=False, default=str))
    else:
        print(f"# Rechnungs-Filter: {company.get('name')}")
        print()
        print(f"- **companyUid:** {company_uid}")
        if args.date_from or args.date_to:
            print(f"- **Zeitraum:** {args.date_from or '?'} bis {args.date_to or '?'}")
        if args.min is not None or args.max is not None:
            print(f"- **Betragsfilter:** min={args.min} max={args.max}")
        print()
        print(format_totals_markdown(totals))
        if by_month is not None:
            print()
            print(format_by_month_markdown(by_month))
        if not args.summary:
            print()
            print("## Rechnungen")
            print()
            print(format_table_markdown(docs))

    return 0


if __name__ == "__main__":
    sys.exit(main())
