"""
extract_live_trades.py — pull live closed deals from MT5 broker history

Fetches all closed EURUSD and USDJPY deals from the MT5 terminal since the
LIVE_CUTOFF date and writes them to:
    data/live/trades_live_eurusd.csv
    data/live/trades_live_usdjpy.csv

The build_equity_curve.py script reads these and merges them with the
historical backtest CSVs to feed the dashboard's monthly drilldown.

Why MT5 deal history (not the live monitor's trade_history JSON)?
The broker-recorded deal history includes swap fees and commission, which
the live monitor's net_pnl field doesn't fully capture. We want the dashboard
to match the broker's account statement exactly.

Run from VPS where MT5 is installed and connected:
    cd C:\\Users\\Administrator\\OneDrive\\fx_macro_intraday
    python src\\research\\extract_live_trades.py

Output is OneDrive-synced so home machine + dashboard pick it up automatically.
"""
from __future__ import annotations

import sys
from datetime import datetime
from pathlib import Path

import pandas as pd

try:
    import MetaTrader5 as mt5
except ImportError:
    print("ERROR: MetaTrader5 library not installed. Run: pip install MetaTrader5")
    sys.exit(1)

# ─────────────────────────────────────────────────────────────────────────
# Config
# ─────────────────────────────────────────────────────────────────────────
PROJECT_ROOT = Path(__file__).resolve().parents[2]
LIVE_DIR     = PROJECT_ROOT / "data" / "live"
LIVE_DIR.mkdir(parents=True, exist_ok=True)

EU_OUT = LIVE_DIR / "trades_live_eurusd.csv"
UJ_OUT = LIVE_DIR / "trades_live_usdjpy.csv"

# Live deployment cutoff — anything before this is treated as backtest/testing
# noise and excluded from the live data. Inclusive: trades on/after this date count.
LIVE_CUTOFF = datetime(2026, 4, 11)

# Lot-size → tier multiplier mapping. Live monitor scales lots by tier:
#   1.0× → ~3 lots, 1.5× → ~4.5 lots, 2.0× → ~6 lots
# We infer tier from rounded lot size (more reliable than recomputing z-score).
def lot_to_tier(lot_size: float) -> float:
    if lot_size >= 5.5:
        return 2.0
    if lot_size >= 4.0:
        return 1.5
    return 1.0

# Symbols to extract — broker may use suffixes like "EURUSD.r" so we accept any
# variant whose name starts with the base symbol.
SYMBOLS = {
    "EURUSD": EU_OUT,
    "USDJPY": UJ_OUT,
}

# ─────────────────────────────────────────────────────────────────────────
# Main
# ─────────────────────────────────────────────────────────────────────────
def main():
    print("=" * 64)
    print("  Extract live trades from MT5 broker history")
    print(f"  Live cutoff: {LIVE_CUTOFF.date()} (trades before this excluded)")
    print("=" * 64)

    if not mt5.initialize():
        err = mt5.last_error()
        print(f"ERROR: mt5.initialize() failed: {err}")
        print("  Is MetaTrader 5 running? Check the terminal is open and logged in.")
        sys.exit(2)

    try:
        acc = mt5.account_info()
        if acc is not None:
            print(f"  Connected: account {acc.login} | broker {acc.server} | balance ${acc.balance:,.2f}")
        else:
            print("  WARNING: account_info() returned None — proceeding anyway")

        # Fetch all closed deals since cutoff
        date_to = datetime.now()
        deals = mt5.history_deals_get(LIVE_CUTOFF, date_to)
        if deals is None:
            print(f"  history_deals_get returned None: {mt5.last_error()}")
            deals = []
        print(f"  Total deals from MT5: {len(deals)}")

        # Group by ticket: each round-trip trade has TWO deals (entry + exit).
        # We sum profit + swap + commission across both legs to get net trade P&L.
        # The exit deal is the one with entry == DEAL_ENTRY_OUT.
        by_position: dict[int, list] = {}
        for d in deals:
            by_position.setdefault(d.position_id, []).append(d)

        eu_rows, uj_rows = [], []

        for pos_id, legs in by_position.items():
            if len(legs) < 2:
                continue  # still-open or one-sided — skip

            # Identify entry leg + exit leg
            entry_leg = next((l for l in legs if l.entry == mt5.DEAL_ENTRY_IN), None)
            exit_leg  = next((l for l in legs if l.entry == mt5.DEAL_ENTRY_OUT), None)
            if entry_leg is None or exit_leg is None:
                continue

            # Symbol filter — accept broker suffix variants (EURUSD.r, EURUSD-x etc.)
            symbol = entry_leg.symbol
            base_symbol = None
            for known in SYMBOLS:
                if symbol.upper().startswith(known):
                    base_symbol = known
                    break
            if base_symbol is None:
                continue

            # Net P&L = sum of profit + swap + commission across all legs for this position
            net_pnl = sum((l.profit or 0) + (l.swap or 0) + (l.commission or 0) for l in legs)

            # Direction: BUY (DEAL_TYPE_BUY=0) on entry → +1, SELL (DEAL_TYPE_SELL=1) → -1
            direction = 1 if entry_leg.type == mt5.DEAL_TYPE_BUY else -1

            entry_time = datetime.fromtimestamp(entry_leg.time)
            exit_time  = datetime.fromtimestamp(exit_leg.time)

            # Cutoff filter — use exit time (when P&L was realized)
            if exit_time < LIVE_CUTOFF:
                continue

            # Tier inferred from lot size
            lot_size = float(entry_leg.volume)
            tier = lot_to_tier(lot_size)

            row = {
                "pair":       base_symbol,
                "ticket":     pos_id,
                "entry_time": entry_time.strftime("%Y-%m-%d %H:%M:%S"),
                "exit_time":  exit_time.strftime("%Y-%m-%d %H:%M:%S"),
                "direction":  direction,
                "lot_size":   lot_size,
                "tier":       tier,
                "net_pnl":    round(net_pnl, 2),
                "swap":       round(sum(l.swap or 0 for l in legs), 2),
                "commission": round(sum(l.commission or 0 for l in legs), 2),
            }

            if base_symbol == "EURUSD":
                eu_rows.append(row)
            elif base_symbol == "USDJPY":
                uj_rows.append(row)

        # Sort chronologically by exit time
        eu_rows.sort(key=lambda r: r["exit_time"])
        uj_rows.sort(key=lambda r: r["exit_time"])

        # Write CSVs (always write — empty file is valid signal of "no live trades yet")
        eu_df = pd.DataFrame(eu_rows)
        uj_df = pd.DataFrame(uj_rows)

        # Standard schema columns even if empty, so build script can rely on it
        empty_schema = ["pair","ticket","entry_time","exit_time","direction",
                        "lot_size","tier","net_pnl","swap","commission"]
        if eu_df.empty:
            eu_df = pd.DataFrame(columns=empty_schema)
        if uj_df.empty:
            uj_df = pd.DataFrame(columns=empty_schema)

        eu_df.to_csv(EU_OUT, index=False)
        uj_df.to_csv(UJ_OUT, index=False)

        # Summary
        print()
        print(f"  EURUSD live trades: {len(eu_rows):>3}", end="")
        if eu_rows:
            total = sum(r["net_pnl"] for r in eu_rows)
            print(f"  sum=${total:>9,.2f}  ({eu_rows[0]['exit_time'][:10]} → {eu_rows[-1]['exit_time'][:10]})")
        else:
            print("  (none)")

        print(f"  USDJPY live trades: {len(uj_rows):>3}", end="")
        if uj_rows:
            total = sum(r["net_pnl"] for r in uj_rows)
            print(f"  sum=${total:>9,.2f}  ({uj_rows[0]['exit_time'][:10]} → {uj_rows[-1]['exit_time'][:10]})")
        else:
            print("  (none)")

        print()
        print(f"  Wrote: {EU_OUT.relative_to(PROJECT_ROOT)}")
        print(f"  Wrote: {UJ_OUT.relative_to(PROJECT_ROOT)}")
        print("=" * 64)

    finally:
        mt5.shutdown()


if __name__ == "__main__":
    main()
