"""Utilities for introspecting x402 ledger activity.

Provides functions to load, categorise, aggregate, and export entries from
the JSONL ledger produced by :class:`~x402_gateway.X402Gateway`.

Typical usage:

    from python.x402_dashboard import load_dashboard_entries, aggregate_totals

    entries = load_dashboard_entries(limit=50)
    totals  = aggregate_totals(entries)
    print(totals)  # {'ETH': 0.00042, 'ASM': 0.00021, ...}
"""

from __future__ import annotations

from datetime import datetime, timezone
from pathlib import Path
import json
from typing import Any, Iterable, Mapping, Sequence

from .x402_gateway import X402Gateway, get_default_gateway


# ---------------------------------------------------------------------------
# JSONL helpers
# ---------------------------------------------------------------------------

def _read_jsonl(path: Path) -> list[dict[str, Any]]:
    """Read all valid JSON lines from a JSONL file. Ignores blank/invalid lines."""
    if not path.exists():
        return []
    records: list[dict[str, Any]] = []
    for line in path.read_text(encoding="utf-8").splitlines():
        line = line.strip()
        if not line:
            continue
        try:
            records.append(json.loads(line))
        except json.JSONDecodeError:
            continue
    return records


def _normalise_timestamp(value: float | int | None) -> str:
    """Convert a unix timestamp to an ISO-8601 string in UTC."""
    if value is None:
        return datetime.now(timezone.utc).isoformat()
    return datetime.fromtimestamp(float(value), tz=timezone.utc).isoformat()


def _categorise_action(endpoint: str | None, event: str | None) -> str:
    """Derive a human-friendly action category from endpoint and event type."""
    if event == "webhook":
        return "Passive"
    if not endpoint:
        return "Codex"
    if endpoint.startswith("cli."):
        return "CLI"
    if endpoint.startswith("yield."):
        return "Passive"
    if endpoint.startswith("codex."):
        return "Codex"
    return "Passive"


# ---------------------------------------------------------------------------
# Public functions
# ---------------------------------------------------------------------------

def load_dashboard_entries(
    *,
    limit: int | None = None,
    gateway: X402Gateway | None = None,
) -> list[dict[str, Any]]:
    """Return ledger entries formatted for dashboard consumption.

    Entries are sorted in reverse chronological order (newest first).

    Parameters
    ----------
    limit:   Maximum number of entries to return (None = all).
    gateway: Gateway instance to read from (defaults to process singleton).

    Returns
    -------
    A list of normalised dashboard entry dicts, each containing:
      - timestamp:    ISO-8601 UTC string
      - action_type:  'CLI', 'Codex', or 'Passive'
      - endpoint:     Endpoint identifier or event type
      - token_payout: ``{'amount': float, 'currency': str}`` or None
      - status:       Entry status string
      - tx_hash:      Transaction hash (if present)
      - metadata:     Scrubbed metadata dict
    """
    gateway = gateway or get_default_gateway()
    raw_entries = _read_jsonl(gateway.ledger_path)
    processed: list[dict[str, Any]] = []

    for entry in raw_entries:
        endpoint = entry.get("endpoint")
        currency = entry.get("currency")
        amount = entry.get("amount")
        metadata = entry.get("metadata", {})
        if not isinstance(metadata, dict):
            metadata = {}
        tx_hash = metadata.get("tx_hash") or metadata.get("transaction_hash")
        status = entry.get("status") or metadata.get("status") or "recorded"
        processed.append(
            {
                "timestamp": _normalise_timestamp(entry.get("timestamp")),
                "action_type": _categorise_action(endpoint, entry.get("event")),
                "endpoint": endpoint or entry.get("event_type"),
                "token_payout": None
                if amount is None or currency is None
                else {
                    "amount": amount,
                    "currency": currency,
                },
                "status": status,
                "tx_hash": tx_hash or metadata.get("tx_status"),
                "metadata": metadata,
            }
        )

    processed.sort(key=lambda item: item["timestamp"], reverse=True)
    if limit is not None:
        processed = processed[:limit]
    return processed


def count_total_events(*, gateway: X402Gateway | None = None) -> int:
    """Return the total number of events recorded in the ledger."""
    gateway = gateway or get_default_gateway()
    return len(_read_jsonl(gateway.ledger_path))


def aggregate_totals(entries: Sequence[Mapping[str, Any]]) -> dict[str, float]:
    """Sum token payouts by currency across a sequence of dashboard entries.

    Parameters
    ----------
    entries: Output of :func:`load_dashboard_entries`.

    Returns
    -------
    A dict mapping currency symbol to total amount, e.g.
    ``{'ETH': 0.00073, 'ASM': 0.00021}``.
    """
    totals: dict[str, float] = {}
    for entry in entries:
        payout = entry.get("token_payout") if isinstance(entry, Mapping) else None
        if isinstance(payout, Mapping):
            amount = payout.get("amount")
            currency = payout.get("currency")
            if isinstance(amount, (int, float)) and isinstance(currency, str):
                totals[currency] = round(totals.get(currency, 0.0) + float(amount), 12)
    return totals


def export_dashboard(
    entries: Iterable[Mapping[str, Any]],
    destination: Path,
    *,
    format: str = "vaultledger",
) -> Path:
    """Export dashboard entries to a file.

    Parameters
    ----------
    entries:     Iterable of dashboard entry dicts.
    destination: Output file path.
    format:      'json' for pretty JSON, 'vaultledger' for compact JSONL.

    Returns the destination path.
    """
    destination.parent.mkdir(parents=True, exist_ok=True)
    if format == "json":
        with destination.open("w", encoding="utf-8") as fp:
            json.dump(list(entries), fp, indent=2)
        return destination

    # Default: vaultledger JSONL format
    with destination.open("w", encoding="utf-8") as fp:
        fp.write("# Vaultfire x402 Earnings Ledger\n")
        for record in entries:
            fp.write(json.dumps(dict(record), separators=(",", ":")))
            fp.write("\n")
    return destination


__all__ = [
    "load_dashboard_entries",
    "export_dashboard",
    "aggregate_totals",
    "count_total_events",
]
