"""
Reconcile tracked Kalshi signals in bet_history.json against actual fills
pulled from the Kalshi API. Shows which signals were skipped (manual-execution
gap) and any fills that don't tie back to a tracked signal.
"""
import json
import re
from datetime import datetime
from pathlib import Path

import kalshi_trader as kt

REPO = Path(r'E:\soccer\data')
HISTORY = REPO / "bet_history.json"
TEAM_MAP = REPO / "team_map.json"
LEAGUE_MAP = REPO / "league_map.json"

# ── Load all data ────────────────────────────────────────────────────────
history = json.loads(HISTORY.read_text())
team_map_raw = json.loads(TEAM_MAP.read_text())
league_map = json.loads(LEAGUE_MAP.read_text())

# Build name → kalshi abbr lookup (case-insensitive, includes aliases)
name_to_abbr = {}
for name, info in team_map_raw.items():
    if not isinstance(info, dict):
        continue
    abbr = info.get("kalshi")
    if not abbr:
        continue
    name_to_abbr[name.lower()] = abbr
    for alias in info.get("aliases", []):
        name_to_abbr[alias.lower()] = abbr

# div → kalshi series
div_to_series = {div: v.get("kalshi_series") for div, v in league_map.items()}

# ── Pull all fills via paginated API ─────────────────────────────────────
print("Fetching all Kalshi fills...")
all_fills = []
cursor = None
while True:
    params = {"limit": 200}
    if cursor:
        params["cursor"] = cursor
    resp = kt._get("/portfolio/fills", params=params)
    fills = resp.get("fills", [])
    if not fills:
        break
    all_fills.extend(fills)
    cursor = resp.get("cursor")
    if not cursor:
        break
print(f"  Got {len(all_fills)} fills")

# Keep only YES side opening fills (buys)
opening_fills = [f for f in all_fills if f.get("side") == "yes"]
print(f"  Opening fills (YES buys): {len(opening_fills)}")

# Parse ticker → (series, date, winner_abbr)
# Format: KXMLSGAME-26MAY02MIAORL-ORL  →  series=KXMLSGAME, date=2026-05-02, winner=ORL
TICKER_RE = re.compile(r"^(KX[A-Z0-9]+GAME)-(\d{2}[A-Z]{3}\d{2})[A-Z]+-([A-Z]+)$")
MONTHS = {"JAN":1,"FEB":2,"MAR":3,"APR":4,"MAY":5,"JUN":6,"JUL":7,"AUG":8,"SEP":9,"OCT":10,"NOV":11,"DEC":12}

def parse_ticker(t):
    m = TICKER_RE.match(t)
    if not m:
        return None
    series, dpart, winner = m.groups()
    yy, mon, dd = int(dpart[:2]), dpart[2:5], int(dpart[5:])
    d = datetime(2000 + yy, MONTHS[mon], dd).date()
    return series, d, winner

# Index fills by (series, date, winner_abbr) → list of fills
fill_index = {}
unparseable = []
for f in opening_fills:
    parsed = parse_ticker(f["ticker"])
    if parsed is None:
        unparseable.append(f["ticker"])
        continue
    fill_index.setdefault(parsed, []).append(f)

if unparseable:
    print(f"  WARNING: {len(unparseable)} tickers unparseable, e.g. {unparseable[:3]}")

# ── Walk tracked bets and match ──────────────────────────────────────────
earliest_fill_date = min(parse_ticker(f["ticker"])[1] for f in opening_fills if parse_ticker(f["ticker"]))
print(f"  Earliest fill date: {earliest_fill_date}")

tracked_kalshi = [
    b for b in history
    if b.get("kalshi_ask") and b.get("date")
    and datetime.strptime(b["date"][:10], "%Y-%m-%d").date() >= earliest_fill_date
]
print(f"\nTracked Kalshi bets from {earliest_fill_date}+: {len(tracked_kalshi)}")

matched = []
missed = []  # tracked but no matching fill
unmapped = []  # couldn't determine kalshi abbr for the team

for b in tracked_kalshi:
    side = b["bet"]
    is_home = "HOME" in side.upper()
    team_name = b["home"] if is_home else b["away"]
    abbr = name_to_abbr.get(team_name.lower())
    if not abbr:
        unmapped.append((b["date"], b["home"], b["away"], side, team_name))
        continue
    div = b["div"]
    series = div_to_series.get(div)
    if not series:
        unmapped.append((b["date"], b["home"], b["away"], side, f"no series for div={div}"))
        continue
    bet_date = datetime.strptime(b["date"][:10], "%Y-%m-%d").date()
    key = (series, bet_date, abbr)
    if key in fill_index:
        matched.append((b, fill_index[key]))
    else:
        missed.append((b, key))

# Find fills with NO matching tracked bet (placed manually off-pipeline)
tracked_keys = set()
for b, _ in matched:
    side = b["bet"]; is_home = "HOME" in side.upper()
    team_name = b["home"] if is_home else b["away"]
    abbr = name_to_abbr.get(team_name.lower())
    bet_date = datetime.strptime(b["date"][:10], "%Y-%m-%d").date()
    tracked_keys.add((div_to_series[b["div"]], bet_date, abbr))

orphan_fills = []
for key, fills_list in fill_index.items():
    if key not in tracked_keys:
        orphan_fills.append((key, fills_list))

# ── Report ───────────────────────────────────────────────────────────────
print("\n" + "=" * 70)
print("RECONCILIATION SUMMARY")
print("=" * 70)
print(f"Tracked Kalshi bets in window:    {len(tracked_kalshi)}")
print(f"  MATCHED to a real fill:         {len(matched)}")
print(f"  MISSED (signal, no fill):       {len(missed)}")
print(f"  UNMAPPED (no abbr lookup):      {len(unmapped)}")
print(f"  ORPHAN fills (no tracked bet):  {len(orphan_fills)}")

# Cash impact of missed bets
missed_total_pnl = 0; missed_wins = 0; missed_losses = 0
for b, _ in missed:
    if b.get("won") is True:
        missed_wins += 1
        missed_total_pnl += b.get("kalshi_pnl", 0)
    elif b.get("won") is False:
        missed_losses += 1
        missed_total_pnl += b.get("kalshi_pnl", 0)

print(f"\nIf you HAD placed all the missed signals (per tracked):")
print(f"  Hypothetical extra P&L: ${missed_total_pnl:+.2f}  ({missed_wins}W / {missed_losses}L)")

print("\n--- MISSED SIGNALS (last 30) ---")
print(f"{'DATE':<11} {'LEAGUE':<5} {'MATCH':<40} {'BET':<10} {'CLV':>5} {'STAKE':>7} {'RESULT':<6}")
for b, key in sorted(missed, key=lambda x: x[0]["date"])[-30:]:
    result = "WIN" if b.get("won") else "LOSS" if b.get("won") is False else "?"
    pnl = b.get("kalshi_pnl", 0)
    print(f"{b['date'][:10]:<11} {b['div']:<5} {(b['home']+' vs '+b['away'])[:38]:<40} {b['bet']:<10} {b.get('clv_kalshi',0):>5.1f} {b.get('kalshi_stake',0):>7.2f} {result:<6} ${pnl:+.2f}")

if unmapped:
    print(f"\n--- UNMAPPED ({len(unmapped)} tracked bets where I couldn't find kalshi abbr) ---")
    for d, h, a, s, reason in unmapped[:15]:
        print(f"  {d[:10]}  {h} vs {a}  ({s})  -> {reason}")

if orphan_fills:
    print(f"\n--- ORPHAN FILLS ({len(orphan_fills)} — placed but not in bet_history) ---")
    for key, fills_list in sorted(orphan_fills, key=lambda x: x[0][1])[:20]:
        series, d, abbr = key
        total_count = sum(float(f["count_fp"]) for f in fills_list)
        avg_px = sum(float(f["yes_price_dollars"]) * float(f["count_fp"]) for f in fills_list) / total_count
        print(f"  {d}  {series}  YES on {abbr}  qty={total_count:.1f} @ avg ${avg_px:.2f}")
