from __future__ import annotations

import argparse
import json
from collections import Counter
from pathlib import Path
from typing import Any


def _family(reason: str) -> str:
    if reason.startswith("confluence_"):
        return "confluence"
    if reason.startswith("tv_"):
        return "tradingview"
    if reason.startswith(("coingecko_", "crowding_", "source_stale", "external_")):
        return "external_freshness"
    if reason.startswith(("weak_liquidity", "spread_", "impact_", "cost_model_")):
        return "execution_quality"
    return "core_signal"


def build_signal_funnel(runtime_dir: str | Path, *, strategy_version: str) -> dict[str, Any]:
    runtime = Path(runtime_dir)
    path = runtime / "signal_journal.jsonl"
    raw_rows = 0
    invalid_rows = 0
    latest_by_window: dict[tuple[str, str], dict[str, Any]] = {}
    if path.exists():
        for line in path.read_text(encoding="utf-8", errors="replace").splitlines():
            if not line.strip():
                continue
            try:
                row = json.loads(line)
            except json.JSONDecodeError:
                invalid_rows += 1
                continue
            if not isinstance(row, dict) or row.get("strategy_version") != strategy_version:
                continue
            raw_rows += 1
            window = str(row.get("data_window_id") or ((row.get("features") or {}).get("data_window_id")) or "")
            coin = str(row.get("coin") or "").upper()
            if not coin or not window:
                invalid_rows += 1
                continue
            latest_by_window[(coin, window)] = row

    family_windows: Counter[str] = Counter()
    blocker_counts: Counter[str] = Counter()
    would_pass_without_confluence = 0
    would_pass_without_external = 0
    core_blocked = 0
    actionable = 0
    for row in latest_by_window.values():
        reasons = list(dict.fromkeys(str(reason) for reason in (row.get("block_reason") or []) if reason))
        families = {_family(reason) for reason in reasons}
        for family in families:
            family_windows[family] += 1
        blocker_counts.update(reasons)
        if not reasons:
            actionable += 1
        if reasons and families <= {"confluence"}:
            would_pass_without_confluence += 1
        if reasons and families <= {"external_freshness"}:
            would_pass_without_external += 1
        if "core_signal" in families:
            core_blocked += 1

    total = len(latest_by_window)
    return {
        "schema_version": "v77_signal_funnel.v1",
        "strategy_id": runtime.name,
        "strategy_version": strategy_version,
        "raw_rows": raw_rows,
        "unique_coin_windows": total,
        "invalid_rows": invalid_rows,
        "actionable_without_runtime_state_blockers": actionable,
        "would_pass_without_confluence": would_pass_without_confluence,
        "would_pass_without_external_freshness": would_pass_without_external,
        "core_signal_blocked": core_blocked,
        "gate_families": dict(sorted(family_windows.items())),
        "top_blockers": blocker_counts.most_common(20),
        "paper_only": True,
        "execution_allowed": False,
        "live_order_allowed": False,
        "mainnet_signed_action": False,
    }


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(description="Deduplicated v77 paper signal funnel; never sends orders.")
    parser.add_argument("--runtime-dir", required=True)
    parser.add_argument("--strategy-version", required=True)
    parser.add_argument("--output")
    parser.add_argument("--json", action="store_true")
    args = parser.parse_args(argv)
    report = build_signal_funnel(args.runtime_dir, strategy_version=args.strategy_version)
    output = Path(args.output) if args.output else Path(args.runtime_dir) / "signal_funnel_latest.json"
    output.parent.mkdir(parents=True, exist_ok=True)
    output.write_text(json.dumps(report, indent=2, sort_keys=True), encoding="utf-8")
    print(json.dumps(report, indent=2, sort_keys=True) if args.json else f"unique_windows={report['unique_coin_windows']} actionable={report['actionable_without_runtime_state_blockers']}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
