"""Workflow: Erweiterte Rechnungs-Suche mit voller Filter-Palette.

Ergaenzt `filter_invoices.py` (das nur mit Lieferant arbeitet) um die
gesamte Filter-Palette von `getDocumentList`. Geht direkt über die API
und kombiniert beliebige Filter.

Aufruf:
  python search_documents.py --text "Hosting" --from 2026-01-01 --to 2026-04-06
  python search_documents.py --status open --from 2026-01-01 --by-month
  python search_documents.py --type 1,2 --note "Server" --updated-since 2026-04-01
  python search_documents.py --tag eu --tag preferred --max-items 50 --format json
  python search_documents.py --no-archived --transaction-assigned

Filter (alle optional, beliebig kombinierbar):
  --text TEXT             Volltextsuche (searchQuery)
  --from YYYY-MM-DD       Startdatum (startDateFilter)
  --to YYYY-MM-DD         Enddatum (endDateFilter)
  --updated-since DATE    Modifikationsdatum-Filter (updatedOrNewSinceFilter)
  --company UID           companyFilter (int) — 0=alle, -1=unassigned, sonst companyUid
  --type CSV              documentTypeFilter, kommagetrennte Type-IDs
  --status STATUS         paymentStatusFilter (z.B. open, paid, overdue)
  --number STR            documentNumberFilter (Substring oder exakt)
  --note STR              noteFilter (Substring auf Notiz)
  --tag TAG               tagFilter[] — kann mehrfach übergeben werden
  --archived MODE         archivedFilter (0=nicht archiviert, 1=alle, 2=archiviert)
  --transaction-assigned        Nur Belege mit zugeordneter Transaktion
  --no-transaction-assigned     Nur Belege OHNE zugeordnete Transaktion
  --min FLOAT             Mindest-Brutto (Client-seitiger Filter, kein API-Param)
  --max FLOAT             Maximal-Brutto (Client-seitiger Filter)
  --max-items N           Pagination kappen (Default: alle)

Output:
  Default markdown mit Summen und Tabelle.
  --summary nur Summen, keine Detail-Tabelle.
  --by-month zusätzlich Monats-Aggregations-Tabelle.
  --format json gibt das volle Ergebnis als JSON aus.

Aggregationen werden aus filter_invoices wiederverwendet (DRY).
"""
from __future__ import annotations

import argparse
import json
import sys
from pathlib import Path
from typing import Any

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

sys.path.insert(0, str(Path(__file__).parent.parent))

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

# Aggregations- und Formatter-Helper aus filter_invoices wiederverwenden.
_WORKFLOWS = Path(__file__).parent
sys.path.insert(0, str(_WORKFLOWS))
import filter_invoices as fi  # noqa: E402


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

def build_filter_params(args: argparse.Namespace) -> dict[str, Any]:
    """Mappt CLI-Args auf API-Query-Parameter.

    Nur tatsächlich gesetzte Filter landen in den Params.
    Datumsfelder, die Client-seitig gefiltert werden (min/max), sind nicht enthalten.
    """
    params: dict[str, Any] = {}
    if args.text:
        params["searchQuery"] = args.text
    if args.date_from:
        params["startDateFilter"] = args.date_from
    if args.date_to:
        params["endDateFilter"] = args.date_to
    if args.updated_since:
        params["updatedOrNewSinceFilter"] = args.updated_since
    if args.company is not None:
        params["companyFilter"] = args.company
    if args.doc_type:
        # documentTypeFilter erwartet eine Liste/Array
        try:
            params["documentTypeFilter"] = [int(t.strip()) for t in args.doc_type.split(",") if t.strip()]
        except ValueError:
            params["documentTypeFilter"] = [t.strip() for t in args.doc_type.split(",") if t.strip()]
    if args.status:
        params["paymentStatusFilter"] = args.status
    if args.number:
        params["documentNumberFilter"] = args.number
    if args.note:
        params["noteFilter"] = args.note
    if args.tag:
        params["tagFilter[]"] = list(args.tag)
    if args.archived is not None:
        params["archivedFilter"] = args.archived
    if args.transaction_assigned:
        params["transactionAssigned"] = True
    if args.no_transaction_assigned:
        params["noTransactionAssigned"] = True
    return params


def search_documents(client: GmiClient, params: dict, max_items: int | None = None) -> list[dict]:
    """Paginiert getDocumentList mit den übergebenen Filtern.

    Stoppt entweder wenn die Pagination leer wird oder wenn max_items erreicht ist.
    """
    out: list[dict] = []
    for item in client.paginate("getDocumentList", items_key="records", page_size=100, **params):
        out.append(item)
        if max_items is not None and len(out) >= max_items:
            break
    return out


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

def main() -> int:
    p = argparse.ArgumentParser()
    # Such-/Filter-Optionen
    p.add_argument("--text", help="Volltextsuche (searchQuery)")
    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("--updated-since", dest="updated_since",
                   help="Nur Belege, die seit YYYY-MM-DD geaendert/neu sind")
    p.add_argument("--company", type=int,
                   help="companyFilter (int): 0=alle, -1=unassigned, sonst companyUid")
    p.add_argument("--type", dest="doc_type",
                   help="documentTypeFilter, kommagetrennte Type-IDs")
    p.add_argument("--status", help="paymentStatusFilter (z.B. open, paid, overdue)")
    p.add_argument("--number", help="documentNumberFilter")
    p.add_argument("--note", help="noteFilter (Substring auf Notiz)")
    p.add_argument("--tag", action="append",
                   help="tagFilter[] — kann mehrfach übergeben werden")
    p.add_argument("--archived", type=int, choices=[0, 1, 2],
                   help="archivedFilter: 0=nicht archiviert, 1=alle, 2=archiviert")
    p.add_argument("--transaction-assigned", dest="transaction_assigned",
                   action="store_true",
                   help="Nur Belege mit zugewiesener Transaktion")
    p.add_argument("--no-transaction-assigned", dest="no_transaction_assigned",
                   action="store_true",
                   help="Nur Belege OHNE zugewiesene Transaktion")

    # Client-seitige Betrags-Filter (kein API-Param)
    p.add_argument("--min", type=float, help="Mindest-Brutto (clientseitig)")
    p.add_argument("--max", type=float, help="Maximal-Brutto (clientseitig)")

    # Pagination-Cap
    p.add_argument("--max-items", dest="max_items", type=int,
                   help="Maximal so viele Treffer holen")

    # Aggregations-/Output-Optionen
    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 Monats-Aggregations-Tabelle")
    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

    params = build_filter_params(args)

    try:
        docs = search_documents(client, params, max_items=args.max_items)
    except (GmiHttpError, GmiValidationError) as e:
        print(json.dumps({"error": "api", "detail": str(e)}))
        return 1

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

    if args.format == "json":
        out = {
            "filter": params,
            "client_filter": {"min": args.min, "max": args.max},
            "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("# Rechnungs-Suche")
        print()
        # Filter-Echo
        active_filters = []
        if args.text:
            active_filters.append(f"text='{args.text}'")
        if args.date_from or args.date_to:
            active_filters.append(f"Zeitraum {args.date_from or '?'} bis {args.date_to or '?'}")
        if args.company is not None:
            active_filters.append(f"company={args.company}")
        if args.doc_type:
            active_filters.append(f"type={args.doc_type}")
        if args.status:
            active_filters.append(f"status={args.status}")
        if args.number:
            active_filters.append(f"number~'{args.number}'")
        if args.note:
            active_filters.append(f"note~'{args.note}'")
        if args.tag:
            active_filters.append(f"tags={','.join(args.tag)}")
        if args.archived is not None:
            active_filters.append(f"archived={args.archived}")
        if args.transaction_assigned:
            active_filters.append("transaction-assigned")
        if args.no_transaction_assigned:
            active_filters.append("no-transaction-assigned")
        if args.updated_since:
            active_filters.append(f"updated-since={args.updated_since}")
        if args.min is not None or args.max is not None:
            active_filters.append(f"betrag={args.min}..{args.max}")
        if args.max_items:
            active_filters.append(f"max-items={args.max_items}")
        if active_filters:
            print("**Filter:** " + " | ".join(active_filters))
            print()
        print(fi.format_totals_markdown(totals))
        if by_month is not None:
            print()
            print(fi.format_by_month_markdown(by_month))
        if not args.summary:
            print()
            print("## Rechnungen")
            print()
            print(fi.format_table_markdown(docs))

    return 0


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