"""Admin dashboard aggregates over existing B-43 eligible events and review queues.

No fabricated demo amounts. Opening balances are unavailable until B-45; peer
group ``opening`` is always null and ``ending`` equals period net change.
Period closing status is unknown without the monthly-close module — returned
as null so the UI shows an em dash rather than a guessed label.
"""

from __future__ import annotations

from datetime import datetime, timedelta, timezone
from decimal import Decimal, ROUND_HALF_UP
import sqlite3


WAN = Decimal("10000")
ZERO = Decimal("0")
TWOPLACES = Decimal("0.01")


def today_shanghai() -> str:
    return datetime.now(timezone(timedelta(hours=8))).date().isoformat()


def _q2(value: Decimal) -> str:
    return str(value.quantize(TWOPLACES, rounding=ROUND_HALF_UP))


def _to_wan(amount: Decimal) -> str:
    return _q2(amount / WAN)


def _parse_day(iso_ts: str | None) -> str | None:
    if not iso_ts:
        return None
    text = str(iso_ts)
    return text[:10] if len(text) >= 10 else None


def audit_counts(connection: sqlite3.Connection) -> dict[str, int]:
    """Derive pending review counts from real queues only.

    Priority mapping (aligned with current admin audit UI semantics):
    - high: unresolved / needs_review match exceptions
    - medium: pending bank-account registrations + pending manual records
    - low: reserved for future low-risk queues (currently always 0)

    Homepage card, sidebar badge and audit-center heading must all read this
    same payload (via ``/api/admin/dashboard`` → ``audit``).
    """
    high = connection.execute(
        """
        SELECT COUNT(*) AS n
        FROM current_transfer_decisions c
        JOIN transfer_match_decisions d ON d.id = c.decision_id
        JOIN canonical_transfer_events e ON e.id = c.event_id
        WHERE e.lifecycle = 'active'
          AND d.classification IN ('unresolved', 'needs_review')
        """
    ).fetchone()["n"]
    medium_accounts = connection.execute(
        "SELECT COUNT(*) AS n FROM bank_accounts WHERE status = 'pending'"
    ).fetchone()["n"]
    medium_manuals = connection.execute(
        """
        SELECT COUNT(*) AS n
        FROM manual_records m
        JOIN current_manual_record_decisions c ON c.record_id = m.id
        JOIN manual_record_decisions d ON d.id = c.decision_id
        WHERE d.state = 'pending'
        """
    ).fetchone()["n"]
    medium = int(medium_accounts) + int(medium_manuals)
    low = 0
    return {
        "total": int(high) + int(medium) + int(low),
        "high": int(high),
        "medium": int(medium),
        "low": int(low),
    }


def _load_eligible(
    connection: sqlite3.Connection, *, from_date: str, cutoff: str
) -> list[sqlite3.Row]:
    return connection.execute(
        """
        SELECT e.event_id, e.decision_id, e.effective_at, e.amount, e.currency,
               e.payer_company_id, e.payee_company_id, e.pairing,
               cpayer.name AS payer_name, cpayee.name AS payee_name
        FROM eligible_intercompany_events e
        JOIN companies cpayer ON cpayer.id = e.payer_company_id
        JOIN companies cpayee ON cpayee.id = e.payee_company_id
        WHERE date(e.effective_at) >= date(?)
          AND date(e.effective_at) <= date(?)
        ORDER BY e.effective_at, e.event_id
        """,
        (from_date, cutoff),
    ).fetchall()


def _company_rows(connection: sqlite3.Connection) -> list[sqlite3.Row]:
    return connection.execute(
        """
        SELECT id, name FROM companies
        WHERE status != 'disabled'
        ORDER BY id
        """
    ).fetchall()


def company_summaries(
    connection: sqlite3.Connection, *, from_date: str, cutoff: str
) -> tuple[list[dict[str, object]], dict[str, object]]:
    companies = _company_rows(connection)
    events = _load_eligible(connection, from_date=from_date, cutoff=cutoff)

    debit_total = ZERO
    credit_total = ZERO
    by_id: dict[int, dict[str, object]] = {}
    for company in companies:
        by_id[int(company["id"])] = {
            "id": int(company["id"]),
            "name": company["name"],
            "detail_count": 0,
            "debit": ZERO,
            "credit": ZERO,
            "period_status": None,
            "period_status_label": "—",
        }

    for event in events:
        amount = Decimal(str(event["amount"]))
        debit_total += amount
        credit_total += amount
        payer_id = int(event["payer_company_id"])
        payee_id = int(event["payee_company_id"])
        if payer_id in by_id:
            row = by_id[payer_id]
            row["detail_count"] = int(row["detail_count"]) + 1
            row["debit"] = Decimal(row["debit"]) + amount
        if payee_id in by_id:
            row = by_id[payee_id]
            row["detail_count"] = int(row["detail_count"]) + 1
            row["credit"] = Decimal(row["credit"]) + amount

    items: list[dict[str, object]] = []
    for company in companies:
        row = by_id[int(company["id"])]
        debit = Decimal(row["debit"])
        credit = Decimal(row["credit"])
        # Payee inflow − payer outflow = signed net from this company's view.
        net = credit - debit
        items.append(
            {
                "id": row["id"],
                "name": row["name"],
                "detail_count": row["detail_count"],
                "debit_wan": _to_wan(debit),
                "credit_wan": _to_wan(credit),
                "net_wan": _to_wan(net),
                "period_status": row["period_status"],
                "period_status_label": row["period_status_label"],
            }
        )

    totals = {
        "company_count": len(items),
        "detail_count": len(events),
        "debit_wan": _to_wan(debit_total),
        "credit_wan": _to_wan(credit_total),
        "net_wan": _to_wan(debit_total - credit_total),
    }
    return items, totals


def company_peer_groups(
    connection: sqlite3.Connection,
    company_id: int,
    *,
    from_date: str,
    cutoff: str,
) -> dict[str, object]:
    company = connection.execute(
        "SELECT id, name FROM companies WHERE id = ?", (company_id,)
    ).fetchone()
    if company is None:
        raise KeyError(company_id)

    events = connection.execute(
        """
        SELECT e.event_id, e.effective_at, e.amount, e.currency,
               e.payer_company_id, e.payee_company_id,
               cpayer.name AS payer_name, cpayee.name AS payee_name
        FROM eligible_intercompany_events e
        JOIN companies cpayer ON cpayer.id = e.payer_company_id
        JOIN companies cpayee ON cpayee.id = e.payee_company_id
        WHERE (e.payer_company_id = ? OR e.payee_company_id = ?)
          AND date(e.effective_at) >= date(?)
          AND date(e.effective_at) <= date(?)
        ORDER BY e.effective_at, e.event_id
        """,
        (company_id, company_id, from_date, cutoff),
    ).fetchall()

    groups: dict[int, dict[str, object]] = {}
    for event in events:
        amount = Decimal(str(event["amount"]))
        payer_id = int(event["payer_company_id"])
        payee_id = int(event["payee_company_id"])
        if payer_id == company_id:
            peer_id = payee_id
            peer_name = event["payee_name"]
            direction = "debit"
            summary = f"付往 {peer_name}"
        else:
            peer_id = payer_id
            peer_name = event["payer_name"]
            direction = "credit"
            summary = f"收自 {peer_name}"

        bucket = groups.get(peer_id)
        if bucket is None:
            bucket = {
                "peer_id": peer_id,
                "peer_name": peer_name,
                "count": 0,
                "opening": None,
                "opening_status": "unavailable",
                "debit": ZERO,
                "credit": ZERO,
                "lines": [],
            }
            groups[peer_id] = bucket

        bucket["count"] = int(bucket["count"]) + 1
        if direction == "debit":
            bucket["debit"] = Decimal(bucket["debit"]) + amount
        else:
            bucket["credit"] = Decimal(bucket["credit"]) + amount

        day = _parse_day(event["effective_at"]) or ""
        bucket["lines"].append(
            {
                "event_id": int(event["event_id"]),
                "date": day,
                "direction": direction,
                "summary": summary,
                "amount_wan": _to_wan(amount),
                "currency": event["currency"] or "CNY",
            }
        )

    result_groups: list[dict[str, object]] = []
    for peer_id in sorted(groups.keys(), key=lambda i: groups[i]["peer_name"]):
        bucket = groups[peer_id]
        debit = Decimal(bucket["debit"])
        credit = Decimal(bucket["credit"])
        ending = credit - debit
        result_groups.append(
            {
                "peer_id": bucket["peer_id"],
                "peer_name": bucket["peer_name"],
                "count": bucket["count"],
                "opening": None,
                "opening_status": "unavailable",
                "debit_wan": _to_wan(debit),
                "credit_wan": _to_wan(credit),
                "ending_wan": _to_wan(ending),
                "result_kind": "period_net_change",
                "lines": bucket["lines"],
            }
        )

    return {
        "company_id": int(company["id"]),
        "company_name": company["name"],
        "from_date": from_date,
        "cutoff": cutoff,
        "groups": result_groups,
    }


def weekly_flow(
    connection: sqlite3.Connection, *, cutoff: str, days: int = 7
) -> dict[str, object]:
    end = datetime.strptime(cutoff, "%Y-%m-%d").date()
    start = end - timedelta(days=days - 1)
    labels: list[str] = []
    inflow = [ZERO] * days
    outflow = [ZERO] * days
    index: dict[str, int] = {}
    for offset in range(days):
        day = start + timedelta(days=offset)
        key = day.isoformat()
        index[key] = offset
        labels.append(f"{day.month:02d}-{day.day:02d}")

    rows = connection.execute(
        """
        SELECT date(e.effective_at) AS day, e.amount
        FROM eligible_intercompany_events e
        WHERE date(e.effective_at) >= date(?)
          AND date(e.effective_at) <= date(?)
        """,
        (start.isoformat(), cutoff),
    ).fetchall()
    for row in rows:
        day = row["day"]
        if day not in index:
            continue
        amount = Decimal(str(row["amount"]))
        # Group-level flow: every eligible transfer is both an outflow (payer)
        # and an inflow (payee); plot both series with the same absolute amount.
        inflow[index[day]] += amount
        outflow[index[day]] += amount

    return {
        "labels": labels,
        "inflow_wan": [_to_wan(v) for v in inflow],
        "outflow_wan": [_to_wan(v) for v in outflow],
    }


def build_dashboard(
    connection: sqlite3.Connection,
    *,
    from_date: str = "2026-01-01",
    cutoff: str | None = None,
) -> dict[str, object]:
    cutoff_date = cutoff or today_shanghai()
    try:
        datetime.strptime(from_date, "%Y-%m-%d")
        datetime.strptime(cutoff_date, "%Y-%m-%d")
    except ValueError as exc:
        raise ValueError("日期必须是 YYYY-MM-DD") from exc
    if from_date > cutoff_date:
        raise ValueError("from 不能晚于 cutoff")

    period = datetime.strptime(cutoff_date, "%Y-%m-%d")
    # Display month for the status column header: use the calendar month of cutoff.
    period_month = period.month
    companies, totals = company_summaries(
        connection, from_date=from_date, cutoff=cutoff_date
    )
    return {
        "from_date": from_date,
        "cutoff": cutoff_date,
        "period_month": period_month,
        "period_label": f"{period.year}-{period.month:02d}",
        "audit": audit_counts(connection),
        "totals": totals,
        "companies": companies,
        "weekly_flow": weekly_flow(connection, cutoff=cutoff_date),
        "opening_status": "unavailable",
    }
