"""
monte_carlo_chart_v1.py
========================
Generates a Monte Carlo simulation chart showing:
  - 2,000 simulated equity paths (shuffled trade order)
  - 5th-95th percentile band (outer light blue)
  - 25th-75th percentile band (inner dark blue)
  - Median simulation path (dashed)
  - Actual historical equity curve (solid dark)

Run from project root on home machine:
  python src/research/monte_carlo_chart_v1.py
"""

import pandas as pd
import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from pathlib import Path
import sys

BASE_PATH  = Path(__file__).resolve().parents[2]
SRC_PATH   = BASE_PATH / "src"
TRADES_DIR = BASE_PATH / "data" / "processed" / "trades"
CHARTS_DIR = BASE_PATH / "data" / "processed"

if str(SRC_PATH) not in sys.path:
    sys.path.append(str(SRC_PATH))

# ── Config ────────────────────────────────────────────────────────────────────
N_SIMS        = 2000
START_BALANCE = 100_000
NOTIONAL      = 300_000
ZSCORE_BANDS  = [(2.75, 3.50, 1.0), (3.50, 4.50, 1.5), (4.50, 99.0, 2.0)]
SEED          = 42

def get_multiplier(z):
    for lo, hi, m in ZSCORE_BANDS:
        if lo <= z < hi:
            return m
    return 2.0

def main():
    print("=" * 60)
    print("MONTE CARLO SIMULATION CHART")
    print("=" * 60)

    # ── Load trade log ────────────────────────────────────────────────────────
    trade_log = None
    for fname in ["trades_real_costs.csv", "trades_eurusd_final.csv"]:
        p = TRADES_DIR / fname
        if p.exists():
            trade_log = pd.read_csv(p)
            print(f"\n  Loaded: {fname}  ({len(trade_log):,} trades)")
            break

    if trade_log is None:
        print("  ERROR: No trade log found in data/processed/trades/")
        return

    # Identify return column
    ret_col = None
    for c in ["return_real", "return", "ret"]:
        if c in trade_log.columns:
            ret_col = c
            break
    if ret_col is None:
        print(f"  ERROR: Could not find return column. Columns: {trade_log.columns.tolist()}")
        return

    # Identify z-score column
    z_col = None
    for c in ["zscore_abs", "lag_zscore_24h_v3", "z_score"]:
        if c in trade_log.columns:
            z_col = c
            break

    # Build returns array with position sizing
    returns = trade_log[ret_col].values.copy()
    if z_col:
        multipliers = trade_log[z_col].abs().apply(get_multiplier).values
        notionals   = np.minimum(NOTIONAL * multipliers, NOTIONAL * 3)
    else:
        notionals = np.full(len(returns), NOTIONAL)

    dollar_pnl = returns * notionals
    n_trades   = len(dollar_pnl)

    print(f"  Trades         : {n_trades:,}")
    print(f"  Win rate       : {(returns > 0).mean()*100:.1f}%")
    print(f"  Avg return     : {returns.mean()*100:.4f}%")
    print(f"  Running {N_SIMS:,} simulations...")

    # ── Actual equity curve ───────────────────────────────────────────────────
    actual_eq = np.empty(n_trades + 1)
    actual_eq[0] = START_BALANCE
    actual_eq[1:] = START_BALANCE + np.cumsum(dollar_pnl)

    # ── Monte Carlo ───────────────────────────────────────────────────────────
    rng  = np.random.default_rng(SEED)
    sims = np.empty((N_SIMS, n_trades + 1))
    sims[:, 0] = START_BALANCE

    for i in range(N_SIMS):
        idx = rng.permutation(n_trades)
        sims[i, 1:] = START_BALANCE + np.cumsum(dollar_pnl[idx])

    # Percentile bands
    p5  = np.percentile(sims, 5,  axis=0)
    p25 = np.percentile(sims, 25, axis=0)
    p50 = np.percentile(sims, 50, axis=0)
    p75 = np.percentile(sims, 75, axis=0)
    p95 = np.percentile(sims, 95, axis=0)

    x = np.arange(n_trades + 1)

    print(f"  Final balance range:")
    print(f"    5th  pct : ${p5[-1]:,.0f}")
    print(f"    25th pct : ${p25[-1]:,.0f}")
    print(f"    Median   : ${p50[-1]:,.0f}")
    print(f"    75th pct : ${p75[-1]:,.0f}")
    print(f"    95th pct : ${p95[-1]:,.0f}")
    print(f"    Actual   : ${actual_eq[-1]:,.0f}")

    # ── Plot ──────────────────────────────────────────────────────────────────
    fig, ax = plt.subplots(figsize=(16, 8))
    fig.patch.set_facecolor("white")
    ax.set_facecolor("white")

    # Draw a sample of individual sim paths (very light)
    sample_idx = rng.choice(N_SIMS, size=300, replace=False)
    for i in sample_idx:
        ax.plot(x, sims[i], color="#a8c4e0", alpha=0.04, linewidth=0.4)

    # 5th-95th band
    ax.fill_between(x, p5, p95, color="#c5d9ee", alpha=0.5,
                    label="5th–95th pct")

    # 25th-75th band
    ax.fill_between(x, p25, p75, color="#89afd4", alpha=0.55,
                    label="25th–75th pct")

    # Median
    ax.plot(x, p50, color="#6b9cc9", linewidth=1.8,
            linestyle="--", alpha=0.9, label="Median sim")

    # Actual
    ax.plot(x, actual_eq, color="#0f1f3d", linewidth=2.2,
            label="Actual", zorder=10)

    # ── Formatting ────────────────────────────────────────────────────────────
    ax.set_title("Monte Carlo Simulation", fontsize=16,
                 fontweight="normal", pad=16, color="#1a1a2e")

    ax.set_xlabel("Trade number", fontsize=12, color="#4a4a6a", labelpad=10)
    ax.set_ylabel("Account balance", fontsize=12, color="#4a4a6a", labelpad=10)

    # Y-axis formatter
    def yfmt(v, _):
        if v >= 1_000_000: return f"${v/1e6:.1f}M"
        if v >= 1_000:     return f"${v/1000:.0f}k"
        return f"${v:.0f}"

    from matplotlib.ticker import FuncFormatter
    ax.yaxis.set_major_formatter(FuncFormatter(yfmt))

    ax.tick_params(colors="#6a6a8a", labelsize=10)
    ax.spines["top"].set_visible(False)
    ax.spines["right"].set_visible(False)
    ax.spines["left"].set_color("#dde3ee")
    ax.spines["bottom"].set_color("#dde3ee")
    ax.grid(axis="y", color="#eaecf5", linewidth=0.8, linestyle="-")
    ax.grid(axis="x", color="#eaecf5", linewidth=0.5, linestyle="-")
    ax.set_axisbelow(True)

    # Legend
    handles = [
        mpatches.Patch(color="#c5d9ee", alpha=0.8, label="5th–95th pct"),
        mpatches.Patch(color="#89afd4", alpha=0.9, label="25th–75th pct"),
        plt.Line2D([0],[0], color="#6b9cc9", linewidth=1.8,
                   linestyle="--", label="Median sim"),
        plt.Line2D([0],[0], color="#0f1f3d", linewidth=2.2, label="Actual"),
    ]
    ax.legend(handles=handles, loc="upper left", frameon=True,
              framealpha=0.9, edgecolor="#dde3ee", fontsize=10)

    # Annotate final actual balance
    ax.annotate(f"${actual_eq[-1]/1e6:.2f}M",
                xy=(n_trades, actual_eq[-1]),
                xytext=(n_trades - n_trades*0.06, actual_eq[-1] * 1.04),
                fontsize=10, color="#0f1f3d", fontweight="bold",
                arrowprops=dict(arrowstyle="->", color="#0f1f3d",
                                lw=1.2, connectionstyle="arc3,rad=-0.2"))

    plt.tight_layout()

    out = CHARTS_DIR / "monte_carlo_chart.png"
    plt.savefig(out, dpi=180, bbox_inches="tight", facecolor="white")
    plt.close()
    print(f"\n  Saved: {out}")
    print(f"\n  Open: {out}")


if __name__ == "__main__":
    main()
