"""
Simulate halftime cash-out strategy.
If away team is losing at HT, cash out at a discount based on goal deficit.
Compare vs hold-to-fulltime baseline.
"""
import pandas as pd, numpy as np, glob, warnings, re
warnings.filterwarnings('ignore')

# ── Load HT scores ─────────────────────────────────────────────────────────────
print("Loading HT scores...")
dfs = []
ht_sources = list(glob.glob(r'C:\Users\lirse\OneDrive\Desktop\soccer\soccer\odds_data\*.csv'))
# Also load extra leagues (E1, E2, SP2, G1) if downloaded
extra = r'C:\Users\lirse\OneDrive\Desktop\soccer\soccer\odds_data\extra_leagues.csv'
if extra not in ht_sources:
    ht_sources.append(extra)
for f in ht_sources:
    try:
        df = pd.read_csv(f, low_memory=False)
        if 'HTHG' in df.columns and 'HTAG' in df.columns:
            needed = ['Date','HomeTeam','AwayTeam','HTHG','HTAG','FTHG','FTAG','FTR']
            dfs.append(df[[c for c in needed if c in df.columns]].copy())
    except: pass

ht_all = pd.concat(dfs, ignore_index=True)
ht_all['Date'] = pd.to_datetime(ht_all['Date'], dayfirst=False, errors='coerce')
ht_all = ht_all.dropna(subset=['Date','HTHG','HTAG']).copy()
ht_all['HTHG'] = pd.to_numeric(ht_all['HTHG'], errors='coerce')
ht_all['HTAG'] = pd.to_numeric(ht_all['HTAG'], errors='coerce')
ht_all = ht_all.dropna(subset=['HTHG','HTAG'])
ht_all = ht_all.drop_duplicates(subset=['Date','HomeTeam','AwayTeam'])
print(f"  {len(ht_all):,} matches with HT scores ({ht_all['Date'].min().date()} to {ht_all['Date'].max().date()})")

# ── Load our bets ──────────────────────────────────────────────────────────────
bets = pd.read_csv(r'C:\Users\lirse\OneDrive\Desktop\soccer\soccer\fbref\bets_export.csv')
bets['date'] = pd.to_datetime(bets['date'])
bets = bets[bets['league_code'] != 'T1'].sort_values('date').reset_index(drop=True)
print(f"  {len(bets):,} bets (excl. Turkey)")

# ── Name normalisation ─────────────────────────────────────────────────────────
def norm(s):
    s = re.sub(r'[^a-z0-9 ]', '', str(s).lower().strip())
    for old, new in [
        ('manchester united','man utd'),('manchester city','man city'),
        ('paris saint germain','psg'),('tottenham hotspur','tottenham'),
        ('atletico madrid','atl madrid'),('inter milan','inter'),
        ('ac milan','milan'),('newcastle united','newcastle'),
        ('wolverhampton wanderers','wolves'),('west bromwich albion','west brom'),
        ('queens park rangers','qpr'),('sheffield wednesday','sheff wed'),
        ('sheffield united','sheff utd'),('nottingham forest','nottm forest'),
        ('brighton & hove albion','brighton'),('brighton hove albion','brighton'),
        ('afc bournemouth','bournemouth'),('west ham united','west ham'),
        ('leicester city','leicester'),('leeds united','leeds'),
    ]:
        s = s.replace(old, new)
    return s.strip()

ht_all['ht_n'] = ht_all['HomeTeam'].apply(norm)
ht_all['at_n'] = ht_all['AwayTeam'].apply(norm)
bets['ht_n']   = bets['home_team'].apply(norm)
bets['at_n']   = bets['away_team'].apply(norm)

# Build lookup
ht_lookup = {}
for _, row in ht_all.iterrows():
    for delta in [0, 1, -1]:
        key = (row['Date'] + pd.Timedelta(days=delta), row['ht_n'], row['at_n'])
        ht_lookup[key] = (int(row['HTHG']), int(row['HTAG']))

bets['ht_home'] = np.nan; bets['ht_away'] = np.nan
matched = 0
for idx, row in bets.iterrows():
    for delta in [0, 1, -1]:
        key = (row['date'] + pd.Timedelta(days=delta), row['ht_n'], row['at_n'])
        if key in ht_lookup:
            bets.at[idx, 'ht_home'], bets.at[idx, 'ht_away'] = ht_lookup[key]
            matched += 1; break

bets_ht = bets[bets['ht_home'].notna()].copy()
bets_ht['ht_deficit'] = bets_ht['ht_away'] - bets_ht['ht_home']
print(f"\nMatched HT scores: {matched:,} / {len(bets):,} ({matched/len(bets)*100:.1f}%)")

# ── HT situation breakdown ─────────────────────────────────────────────────────
print("\nHT situation breakdown (comeback rates):")
print(f"  {'Situation':<12} {'n':>5}  {'FT_win%':>8}  {'Flat_ROI':>9}  {'avg_odds':>9}")
for deficit, label in [(-3,'down 3+'),(-2,'down 2'),(-1,'down 1'),(0,'level'),(1,'winning')]:
    if deficit <= -3:
        sub = bets_ht[bets_ht['ht_deficit'] <= -3]
    elif deficit >= 1:
        sub = bets_ht[bets_ht['ht_deficit'] >= 1]
    else:
        sub = bets_ht[bets_ht['ht_deficit'] == deficit]
    if len(sub) == 0: continue
    wr  = sub['won'].mean() * 100
    roi = ((sub['won'] * (sub['b365_A'] - 1)) - (1 - sub['won'])).mean() * 100
    avg = sub['b365_A'].mean()
    print(f"  {label:<12} {len(sub):5d}  {wr:7.1f}%  {roi:+8.1f}%  {avg:9.2f}")

# ── Strategy runner ────────────────────────────────────────────────────────────
KELLY_FRAC = 0.50; MAX_STAKE = 0.20

def kelly_stake(p, odds, frac=0.5, mx=0.20):
    b = odds - 1; k = (p * b - (1 - p)) / b
    return min(max(k, 0) * frac, mx)

def run_strategy(bets_df, rules):
    bk = 1000.0; peak = 1000.0; max_dd = 0.0
    n_co = saved = missed = 0
    for _, row in bets_df.iterrows():
        s = bk * kelly_stake(row['model_pA'], row['b365_A'], KELLY_FRAC, MAX_STAKE)
        d = int(row['ht_deficit'])
        co_key = max(d, -3)  # group -3 and worse together
        if rules and d < 0 and co_key in rules:
            pl = -s * (1 - rules[co_key])
            n_co += 1
            if row['won']: missed += 1
            else: saved += 1
        else:
            pl = s * (row['b365_A'] - 1) if row['won'] else -s
        bk += pl; peak = max(peak, bk)
        max_dd = min(max_dd, (bk - peak) / peak)
    return {'final': bk, 'dd': max_dd * 100, 'n_co': n_co, 'saved': saved, 'missed': missed}

# ── Strategy sweep ─────────────────────────────────────────────────────────────
print("\n" + "="*90)
print("STRATEGY SWEEP  (actual backtest, excl. Turkey, Half Kelly)")
base = run_strategy(bets_ht, None)
print(f"\n  {'Strategy':<40} {'Final$':>8}  {'vs Base':>8}  {'MaxDD':>7}  {'COd':>5}  {'Saved':>6}  {'Missed':>7}")
print("  " + "-"*88)
print(f"  {'Baseline (hold all)':<40} ${base['final']:>7,.0f}  {'---':>8}  {base['dd']:>6.1f}%")

scenarios = [
    ("Scaled: -1=40% -2=20% -3=5%",  {-1:0.40,-2:0.20,-3:0.05}),
    ("Scaled: -1=50% -2=25% -3=5%",  {-1:0.50,-2:0.25,-3:0.05}),
    ("Scaled: -1=35% -2=15% -3=0%",  {-1:0.35,-2:0.15,-3:0.00}),
    ("Scaled: -1=30% -2=10% -3=0%",  {-1:0.30,-2:0.10,-3:0.00}),
    ("Flat 40% all deficits",         {-1:0.40,-2:0.40,-3:0.40}),
    ("Flat 30% all deficits",         {-1:0.30,-2:0.30,-3:0.30}),
    ("Flat 20% all deficits",         {-1:0.20,-2:0.20,-3:0.20}),
    ("Only cash -2+ (40/20/5%)",      {-2:0.40,-3:0.05}),
    ("Only cash -2+ (20/5%)",         {-2:0.20,-3:0.05}),
    ("Only cash -3 (0%)",             {-3:0.00}),
]

best_final = base['final']
for label, rules in scenarios:
    r = run_strategy(bets_ht, rules)
    d = r['final'] - base['final']
    mk = " ◄ BEST" if r['final'] > best_final else ""
    if r['final'] > best_final: best_final = r['final']; best_label = label; best_rules = rules
    print(f"  {label:<40} ${r['final']:>7,.0f}  {d:>+8,.0f}  {r['dd']:>6.1f}%  "
          f"{r['n_co']:>5}  {r['saved']:>6}  {r['missed']:>7}{mk}")

# ── Monte Carlo ────────────────────────────────────────────────────────────────
print("\n" + "="*90)
print("MONTE CARLO (10,000 sims)")

def mc_run(bets_df, rules, n_sims=10_000):
    N = len(bets_df)
    odds     = bets_df['b365_A'].values
    pA       = bets_df['model_pA'].values
    deficits = bets_df['ht_deficit'].fillna(0).apply(lambda x: max(int(x), -3)).values
    won      = bets_df['won'].values
    stakes   = np.array([kelly_stake(p, o, KELLY_FRAC, MAX_STAKE) for p, o in zip(pA, odds)])
    # Per-deficit win rates (use historical rates so MC reflects real comeback frequency)
    wr_by_deficit = {}
    for d in [-3, -2, -1, 0, 1, 2, 3]:
        mask = deficits == d
        wr_by_deficit[d] = won[mask].mean() if mask.sum() > 0 else bets_df['won'].mean()
    wr_arr = np.array([wr_by_deficit.get(d, bets_df['won'].mean()) for d in deficits])

    terminal = np.zeros(n_sims); max_dds = np.zeros(n_sims)
    for sim in range(n_sims):
        outcomes = np.random.random(N) < wr_arr  # per-bet win probability
        bk = 1000.0; peak = 1000.0; max_dd = 0.0
        for i in range(N):
            s = bk * stakes[i]
            if rules and deficits[i] < 0 and deficits[i] in rules:
                pl = -s * (1 - rules[deficits[i]])
            else:
                pl = s * (odds[i] - 1) if outcomes[i] else -s
            bk += pl; peak = max(peak, bk)
            max_dd = min(max_dd, (bk - peak) / peak)
        terminal[sim] = bk; max_dds[sim] = max_dd
    return terminal, max_dds

mc_configs = [
    ("Baseline",                          None),
    ("Scaled: -1=40% -2=20% -3=5%",      {-1:0.40,-2:0.20,-3:0.05}),
    ("Scaled: -1=50% -2=25% -3=5%",      {-1:0.50,-2:0.25,-3:0.05}),
    ("Flat 40% all",                      {-1:0.40,-2:0.40,-3:0.40}),
    ("Only cash -2+ (40/5%)",            {-2:0.40,-3:0.05}),
]

print(f"\n  {'Strategy':<40} {'Median':>9} {'P(prof)':>9} {'P(>5k)':>8} {'MedDD':>8} {'Ruin':>6}")
print("  " + "-"*82)
for label, rules in mc_configs:
    print(f"  Running: {label}...")
    t, d = mc_run(bets_ht, rules)
    print(f"  {label:<40} ${np.median(t):>7,.0f}  "
          f"{(t>1000).mean()*100:>8.1f}%  "
          f"{(t>5000).mean()*100:>7.1f}%  "
          f"{np.median(d)*100:>7.1f}%  "
          f"{(t<500).mean()*100:>5.1f}%")

print("\nDone.")
