"""Generator pentru data/backtest.xlsx.

5 strategii de management comparate side-by-side pe semnale blackbox:
  - TP0 only       : 100% close la TP0
  - TP1 only       : 100% OCO la SL/TP1
  - TP2 only       : 100% OCO la SL/TP2
  - Hybrid + BE    : 50% TP0 + mut SL la BE + 50% TP1 (recomandat de trader)
  - Hybrid no BE   : 50% TP0 + 50% TP1, fără BE (control pentru a izola valoarea BE-ului)

Rulare:
    pip install openpyxl
    python scripts/generate_template.py
"""

from __future__ import annotations

import shutil
from datetime import date, datetime, time, timedelta
from pathlib import Path

from openpyxl import Workbook
from openpyxl.chart import LineChart, Reference
from openpyxl.formatting.rule import CellIsRule
from openpyxl.styles import Alignment, Border, Font, PatternFill, Side
from openpyxl.utils import get_column_letter
from openpyxl.worksheet.datavalidation import DataValidation


OUTPUT = Path(__file__).resolve().parent.parent / "data" / "backtest.xlsx"
MAX_ROWS = 500  # rânduri pre-completate cu formule în sheet-ul Trades

# ---------------------------------------------------------------------------
# Styles
# ---------------------------------------------------------------------------

HEADER_FILL = PatternFill("solid", fgColor="1F3864")
HEADER_FONT = Font(name="Calibri", size=11, bold=True, color="FFFFFF")
INPUT_FILL = PatternFill("solid", fgColor="FFF8E1")
DERIVED_FILL = PatternFill("solid", fgColor="E8F1FA")
HIDDEN_FILL = PatternFill("solid", fgColor="F0F0F0")
TITLE_FONT = Font(name="Calibri", size=16, bold=True, color="1F3864")
SUBTITLE_FONT = Font(name="Calibri", size=12, bold=True, color="1F3864")
THIN = Side(border_style="thin", color="BFBFBF")
BORDER = Border(left=THIN, right=THIN, top=THIN, bottom=THIN)
CENTER = Alignment(horizontal="center", vertical="center")
LEFT = Alignment(horizontal="left", vertical="center")
RIGHT = Alignment(horizontal="right", vertical="center")


# ---------------------------------------------------------------------------
# Lists
# ---------------------------------------------------------------------------

STRATEGIES = ["M2D", "EMA cross", "Order block", "Liquidity sweep", "Custom"]
SESSIONS = ["A1", "A2", "A3", "B", "C", "D", "Other"]
INDICATORS = ["DIA", "US30", "SPY", "QQQ", "ES", "NQ"]
TIMEFRAMES = ["1min", "3min", "15min"]
DIRECTIONS = ["Buy", "Sell"]
OUTCOMES = ["SL", "TP0", "TP1", "TP2"]

# Cele 5 strategii de management (sufix folosit în numele coloanelor) + label friendly
STRAT_KEYS = ["tp0only", "tp1only", "tp2only", "hybrid_be", "hybrid_nobe"]
STRAT_LABELS = {
    "tp0only": "TP0 only",
    "tp1only": "TP1 only",
    "tp2only": "TP2 only",
    "hybrid_be": "Hybrid + BE",
    "hybrid_nobe": "Hybrid no BE",
}

# ---------------------------------------------------------------------------
# Trades sheet — schema
# ---------------------------------------------------------------------------

def _candidate_windows() -> list[tuple[str, time, time]]:
    """Ferestre suprapuse intre 16:30 si 23:00, evaluate pe ora Romaniei."""
    base = datetime(2000, 1, 1, 16, 30)
    last_start = datetime(2000, 1, 1, 22, 0)
    hard_ends = [
        datetime(2000, 1, 1, 22, 45),
        datetime(2000, 1, 1, 23, 0),
    ]
    durations = [timedelta(minutes=m) for m in (60, 90, 120, 180)]
    seen: set[tuple[time, time]] = set()
    windows: list[tuple[str, time, time]] = []

    start = base
    while start <= last_start:
        ends = [start + d for d in durations]
        ends += [end for end in hard_ends if end - start >= timedelta(minutes=60)]
        for end in ends:
            if end > hard_ends[-1]:
                continue
            key = (start.time(), end.time())
            if key in seen:
                continue
            seen.add(key)
            windows.append((f"{start:%H:%M}-{end:%H:%M}", start.time(), end.time()))
        start += timedelta(minutes=30)
    return windows


TRADABLE_WINDOWS = _candidate_windows()


INPUT_HEADERS = [
    "#", "Data", "Ora RO", "Zi", "Sesiune",
    "Strategie", "Indicator", "TF",
    "Direcție", "SL %", "TP0 %", "TP1 %", "TP2 %",
    "Outcome", "Notes",
]
DERIVED_HEADERS = (
    ["SL $", "SL $ Prop"]
    +
    [f"R_{s}" for s in STRAT_KEYS]
    + [f"$_{s}" for s in STRAT_KEYS]
    + [f"Bal_{s}" for s in STRAT_KEYS]
    + [f"$Prop_{s}" for s in STRAT_KEYS]
    + [f"BalProp_{s}" for s in STRAT_KEYS]
)
HELPER_HEADERS = (
    [f"Win_{s}" for s in STRAT_KEYS]
    + [f"Peak_{s}" for s in STRAT_KEYS]
    + [f"DD_{s}" for s in STRAT_KEYS]
    + [f"DailyPL_{s}" for s in STRAT_KEYS]
    + [f"PeakProp_{s}" for s in STRAT_KEYS]
    + [f"DDProp_{s}" for s in STRAT_KEYS]
)
TRADES_HEADERS = INPUT_HEADERS + DERIVED_HEADERS + HELPER_HEADERS

# Mapă nume → literă coloană Excel
COL = {name: get_column_letter(i + 1) for i, name in enumerate(TRADES_HEADERS)}


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


def _col_to_int(letter: str) -> int:
    n = 0
    for ch in letter:
        n = n * 26 + (ord(ch) - ord("A") + 1)
    return n


# ---------------------------------------------------------------------------
# Config sheet
# ---------------------------------------------------------------------------


def build_config(wb: Workbook) -> None:
    ws = wb.create_sheet("Config", 0)
    ws.sheet_view.showGridLines = False

    ws["A1"] = "Config — editează doar celulele galbene"
    ws["A1"].font = TITLE_FONT
    ws.merge_cells("A1:C1")

    ws["A3"] = "Setting"
    ws["B3"] = "Value"
    ws["C3"] = "Note"
    for c in ("A3", "B3", "C3"):
        ws[c].font = HEADER_FONT
        ws[c].fill = HEADER_FILL
        ws[c].alignment = CENTER

    ws["A4"] = "Account Size Start ($)"
    ws["B4"] = 10000
    ws["C4"] = "Balanța inițială pentru calcule $ și HWM (model abstract)"

    ws["A5"] = "Risk reper (%)"
    ws["B5"] = 1.0
    ws["C5"] = "Reper opțional; $_* se calculează din SL% × Account Size Start"

    ws["A6"] = "Risk reper ($)"
    ws["B6"] = "=B4*B5/100"
    ws["C6"] = "Auto — informativ; nu este folosit în formulele $_*"

    for r in (4, 5):
        ws.cell(row=r, column=2).fill = INPUT_FILL
        ws.cell(row=r, column=2).border = BORDER
    ws["B6"].fill = DERIVED_FILL
    ws["B6"].border = BORDER
    ws["B4"].number_format = "$#,##0"
    ws["B5"].number_format = '0.0"%"'
    ws["B6"].number_format = "$#,##0.00"

    # ---- Bloc Cont Prop Firm (separat de modelul abstract de mai sus) ----
    ws["A8"] = "Cont Prop Firm"
    ws["A8"].font = SUBTITLE_FONT
    ws.merge_cells("A8:C8")

    ws["A9"] = "Account Prop Start ($)"
    ws["B9"] = 50000
    ws["C9"] = "Balanța contului de prop"

    ws["A10"] = "Contracte per trade"
    ws["B10"] = 1
    ws["C10"] = "Număr de contracte tranzacționate per semnal (TradeLocker)"

    ws["A11"] = "$ per 1% per contract"
    ws["B11"] = 10000
    ws["C11"] = "Pe DIA: 0.10% = $1000 ⇒ 1% = $10,000 (1 contract notional ≈ $1M)"

    ws["A12"] = "Daily Loss Limit (%)"
    ws["B12"] = 4.0
    ws["C12"] = "Limită zilnică prop firm; depășire = cont mort"

    ws["A13"] = "Daily Loss Limit ($)"
    ws["B13"] = "=B9*B12/100"
    ws["C13"] = "Auto — derivat din B9 și B12"

    ws["A14"] = "Max Loss Limit (%)"
    ws["B14"] = 7.0
    ws["C14"] = "Limită totală pe cont; depășire = cont mort"

    ws["A15"] = "Max Loss Limit ($)"
    ws["B15"] = "=B9*B14/100"
    ws["C15"] = "Auto — derivat din B9 și B14"

    for r in (9, 10, 11, 12, 14):  # inputuri galbene
        ws.cell(row=r, column=2).fill = INPUT_FILL
        ws.cell(row=r, column=2).border = BORDER
    for r in (13, 15):  # derived blue
        ws.cell(row=r, column=2).fill = DERIVED_FILL
        ws.cell(row=r, column=2).border = BORDER

    ws["B9"].number_format = "$#,##0"
    ws["B10"].number_format = "0"
    ws["B11"].number_format = "$#,##0"
    ws["B12"].number_format = '0.0"%"'
    ws["B13"].number_format = "$#,##0"
    ws["B14"].number_format = '0.0"%"'
    ws["B15"].number_format = "$#,##0"

    # Liste dropdown — coloanele E–J (6 coloane)
    list_columns = [
        ("Strategii", STRATEGIES),
        ("Sesiuni (auto)", SESSIONS),
        ("Indicatori", INDICATORS),
        ("TF", TIMEFRAMES),
        ("Direcție", DIRECTIONS),
        ("Outcome", OUTCOMES),
    ]
    for col_idx, (label, values) in enumerate(list_columns, start=5):
        cell = ws.cell(row=3, column=col_idx, value=label)
        cell.font = HEADER_FONT
        cell.fill = HEADER_FILL
        cell.alignment = CENTER
        for row_idx, v in enumerate(values, start=4):
            c = ws.cell(row=row_idx, column=col_idx, value=v)
            c.alignment = CENTER

    widths = {
        "A": 24, "B": 14, "C": 38, "D": 2,
        "E": 14, "F": 14, "G": 13, "H": 10, "I": 10, "J": 12,
    }
    for col, w in widths.items():
        ws.column_dimensions[col].width = w


# ---------------------------------------------------------------------------
# Formula builders pentru Trades sheet
# ---------------------------------------------------------------------------


def _f_day(r: int) -> str:
    d = f'{COL["Data"]}{r}'
    return (
        f'=IF({d}="","",'
        f'CHOOSE(WEEKDAY({d},2),"Lu","Ma","Mi","Jo","Vi","Sa","Du"))'
    )


def _f_session(r: int) -> str:
    """Derivă Sesiunea M2D din Data + Ora RO."""
    d = f'{COL["Data"]}{r}'
    t = f'{COL["Ora RO"]}{r}'
    wd = f"WEEKDAY({d},2)"
    mid_week = f"AND({wd}>=2,{wd}<=4)"
    return (
        f'=IF(OR({d}="",{t}=""),"",'
        f"IF(OR({wd}=1,{wd}=5),\"D\","
        f'IF(AND({t}>=TIME(15,30,0),{t}<TIME(16,30,0)),"C",'
        f'IF(AND({mid_week},{t}>=TIME(16,35,0),{t}<TIME(17,0,0)),"A1",'
        f'IF(AND({mid_week},{t}>=TIME(17,0,0),{t}<TIME(18,0,0)),"A2",'
        f'IF(AND({mid_week},{t}>=TIME(18,0,0),{t}<TIME(19,0,0)),"A3",'
        f'IF(AND({mid_week},{t}>=TIME(22,0,0),{t}<TIME(22,45,0)),"B",'
        f'"Other")))))))'
    )


def _f_r_tp0only(r: int) -> str:
    o = f'{COL["Outcome"]}{r}'
    sl = f'{COL["SL %"]}{r}'
    tp0 = f'{COL["TP0 %"]}{r}'
    return f'=IF({o}="","",IF({o}="SL",-1,{tp0}/{sl}))'


def _f_r_tp1only(r: int) -> str:
    o = f'{COL["Outcome"]}{r}'
    sl = f'{COL["SL %"]}{r}'
    tp1 = f'{COL["TP1 %"]}{r}'
    return (
        f'=IF({o}="","",'
        f'IF(OR({o}="SL",{o}="TP0"),-1,{tp1}/{sl}))'
    )


def _f_r_tp2only(r: int) -> str:
    o = f'{COL["Outcome"]}{r}'
    sl = f'{COL["SL %"]}{r}'
    tp2 = f'{COL["TP2 %"]}{r}'
    return f'=IF({o}="","",IF({o}="TP2",{tp2}/{sl},-1))'


def _f_r_hybrid_be(r: int) -> str:
    o = f'{COL["Outcome"]}{r}'
    sl = f'{COL["SL %"]}{r}'
    tp0 = f'{COL["TP0 %"]}{r}'
    tp1 = f'{COL["TP1 %"]}{r}'
    return (
        f'=IF({o}="","",'
        f'IF({o}="SL",-1,'
        f'IF({o}="TP0",0.5*{tp0}/{sl},'
        f'0.5*({tp0}+{tp1})/{sl})))'
    )


def _f_r_hybrid_nobe(r: int) -> str:
    o = f'{COL["Outcome"]}{r}'
    sl = f'{COL["SL %"]}{r}'
    tp0 = f'{COL["TP0 %"]}{r}'
    tp1 = f'{COL["TP1 %"]}{r}'
    return (
        f'=IF({o}="","",'
        f'IF({o}="SL",-1,'
        f'IF({o}="TP0",0.5*{tp0}/{sl}-0.5,'
        f'0.5*({tp0}+{tp1})/{sl})))'
    )


R_FN: dict[str, callable] = {
    "tp0only": _f_r_tp0only,
    "tp1only": _f_r_tp1only,
    "tp2only": _f_r_tp2only,
    "hybrid_be": _f_r_hybrid_be,
    "hybrid_nobe": _f_r_hybrid_nobe,
}


def _f_dollar(r: int, r_col: str) -> str:
    """$ P&L per trade = R × SL% × Contracte × $/1% per contract (TradeLocker real)."""
    rc = f"{COL[r_col]}{r}"
    sl = f"{COL['SL %']}{r}"
    return f'=IF({rc}="","",{rc}*{sl}*Config!$B$10*Config!$B$11)'


def _f_sl_dollar(r: int) -> str:
    """SL $ = SL% × Contracte × $/1% per contract."""
    sl = f"{COL['SL %']}{r}"
    return f'=IF({sl}="","",{sl}*Config!$B$10*Config!$B$11)'


def _f_sl_dollar_prop(r: int) -> str:
    """SL $ pe contul de prop — același cont real, formula identică cu SL $."""
    sl = f"{COL['SL %']}{r}"
    return f'=IF({sl}="","",{sl}*Config!$B$10*Config!$B$11)'


def _f_balance(r: int, dollar_col: str) -> str:
    dc = COL[dollar_col]
    return f'=IF({dc}{r}="","",Config!$B$4 + SUM(${dc}$2:{dc}{r}))'


def _f_win(r: int, r_col: str) -> str:
    rc = f"{COL[r_col]}{r}"
    return f'=IF({rc}="","",IF({rc}>0,1,0))'


def _f_peak(r: int, balance_col: str, peak_col: str) -> str:
    bc = COL[balance_col]
    pc = COL[peak_col]
    if r == 2:
        return f'=IF({bc}{r}="","",{bc}{r})'
    return (
        f'=IF({bc}{r}="","",'
        f'IF({pc}{r-1}="",{bc}{r},MAX({pc}{r-1},{bc}{r})))'
    )


def _f_drawdown(r: int, peak_col: str, balance_col: str) -> str:
    pc = f"{COL[peak_col]}{r}"
    bc = f"{COL[balance_col]}{r}"
    return f'=IF({bc}="","",{pc}-{bc})'


def _f_dollar_prop(r: int, r_col: str) -> str:
    """$ P&L pe contul de prop — același calcul ca _f_dollar (cont real TradeLocker).

    Diferența între cont abstract și prop e doar balanța de start; $-ul per trade
    e identic pentru că reflectă realitatea contractelor tranzacționate.
    """
    rc = f"{COL[r_col]}{r}"
    sl = f"{COL['SL %']}{r}"
    return f'=IF({rc}="","",{rc}*{sl}*Config!$B$10*Config!$B$11)'


def _f_balance_prop(r: int, dollar_col: str) -> str:
    dc = COL[dollar_col]
    return f'=IF({dc}{r}="","",Config!$B$9 + SUM(${dc}$2:{dc}{r}))'


def _f_daily_pl(r: int, dollar_col: str) -> str:
    """Cumul P&L pe ziua curentă (până la rândul r inclusiv)."""
    dc = COL[dollar_col]
    d_col = COL["Data"]
    d = f"{d_col}{r}"
    return (
        f'=IF(OR({dc}{r}="",{d}=""),"",'
        f'SUMIFS(${dc}$2:{dc}{r},${d_col}$2:{d_col}{r},{d}))'
    )


# ---------------------------------------------------------------------------
# Trades sheet
# ---------------------------------------------------------------------------


def build_trades(wb: Workbook) -> None:
    ws = wb.create_sheet("Trades", 1)
    ws.sheet_view.showGridLines = False
    ws.freeze_panes = "B2"

    # Headers
    for col_idx, header in enumerate(TRADES_HEADERS, start=1):
        cell = ws.cell(row=1, column=col_idx, value=header)
        cell.font = HEADER_FONT
        cell.fill = HEADER_FILL
        cell.alignment = CENTER
        cell.border = BORDER

    # Formule pe toate rândurile pre-pregătite
    for r in range(2, MAX_ROWS + 2):
        ws.cell(row=r, column=1, value="=ROW()-1")
        ws[f'{COL["Zi"]}{r}'] = _f_day(r)
        ws[f'{COL["Sesiune"]}{r}'] = _f_session(r)
        ws[f'{COL["SL $"]}{r}'] = _f_sl_dollar(r)
        ws[f'{COL["SL $ Prop"]}{r}'] = _f_sl_dollar_prop(r)

        for strat in STRAT_KEYS:
            ws[f'{COL[f"R_{strat}"]}{r}'] = R_FN[strat](r)
            ws[f'{COL[f"$_{strat}"]}{r}'] = _f_dollar(r, f"R_{strat}")
            ws[f'{COL[f"Bal_{strat}"]}{r}'] = _f_balance(r, f"$_{strat}")
            ws[f'{COL[f"Win_{strat}"]}{r}'] = _f_win(r, f"R_{strat}")
            ws[f'{COL[f"Peak_{strat}"]}{r}'] = _f_peak(
                r, f"Bal_{strat}", f"Peak_{strat}"
            )
            ws[f'{COL[f"DD_{strat}"]}{r}'] = _f_drawdown(
                r, f"Peak_{strat}", f"Bal_{strat}"
            )
            # Prop firm tracking — paralel cu modelul abstract
            ws[f'{COL[f"$Prop_{strat}"]}{r}'] = _f_dollar_prop(r, f"R_{strat}")
            ws[f'{COL[f"BalProp_{strat}"]}{r}'] = _f_balance_prop(r, f"$Prop_{strat}")
            ws[f'{COL[f"DailyPL_{strat}"]}{r}'] = _f_daily_pl(r, f"$Prop_{strat}")
            ws[f'{COL[f"PeakProp_{strat}"]}{r}'] = _f_peak(
                r, f"BalProp_{strat}", f"PeakProp_{strat}"
            )
            ws[f'{COL[f"DDProp_{strat}"]}{r}'] = _f_drawdown(
                r, f"PeakProp_{strat}", f"BalProp_{strat}"
            )

    # Sample row 2
    ws["B2"] = date(2026, 5, 13)
    ws["C2"] = time(17, 33)
    ws[f'{COL["Strategie"]}2'] = "M2D"
    ws[f'{COL["Indicator"]}2'] = "DIA"
    ws[f'{COL["TF"]}2'] = "1min"
    ws[f'{COL["Direcție"]}2'] = "Sell"
    ws[f'{COL["SL %"]}2'] = 0.30
    ws[f'{COL["TP0 %"]}2'] = 0.10
    ws[f'{COL["TP1 %"]}2'] = 0.15
    ws[f'{COL["TP2 %"]}2'] = 0.30
    ws[f'{COL["Outcome"]}2'] = "TP1"
    ws[f'{COL["Notes"]}2'] = "Exemplu — șterge când începi"

    # Number formats
    for col_name in ("SL %", "TP0 %", "TP1 %", "TP2 %"):
        for r in range(2, MAX_ROWS + 2):
            ws[f"{COL[col_name]}{r}"].number_format = '0.000"%"'

    for col_name in ("SL $", "SL $ Prop"):
        for r in range(2, MAX_ROWS + 2):
            ws[f"{COL[col_name]}{r}"].number_format = '"$"#,##0.00'

    for strat in STRAT_KEYS:
        for r in range(2, MAX_ROWS + 2):
            ws[f"{COL[f'R_{strat}']}{r}"].number_format = "+0.000;-0.000;0.000"
            for prefix in (
                "$_", "Bal_", "Peak_", "DD_",
                "$Prop_", "BalProp_", "DailyPL_", "PeakProp_", "DDProp_",
            ):
                ws[f"{COL[f'{prefix}{strat}']}{r}"].number_format = '"$"#,##0.00'

    for r in range(2, MAX_ROWS + 2):
        ws[f"B{r}"].number_format = "yyyy-mm-dd"

    # Coloring
    input_letters = {
        COL[n]
        for n in (
            "Data", "Ora RO", "Strategie", "Indicator", "TF",
            "Direcție", "SL %", "TP0 %", "TP1 %", "TP2 %",
            "Outcome", "Notes",
        )
    }
    derived_letters = {COL["Zi"], COL["Sesiune"], COL["SL $"], COL["SL $ Prop"]}
    for strat in STRAT_KEYS:
        for prefix in ("R_", "$_", "Bal_", "$Prop_", "BalProp_"):
            derived_letters.add(COL[f"{prefix}{strat}"])
    helper_letters = set()
    for strat in STRAT_KEYS:
        for prefix in ("Win_", "Peak_", "DD_", "DailyPL_", "PeakProp_", "DDProp_"):
            helper_letters.add(COL[f"{prefix}{strat}"])

    for r in range(2, MAX_ROWS + 2):
        for cl in input_letters:
            ws[f"{cl}{r}"].fill = INPUT_FILL
        for cl in derived_letters:
            ws[f"{cl}{r}"].fill = DERIVED_FILL
        for cl in helper_letters:
            ws[f"{cl}{r}"].fill = HIDDEN_FILL

    # Column widths
    widths = {
        "A": 5, "B": 12, "C": 9, "D": 5, "E": 9,
        "F": 12, "G": 11, "H": 8, "I": 9,
        "J": 9, "K": 9, "L": 9, "M": 9,
        "N": 11, "O": 28,
    }
    for col, w in widths.items():
        ws.column_dimensions[col].width = w
    for col_name in ("SL $", "SL $ Prop"):
        ws.column_dimensions[COL[col_name]].width = 12
    # Derived + helper: width 11
    for strat in STRAT_KEYS:
        for prefix in (
            "R_", "$_", "Bal_", "Win_", "Peak_", "DD_",
            "$Prop_", "BalProp_", "DailyPL_", "PeakProp_", "DDProp_",
        ):
            ws.column_dimensions[COL[f"{prefix}{strat}"]].width = 11

    # Ascund helper-ele prop firm într-un outline collapsible
    for strat in STRAT_KEYS:
        for prefix in ("DailyPL_", "PeakProp_", "DDProp_"):
            cl = COL[f"{prefix}{strat}"]
            ws.column_dimensions[cl].outlineLevel = 1
            ws.column_dimensions[cl].hidden = True

    # Data validation dropdowns
    def _add_dv(col_name: str, source: str) -> None:
        cl = COL[col_name]
        dv = DataValidation(
            type="list", formula1=source,
            allow_blank=True, showErrorMessage=True,
        )
        dv.error = "Valoare invalidă — folosește dropdown-ul."
        dv.errorTitle = "Input invalid"
        dv.add(f"{cl}2:{cl}{MAX_ROWS + 1}")
        ws.add_data_validation(dv)

    # Config columns: E=Strategii, F=Sesiuni, G=Indicatori, H=TF, I=Direcție, J=Outcome
    _add_dv("Strategie", "=Config!$E$4:$E$8")
    _add_dv("Indicator", "=Config!$G$4:$G$9")
    _add_dv("TF", "=Config!$H$4:$H$6")
    _add_dv("Direcție", "=Config!$I$4:$I$5")
    _add_dv("Outcome", "=Config!$J$4:$J$7")

    # Conditional formatting pe coloanele R (5 strategii)
    green_fill = PatternFill("solid", fgColor="C6EFCE")
    red_fill = PatternFill("solid", fgColor="FFC7CE")
    grey_fill = PatternFill("solid", fgColor="D9D9D9")
    for strat in STRAT_KEYS:
        cl = COL[f"R_{strat}"]
        rng = f"{cl}2:{cl}{MAX_ROWS + 1}"
        ws.conditional_formatting.add(
            rng, CellIsRule(operator="greaterThan", formula=["0"], fill=green_fill)
        )
        ws.conditional_formatting.add(
            rng, CellIsRule(operator="lessThan", formula=["0"], fill=red_fill)
        )
        ws.conditional_formatting.add(
            rng, CellIsRule(operator="equal", formula=["0"], fill=grey_fill)
        )


# ---------------------------------------------------------------------------
# Dashboard sheet
# ---------------------------------------------------------------------------


def _range(col_name: str) -> str:
    cl = COL[col_name]
    return f"Trades!${cl}$2:${cl}${MAX_ROWS + 1}"


METRIC_HINTS: dict[str, str] = {
    "Trades Placed": (
        "Câte trade-uri ai logat în total.\n"
        "Cu cât N e mai mare, cu atât celelalte metrici sunt mai de încredere.\n"
        "Exemplu: la N=10 Win Ratio e zgomot pur, la N=40 începe să aibă semnal, la N=100 e solid."
    ),
    "Wins": (
        "Câte trade-uri s-au închis pe plus (R > 0).\n"
        "Singur nu spune nimic — privește-l raportat la total (vezi Win Ratio mai jos)."
    ),
    "Win Ratio": (
        "Procentul de trade-uri câștigătoare. WR = 60% înseamnă 6 wins din 10 trade-uri.\n"
        "Singur NU spune dacă strategia e profitabilă — citește-l împreună cu R:R de pe rândul următor."
    ),
    "Average Win ($)": (
        "Câștigul mediu pe trade-urile pozitive.\n"
        "Comparat cu Average Loss îți spune cât de mari sunt câștigurile vs pierderile.\n"
        "Exemplu: 4 wins de $50 și 2 wins de $80 — Average Win = $60."
    ),
    "Average Loss ($)": (
        "Pierderea medie pe trade-urile negative (cifra apare cu minus).\n"
        "În dolari reali, −1R depinde de SL%: pierdere ≈ SL% × Contracte × $/1% per contract.\n"
        "Dacă e mult mai mare decât riscul calculat din SL, ai SL-uri sărite (slippage, gap-uri)."
    ),
    "Best Trade ($)": (
        "Cel mai mare câștig individual.\n"
        "Dacă majoritatea profitului total vine dintr-un singur trade outlier, edge-ul e fragil — "
        "elimini acel trade și strategia devine pierzătoare."
    ),
    "Worst Trade ($)": (
        "Cea mai mare pierdere individuală.\n"
        "Ar trebui să fie aproximativ egală cu −1R calculat din SL% × Contracte × $/1% per contract.\n"
        "Pe TradeLocker DIA: SL=0.30%, 1 contract → ≈ −$3000. Dacă e mai mare, ai slippage/gap."
    ),
    "Profit Factor": (
        "Total bani câștigați împărțit la total bani pierduți (în valoare absolută).\n"
        "Sub 1.0 = pierzi pe ansamblu. Peste 1.5 = solid. Peste 2.0 = câștigi de 2× cât pierzi.\n"
        "Exemplu: 4 wins de $50 (= $200) + 6 losses de $30 (= $180) — PF = 200÷180 = 1.11, profitabil marginal."
    ),
    "Risk:Reward": (
        "De câte ori e mai mare câștigul mediu decât pierderea medie.\n"
        "R:R = 2 înseamnă: când câștigi, câștigi $2; când pierzi, pierzi $1.\n"
        "Cu R:R mare poți avea Win Ratio mic și tot să faci bani."
    ),
    "Expectancy (R)": (
        "Cât câștigi în medie pe UN trade, exprimat în R.\n"
        "+0.30R = câștigi 0.30 × riscul mediu al trade-urilor.\n"
        "−0.10R = pierzi 0.10 × riscul mediu al trade-urilor.\n"
        "Pragul de GO LIVE: +0.20R sau mai mult."
    ),
    "Expectancy ($)": (
        "Aceeași expectancy convertită în dolari, folosind SL% × Contracte × $/1% per contract.\n"
        "Util ca să vezi cât câștigi în medie pe trade în bani reali (TradeLocker), nu doar în R."
    ),
    "Cumulative P&L ($)": (
        "Suma profitului și pierderii pe toate trade-urile logate.\n"
        "E ce-ai avea în plus (sau minus) față de balanța de start din Config."
    ),
    "HWM Balance ($)": (
        "Highest Watermark — cea mai mare balanță atinsă vreodată în jurnal.\n"
        "Punct de referință pentru calculul drawdown-ului."
    ),
    "Max Drawdown ($)": (
        "Cea mai mare cădere ($) din vârf la fundul ulterior al balanței. Măsoară durerea psihologică maximă.\n"
        "Exemplu: ai urcat la $11,500, ai coborât la $9,800 — DD = $1,700, adică 17% din peak.\n"
        "Un drawdown mare la backtest e foarte greu de tolerat în live cu bani reali — așteaptă-te să renunți."
    ),
    # ---- Prop firm metrics ----
    "Account Prop Start ($)": (
        "Capitalul de start al contului de prop firm (default $50,000).\n"
        "Editabil în Config B9."
    ),
    "Position Size ($)": (
        "Configurare contract real TradeLocker:\n"
        "  • Contracte per trade (Config B10) — câte contracte tranzacționezi pe semnal.\n"
        "  • $ per 1% per contract (Config B11) — pe DIA: 0.10% = $1000 → 1% = $10,000.\n"
        "Pierderea pe SL = SL% × Contracte × $/1% per contract. Pentru SL=0.30%, 1 contract → $3000."
    ),
    "Cumulative P&L Prop ($)": (
        "Profitul total al contului de prop pe traseul logat.\n"
        "Reflectă $ real (SL% × Contracte × $/1% per contract), nu un procent abstract din cont.\n"
        "Adunat peste $50,000 dă balanța finală reală."
    ),
    "Final Balance Prop ($)": (
        "Balanța finală a contului de prop = $50,000 + Cumulative P&L Prop.\n"
        "Compar-o cu pragul de stop-out al firmei de prop: $50,000 − $3,500 = $46,500."
    ),
    "Worst Daily Loss ($)": (
        "Cea mai proastă pierdere cumulativă într-o zi calendaristică.\n"
        "Dacă e mai mică decât −$2,000, ai depășit Daily Loss Limit într-o zi — cont mort.\n"
        "Atenție: un singur breach = pierdere cont, indiferent dacă ai recuperat ulterior."
    ),
    "Daily Limit Status": (
        "PASS dacă nicio zi nu a depășit Daily Loss Limit ($2,000 default).\n"
        "FAIL = strategia ar fi pierdut contul prin daily breach pe traseul logat."
    ),
    "Max Account Drawdown ($)": (
        "Cea mai mare cădere de la peak pe contul de prop.\n"
        "Dacă > $3,500 (7% din $50k), ai depășit Max Loss Limit — cont mort."
    ),
    "Max Loss Status": (
        "PASS dacă Max Account Drawdown ≤ $3,500.\n"
        "FAIL = strategia ar fi pierdut contul prin drawdown cumulativ."
    ),
    "Overall Prop Status": (
        "CONFORM = strategia ar fi supraviețuit pe contul de prop pe traseul logat.\n"
        "CONT PIERDUT = cel puțin o breach (daily sau max) — strategia nu e viabilă pe acest cont prop."
    ),
}


def build_dashboard(wb: Workbook) -> None:
    ws = wb.create_sheet("Dashboard", 2)
    ws.sheet_view.showGridLines = False

    ws["A1"] = "Backtest Dashboard"
    ws["A1"].font = TITLE_FONT
    ws.merge_cells("A1:G1")

    ws["A2"] = (
        "Comparație 5 strategii management — pe aceleași semnale blackbox"
    )
    ws["A2"].font = Font(name="Calibri", size=10, italic=True, color="595959")
    ws.merge_cells("A2:G2")

    # Row 4: headers (5 columns B-F pentru strategii + G pentru "Cum citesc")
    ws["A4"] = "Metric"
    strat_cols = {}  # strat_key → column letter (B/C/D/E/F)
    for i, strat in enumerate(STRAT_KEYS):
        letter = get_column_letter(2 + i)
        strat_cols[strat] = letter
        ws[f"{letter}4"] = STRAT_LABELS[strat]
    ws["G4"] = "Cum citesc"
    for letter in ["A"] + list(strat_cols.values()) + ["G"]:
        c = ws[f"{letter}4"]
        c.font = HEADER_FONT
        c.fill = HEADER_FILL
        c.alignment = CENTER
        c.border = BORDER

    # Ranges per strategie
    R = {s: _range(f"R_{s}") for s in STRAT_KEYS}
    D = {s: _range(f"$_{s}") for s in STRAT_KEYS}
    W = {s: _range(f"Win_{s}") for s in STRAT_KEYS}
    BAL = {s: _range(f"Bal_{s}") for s in STRAT_KEYS}
    DD = {s: _range(f"DD_{s}") for s in STRAT_KEYS}
    OUTCOME_RANGE = _range("Outcome")

    # Metric rows — fiecare metric e un dict cu per-strategy formula + format
    metrics: list[tuple[str, callable, str]] = [
        # (label, fn(strat_key) -> formula, number_format)
        ("Trades Placed", lambda s: f'=COUNTA({OUTCOME_RANGE})', "0"),
        ("Wins", lambda s: f'=COUNTIF({W[s]},1)', "0"),
        ("Average Win ($)", lambda s: f'=IFERROR(AVERAGEIF({D[s]},">0"),0)', '"$"#,##0.00'),
        ("Average Loss ($)", lambda s: f'=IFERROR(AVERAGEIF({D[s]},"<0"),0)', '"$"#,##0.00'),
        ("Best Trade ($)", lambda s: f'=IFERROR(MAX({D[s]}),0)', '"$"#,##0.00'),
        ("Worst Trade ($)", lambda s: f'=IFERROR(MIN({D[s]}),0)', '"$"#,##0.00'),
        ("Profit Factor", lambda s: f'=IFERROR(SUMIF({D[s]},">0")/ABS(SUMIF({D[s]},"<0")),0)', "0.00"),
        # Win Ratio: depends on Wins + Trades Placed — handled after metrics list (placeholder)
        ("Win Ratio", lambda s: None, "0.0%"),
        # Risk:Reward — placeholder; bazat pe rândurile Avg Win/Loss
        ("Risk:Reward", lambda s: None, "0.00"),
        ("Expectancy (R)", lambda s: f'=IFERROR(AVERAGE({R[s]}),0)', "+0.000;-0.000;0.000"),
        ("Expectancy ($)", lambda s: f'=IFERROR(AVERAGE({D[s]}),0)', '"$"#,##0.00'),
        ("Cumulative P&L ($)", lambda s: f'=SUM({D[s]})', '"$"#,##0.00'),
        # HWM — placeholder cu ref la Trades Placed (row 5)
        ("HWM Balance ($)", lambda s: None, '"$"#,##0.00'),
        ("Max Drawdown ($)", lambda s: f'=IFERROR(MAX({DD[s]}),0)', '"$"#,##0.00'),
    ]

    # Determine row indexes pentru formule speciale (depind de poziție)
    label_to_row = {label: 5 + idx for idx, (label, _, _) in enumerate(metrics)}
    trades_row = label_to_row["Trades Placed"]
    wins_row = label_to_row["Wins"]
    avg_win_row = label_to_row["Average Win ($)"]
    avg_loss_row = label_to_row["Average Loss ($)"]

    for idx, (label, fn, fmt) in enumerate(metrics):
        r = 5 + idx
        ws[f"A{r}"] = label
        ws[f"A{r}"].font = Font(name="Calibri", size=11, bold=True)
        ws[f"A{r}"].border = BORDER
        ws[f"A{r}"].alignment = LEFT
        for strat in STRAT_KEYS:
            letter = strat_cols[strat]
            if label == "Win Ratio":
                formula = f"=IFERROR({letter}{wins_row}/{letter}{trades_row},0)"
            elif label == "Risk:Reward":
                formula = f"=IFERROR({letter}{avg_win_row}/ABS({letter}{avg_loss_row}),0)"
            elif label == "HWM Balance ($)":
                formula = (
                    f"=IF({letter}{trades_row}=0,Config!$B$4,MAX({BAL[strat]}))"
                )
            else:
                formula = fn(strat)
            cell = ws[f"{letter}{r}"]
            cell.value = formula
            cell.number_format = fmt
            cell.fill = DERIVED_FILL
            cell.border = BORDER
            cell.alignment = RIGHT
        # Coloana G — interpretare narativă + exemplu numeric
        hint_cell = ws[f"G{r}"]
        hint_cell.value = METRIC_HINTS.get(label, "")
        hint_cell.font = Font(name="Calibri", size=10, color="595959")
        hint_cell.alignment = Alignment(horizontal="left", vertical="top", wrap_text=True)
        hint_cell.border = BORDER

    # ---- FERESTRE CANDIDATE x STRATEGIE ----
    # Tabel principal pentru alegerea ferestrei tradabile. Drawdown-ul este
    # calculat cu helper-e ascunse pe fereastra curenta, nu din DD global.
    window_title_row = 5 + len(metrics) + 2
    ws[f"A{window_title_row}"] = "FERESTRE CANDIDATE x STRATEGIE"
    ws[f"A{window_title_row}"].font = SUBTITLE_FONT
    ws.merge_cells(f"A{window_title_row}:R{window_title_row}")

    window_header_row = window_title_row + 1
    window_headers = [
        "Fereastra", "Start", "End", "Strategie", "N", "Wins", "WR",
        "Expectancy R", "Expectancy $", "Profit Factor", "Cum P&L $",
        "Max Drawdown $", "Worst Daily Loss Prop $", "Max Drawdown Prop $",
        "Daily Breach", "Max Loss Breach", "Status Prop", "Status Edge",
        "Score",
    ]
    for col_idx, header in enumerate(window_headers, start=1):
        c = ws.cell(row=window_header_row, column=col_idx, value=header)
        c.font = HEADER_FONT
        c.fill = HEADER_FILL
        c.alignment = CENTER
        c.border = BORDER

    TIME_RANGE = _range("Ora RO")
    PROP_D = {s: _range(f"$Prop_{s}") for s in STRAT_KEYS}
    helper_start_col = 27  # AA, ascuns.

    def _emit_window_helpers(visible_row: int, strat: str, combo_idx: int) -> dict[str, str]:
        base_col = helper_start_col + combo_idx * 7
        helper_names = ["Cum", "Peak", "DD", "DailyProp", "CumProp", "PeakProp", "DDProp"]
        cols = {name: get_column_letter(base_col + idx) for idx, name in enumerate(helper_names)}
        for idx, name in enumerate(helper_names):
            col = get_column_letter(base_col + idx)
            ws[f"{col}1"] = f"{name}_{visible_row}"
            ws.column_dimensions[col].hidden = True
            ws.column_dimensions[col].width = 3

        start_cell = f"$B${visible_row}"
        end_cell = f"$C${visible_row}"
        dollar_col = COL[f"$_{strat}"]
        prop_col = COL[f"$Prop_{strat}"]
        time_col = COL["Ora RO"]
        date_col = COL["Data"]
        outcome_col = COL["Outcome"]

        for helper_row, trade_row in enumerate(range(2, MAX_ROWS + 2), start=2):
            in_window = (
                f'AND(Trades!${outcome_col}{trade_row}<>"",'
                f"Trades!${time_col}{trade_row}>={start_cell},"
                f"Trades!${time_col}{trade_row}<{end_cell})"
            )
            dollar = f"Trades!${dollar_col}{trade_row}"
            prop = f"Trades!${prop_col}{trade_row}"
            if helper_row == 2:
                ws[f"{cols['Cum']}{helper_row}"] = f"=IF({in_window},{dollar},0)"
                ws[f"{cols['Peak']}{helper_row}"] = f"=MAX(0,{cols['Cum']}{helper_row})"
                ws[f"{cols['CumProp']}{helper_row}"] = f"=IF({in_window},{prop},0)"
                ws[f"{cols['PeakProp']}{helper_row}"] = f"=MAX(0,{cols['CumProp']}{helper_row})"
            else:
                prev = helper_row - 1
                ws[f"{cols['Cum']}{helper_row}"] = (
                    f"={cols['Cum']}{prev}+IF({in_window},{dollar},0)"
                )
                ws[f"{cols['Peak']}{helper_row}"] = (
                    f"=MAX({cols['Peak']}{prev},{cols['Cum']}{helper_row})"
                )
                ws[f"{cols['CumProp']}{helper_row}"] = (
                    f"={cols['CumProp']}{prev}+IF({in_window},{prop},0)"
                )
                ws[f"{cols['PeakProp']}{helper_row}"] = (
                    f"=MAX({cols['PeakProp']}{prev},{cols['CumProp']}{helper_row})"
                )
            ws[f"{cols['DD']}{helper_row}"] = (
                f"={cols['Peak']}{helper_row}-{cols['Cum']}{helper_row}"
            )
            ws[f"{cols['DDProp']}{helper_row}"] = (
                f"={cols['PeakProp']}{helper_row}-{cols['CumProp']}{helper_row}"
            )
            ws[f"{cols['DailyProp']}{helper_row}"] = (
                f'=IF({in_window},'
                f'SUMIFS(Trades!${prop_col}$2:Trades!${prop_col}{trade_row},'
                f'Trades!${date_col}$2:Trades!${date_col}{trade_row},Trades!${date_col}{trade_row},'
                f'Trades!${time_col}$2:Trades!${time_col}{trade_row},">="&{start_cell},'
                f'Trades!${time_col}$2:Trades!${time_col}{trade_row},"<"&{end_cell}),'
                f'"")'
            )
        return cols

    pass_fill = PatternFill("solid", fgColor="C6EFCE")
    fail_fill = PatternFill("solid", fgColor="FFC7CE")
    warn_fill = PatternFill("solid", fgColor="FFEB9C")
    combo_rows: list[int] = []
    combo_idx = 0
    row = window_header_row + 1
    for label, start_time, end_time in TRADABLE_WINDOWS:
        for strat in STRAT_KEYS:
            helper_cols = _emit_window_helpers(row, strat, combo_idx)
            ws[f"A{row}"] = label
            ws[f"B{row}"] = start_time
            ws[f"C{row}"] = end_time
            ws[f"D{row}"] = STRAT_LABELS[strat]
            ws[f"E{row}"] = (
                f'=COUNTIFS({OUTCOME_RANGE},"<>",{TIME_RANGE},">="&B{row},'
                f'{TIME_RANGE},"<"&C{row})'
            )
            ws[f"F{row}"] = (
                f'=COUNTIFS({W[strat]},1,{OUTCOME_RANGE},"<>",'
                f'{TIME_RANGE},">="&B{row},{TIME_RANGE},"<"&C{row})'
            )
            ws[f"G{row}"] = f"=IFERROR(F{row}/E{row},0)"
            ws[f"H{row}"] = (
                f'=IFERROR(AVERAGEIFS({R[strat]},{OUTCOME_RANGE},"<>",'
                f'{TIME_RANGE},">="&B{row},{TIME_RANGE},"<"&C{row}),0)'
            )
            ws[f"I{row}"] = (
                f'=IFERROR(AVERAGEIFS({D[strat]},{OUTCOME_RANGE},"<>",'
                f'{TIME_RANGE},">="&B{row},{TIME_RANGE},"<"&C{row}),0)'
            )
            ws[f"J{row}"] = (
                f'=IFERROR(SUMIFS({D[strat]},{D[strat]},">0",{OUTCOME_RANGE},"<>",'
                f'{TIME_RANGE},">="&B{row},{TIME_RANGE},"<"&C{row})/'
                f'ABS(SUMIFS({D[strat]},{D[strat]},"<0",{OUTCOME_RANGE},"<>",'
                f'{TIME_RANGE},">="&B{row},{TIME_RANGE},"<"&C{row})),0)'
            )
            ws[f"K{row}"] = (
                f'=SUMIFS({D[strat]},{OUTCOME_RANGE},"<>",'
                f'{TIME_RANGE},">="&B{row},{TIME_RANGE},"<"&C{row})'
            )
            ws[f"L{row}"] = (
                f'=IFERROR(MAX({helper_cols["DD"]}2:{helper_cols["DD"]}{MAX_ROWS + 1}),0)'
            )
            ws[f"M{row}"] = (
                f'=IFERROR(MIN({helper_cols["DailyProp"]}2:'
                f'{helper_cols["DailyProp"]}{MAX_ROWS + 1}),0)'
            )
            ws[f"N{row}"] = (
                f'=IFERROR(MAX({helper_cols["DDProp"]}2:'
                f'{helper_cols["DDProp"]}{MAX_ROWS + 1}),0)'
            )
            ws[f"O{row}"] = f'=IF(M{row}<-Config!$B$13,"DA","NU")'
            ws[f"P{row}"] = f'=IF(N{row}>Config!$B$15,"DA","NU")'
            ws[f"Q{row}"] = f'=IF(OR(O{row}="DA",P{row}="DA"),"CONT PIERDUT","CONFORM")'
            ws[f"R{row}"] = (
                f'=IF(E{row}<1,"",'
                f'IF(OR(O{row}="DA",P{row}="DA"),"BREACH",'
                f'IF(AND(E{row}>=40,G{row}>=55%,H{row}>=0.2),"CANDIDAT","PRE-CANDIDAT")))'
            )
            ws[f"S{row}"] = (
                f'=IF(E{row}<1,-1E+12,'
                f'H{row}*100000+J{row}*1000+K{row}-L{row}-N{row}/10)'
            )
            combo_rows.append(row)
            combo_idx += 1
            row += 1

    for r in combo_rows:
        for c in range(1, len(window_headers) + 1):
            cell = ws.cell(row=r, column=c)
            cell.border = BORDER
            cell.alignment = RIGHT if c not in (1, 4, 15, 16, 17, 18) else CENTER
            if c not in (1, 2, 3, 4):
                cell.fill = DERIVED_FILL
        for c in ("B", "C"):
            ws[f"{c}{r}"].number_format = "hh:mm"
        ws[f"E{r}"].number_format = "0"
        ws[f"F{r}"].number_format = "0"
        ws[f"G{r}"].number_format = "0.0%"
        ws[f"H{r}"].number_format = "+0.000;-0.000;0.000"
        for c in ("I", "K", "L", "M", "N"):
            ws[f"{c}{r}"].number_format = '"$"#,##0.00'
        ws[f"J{r}"].number_format = "0.00"
    ws.column_dimensions["S"].hidden = True

    if combo_rows:
        first_combo = combo_rows[0]
        last_combo = combo_rows[-1]
        status_rng = f"Q{first_combo}:R{last_combo}"
        ws.conditional_formatting.add(
            status_rng, CellIsRule(operator="equal", formula=['"CONFORM"'], fill=pass_fill)
        )
        ws.conditional_formatting.add(
            status_rng, CellIsRule(operator="equal", formula=['"CANDIDAT"'], fill=pass_fill)
        )
        ws.conditional_formatting.add(
            status_rng, CellIsRule(operator="equal", formula=['"CONT PIERDUT"'], fill=fail_fill)
        )
        ws.conditional_formatting.add(
            status_rng, CellIsRule(operator="equal", formula=['"BREACH"'], fill=fail_fill)
        )
        ws.conditional_formatting.add(
            status_rng, CellIsRule(operator="equal", formula=['"PRE-CANDIDAT"'], fill=warn_fill)
        )

    top_title_row = row + 2
    ws[f"A{top_title_row}"] = "TOP CANDIDATE"
    ws[f"A{top_title_row}"].font = SUBTITLE_FONT
    ws.merge_cells(f"A{top_title_row}:J{top_title_row}")
    top_note_row = top_title_row + 1
    ws[f"A{top_note_row}"] = (
        "Top 10 ferestre după scor compus. CANDIDAT = îndeplinește toate pragurile "
        "(N≥40, WR≥55%, ExpR≥0.2, no breach). PRE-CANDIDAT = N≥1 fără breach dar sub praguri. "
        "BREACH = ar fi pierdut contul prop."
    )
    ws[f"A{top_note_row}"].font = Font(name="Calibri", size=10, italic=True, color="595959")
    ws[f"A{top_note_row}"].alignment = Alignment(horizontal="left", vertical="center", wrap_text=True)
    ws.merge_cells(f"A{top_note_row}:J{top_note_row}")
    top_header_row = top_note_row + 1
    top_headers = [
        "#", "Fereastra", "Strategie", "N", "WR", "Expectancy R",
        "Profit Factor", "Cum P&L $", "Max DD Prop $", "Status Edge",
    ]
    for col_idx, header in enumerate(top_headers, start=1):
        c = ws.cell(row=top_header_row, column=col_idx, value=header)
        c.font = HEADER_FONT
        c.fill = HEADER_FILL
        c.alignment = CENTER
        c.border = BORDER

    for idx in range(1, 11):
        r = top_header_row + idx
        ws[f"A{r}"] = idx
        if combo_rows:
            rank_formula = f"LARGE($S${first_combo}:$S${last_combo},{idx})"
            match_formula = f"MATCH({rank_formula},$S${first_combo}:$S${last_combo},0)"
            for target, source in zip(
                ["B", "C", "D", "E", "F", "G", "H", "I", "J"],
                ["A", "D", "E", "G", "H", "J", "K", "N", "R"],
            ):
                ws[f"{target}{r}"] = (
                    f'=IFERROR(IF({rank_formula}<=-1E+11,"",'
                    f'INDEX(${source}${first_combo}:${source}${last_combo},{match_formula})),"")'
                )
        for c in range(1, len(top_headers) + 1):
            cell = ws.cell(row=r, column=c)
            cell.border = BORDER
            cell.alignment = RIGHT if c not in (2, 3, 10) else CENTER
        ws[f"E{r}"].number_format = "0.0%"
        ws[f"F{r}"].number_format = "+0.000;-0.000;0.000"
        ws[f"G{r}"].number_format = "0.00"
        ws[f"H{r}"].number_format = '"$"#,##0.00'
        ws[f"I{r}"].number_format = '"$"#,##0.00'

    # CF pe coloana Status Edge din TOP CANDIDATE
    top_status_rng = f"J{top_header_row + 1}:J{top_header_row + 10}"
    ws.conditional_formatting.add(
        top_status_rng, CellIsRule(operator="equal", formula=['"CANDIDAT"'], fill=pass_fill)
    )
    ws.conditional_formatting.add(
        top_status_rng, CellIsRule(operator="equal", formula=['"PRE-CANDIDAT"'], fill=warn_fill)
    )
    ws.conditional_formatting.add(
        top_status_rng, CellIsRule(operator="equal", formula=['"BREACH"'], fill=fail_fill)
    )

    # Conditional formatting reutilizabil pentru celulele Cum $
    bd_green = PatternFill("solid", fgColor="C6EFCE")
    bd_red = PatternFill("solid", fgColor="FFC7CE")

    # Helper pentru breakdown wide: rânduri = items, coloane = 5 strategii Cum $ + N total
    def _emit_breakdown_strats(
        start_row: int, title: str, first_col_label: str,
        items: list[str], item_range: str,
    ) -> int:
        # Layout: A=item, B..F=5 strategii (Cum $), G=N total
        last_col_idx = 1 + len(STRAT_KEYS) + 1  # A + 5 strategii + N
        last_letter = get_column_letter(last_col_idx)
        ws[f"A{start_row}"] = title
        ws[f"A{start_row}"].font = SUBTITLE_FONT
        ws.merge_cells(f"A{start_row}:{last_letter}{start_row}")
        headers = [first_col_label] + [STRAT_LABELS[s] for s in STRAT_KEYS] + ["N total"]
        for col_idx, h in enumerate(headers, start=1):
            c = ws.cell(row=start_row + 1, column=col_idx, value=h)
            c.font = HEADER_FONT
            c.fill = HEADER_FILL
            c.alignment = CENTER
            c.border = BORDER
        strat_letters = [get_column_letter(2 + i) for i in range(len(STRAT_KEYS))]
        n_letter = get_column_letter(last_col_idx)
        for i, item in enumerate(items):
            r = start_row + 2 + i
            ws[f"A{r}"] = item
            for idx, strat in enumerate(STRAT_KEYS):
                cl = strat_letters[idx]
                ws[f"{cl}{r}"] = f'=SUMIFS({D[strat]},{item_range},"{item}")'
                ws[f"{cl}{r}"].number_format = '"$"#,##0.00'
            ws[f"{n_letter}{r}"] = f'=COUNTIF({item_range},"{item}")'
            ws[f"{n_letter}{r}"].number_format = "0"
            for col_idx in range(1, last_col_idx + 1):
                cell = ws.cell(row=r, column=col_idx)
                cell.border = BORDER
                cell.alignment = LEFT if col_idx == 1 else RIGHT
                if 2 <= col_idx <= 1 + len(STRAT_KEYS):
                    cell.fill = DERIVED_FILL
        # CF pe coloanele 5 strategii: verde >0, roșu <0
        if items:
            first_data_row = start_row + 2
            last_data_row = start_row + 1 + len(items)
            cf_rng = (
                f"{strat_letters[0]}{first_data_row}:"
                f"{strat_letters[-1]}{last_data_row}"
            )
            ws.conditional_formatting.add(
                cf_rng, CellIsRule(operator="greaterThan", formula=["0"], fill=bd_green)
            )
            ws.conditional_formatting.add(
                cf_rng, CellIsRule(operator="lessThan", formula=["0"], fill=bd_red)
            )
        return start_row + 1 + len(items)

    # Breakdowns — toate cele 5 strategii vizibile, Cum P&L $ per strategie
    start = top_header_row + 13
    after_strat = _emit_breakdown_strats(
        start + 2, "PER STRATEGIE — Cum P&L $ per strategie", "Strategie",
        STRATEGIES, _range("Strategie"),
    )
    after_ind = _emit_breakdown_strats(
        after_strat + 2, "PER INDICATOR — Cum P&L $ per strategie", "Indicator",
        INDICATORS, _range("Indicator"),
    )
    after_dir = _emit_breakdown_strats(
        after_ind + 2, "PER DIRECȚIE — Cum P&L $ per strategie", "Direcție",
        DIRECTIONS, _range("Direcție"),
    )

    # ---- PROP FIRM COMPLIANCE ----
    PROP_RANGES = {
        "dollar": {s: _range(f"$Prop_{s}") for s in STRAT_KEYS},
        "balance": {s: _range(f"BalProp_{s}") for s in STRAT_KEYS},
        "daily": {s: _range(f"DailyPL_{s}") for s in STRAT_KEYS},
        "dd": {s: _range(f"DDProp_{s}") for s in STRAT_KEYS},
    }

    prop_title_row = after_dir + 2
    ws[f"A{prop_title_row}"] = "PROP FIRM COMPLIANCE"
    ws[f"A{prop_title_row}"].font = SUBTITLE_FONT
    ws.merge_cells(f"A{prop_title_row}:G{prop_title_row}")

    # Header pentru tabel
    prop_header_row = prop_title_row + 1
    ws[f"A{prop_header_row}"] = "Metric"
    for strat in STRAT_KEYS:
        ws[f"{strat_cols[strat]}{prop_header_row}"] = STRAT_LABELS[strat]
    ws[f"G{prop_header_row}"] = "Cum citesc"
    for letter in ["A"] + list(strat_cols.values()) + ["G"]:
        c = ws[f"{letter}{prop_header_row}"]
        c.font = HEADER_FONT
        c.fill = HEADER_FILL
        c.alignment = CENTER
        c.border = BORDER

    # Definițiile rândurilor — (label, formula_fn(strat), number_format)
    fail_pass_fmt = "@"
    prop_metrics: list[tuple[str, callable, str]] = [
        (
            "Account Prop Start ($)",
            lambda s: "=Config!$B$9",
            '"$"#,##0',
        ),
        (
            "Position Size ($)",
            lambda s: "=Config!$B$11",
            '"$"#,##0',
        ),
        (
            "Cumulative P&L Prop ($)",
            lambda s: f"=SUM({PROP_RANGES['dollar'][s]})",
            '"$"#,##0.00',
        ),
        (
            "Final Balance Prop ($)",
            lambda s: f"=Config!$B$9+SUM({PROP_RANGES['dollar'][s]})",
            '"$"#,##0.00',
        ),
        (
            "Worst Daily Loss ($)",
            lambda s: f"=IFERROR(MIN({PROP_RANGES['daily'][s]}),0)",
            '"$"#,##0.00',
        ),
        # placeholder pentru Daily Status — depinde de Worst Daily de mai sus
        ("Daily Limit Status", lambda s: None, fail_pass_fmt),
        (
            "Max Account Drawdown ($)",
            lambda s: f"=IFERROR(MAX({PROP_RANGES['dd'][s]}),0)",
            '"$"#,##0.00',
        ),
        # placeholder pentru Max Status — depinde de Max DD de mai sus
        ("Max Loss Status", lambda s: None, fail_pass_fmt),
        # placeholder pentru Overall — depinde de cele două statuses
        ("Overall Prop Status", lambda s: None, fail_pass_fmt),
    ]

    prop_label_to_row = {
        label: prop_header_row + 1 + idx
        for idx, (label, _, _) in enumerate(prop_metrics)
    }
    worst_daily_row = prop_label_to_row["Worst Daily Loss ($)"]
    daily_status_row = prop_label_to_row["Daily Limit Status"]
    max_dd_row = prop_label_to_row["Max Account Drawdown ($)"]
    max_status_row = prop_label_to_row["Max Loss Status"]

    for idx, (label, fn, fmt) in enumerate(prop_metrics):
        r = prop_header_row + 1 + idx
        ws[f"A{r}"] = label
        ws[f"A{r}"].font = Font(name="Calibri", size=11, bold=True)
        ws[f"A{r}"].border = BORDER
        ws[f"A{r}"].alignment = LEFT
        for strat in STRAT_KEYS:
            letter = strat_cols[strat]
            if label == "Daily Limit Status":
                formula = (
                    f'=IF({letter}{worst_daily_row}<-Config!$B$13,"FAIL","PASS")'
                )
            elif label == "Max Loss Status":
                formula = (
                    f'=IF({letter}{max_dd_row}>Config!$B$15,"FAIL","PASS")'
                )
            elif label == "Overall Prop Status":
                formula = (
                    f'=IF(OR({letter}{daily_status_row}="FAIL",'
                    f'{letter}{max_status_row}="FAIL"),'
                    f'"CONT PIERDUT","CONFORM")'
                )
            else:
                formula = fn(strat)
            cell = ws[f"{letter}{r}"]
            cell.value = formula
            cell.number_format = fmt
            cell.fill = DERIVED_FILL
            cell.border = BORDER
            cell.alignment = RIGHT if fmt != fail_pass_fmt else CENTER
        # Hint în coloana G
        hint_cell = ws[f"G{r}"]
        hint_cell.value = METRIC_HINTS.get(label, "")
        hint_cell.font = Font(name="Calibri", size=10, color="595959")
        hint_cell.alignment = Alignment(
            horizontal="left", vertical="top", wrap_text=True
        )
        hint_cell.border = BORDER

    # Conditional formatting pe status rows — verde PASS/CONFORM, roșu FAIL/CONT PIERDUT
    pass_fill = PatternFill("solid", fgColor="C6EFCE")
    fail_fill = PatternFill("solid", fgColor="FFC7CE")
    for status_row in (daily_status_row, max_status_row):
        rng = (
            f"{strat_cols[STRAT_KEYS[0]]}{status_row}:"
            f"{strat_cols[STRAT_KEYS[-1]]}{status_row}"
        )
        ws.conditional_formatting.add(
            rng, CellIsRule(operator="equal", formula=['"PASS"'], fill=pass_fill)
        )
        ws.conditional_formatting.add(
            rng, CellIsRule(operator="equal", formula=['"FAIL"'], fill=fail_fill)
        )
    overall_row = prop_label_to_row["Overall Prop Status"]
    overall_rng = (
        f"{strat_cols[STRAT_KEYS[0]]}{overall_row}:"
        f"{strat_cols[STRAT_KEYS[-1]]}{overall_row}"
    )
    ws.conditional_formatting.add(
        overall_rng,
        CellIsRule(operator="equal", formula=['"CONFORM"'], fill=pass_fill),
    )
    ws.conditional_formatting.add(
        overall_rng,
        CellIsRule(
            operator="equal", formula=['"CONT PIERDUT"'], fill=fail_fill
        ),
    )

    # Înălțime rânduri prop (cu hint multi-line)
    for r in range(prop_header_row + 1, prop_header_row + 1 + len(prop_metrics)):
        ws.row_dimensions[r].height = 60

    # ---- PROP FIRM COMPLIANCE per FEREASTRĂ × STRATEGIE ----
    # Reshape compliance: rânduri = combo (fereastră × strategie), coloane = metrici compliance.
    # Datele sunt referențiate direct din FERESTRE CANDIDATE (cols A, D, M, N, O, P, Q).
    if combo_rows:
        win_prop_title_row = prop_header_row + 1 + len(prop_metrics) + 2
        ws[f"A{win_prop_title_row}"] = "PROP FIRM COMPLIANCE — per FEREASTRĂ × STRATEGIE"
        ws[f"A{win_prop_title_row}"].font = SUBTITLE_FONT
        ws.merge_cells(f"A{win_prop_title_row}:G{win_prop_title_row}")

        win_prop_note_row = win_prop_title_row + 1
        ws[f"A{win_prop_note_row}"] = (
            "Defalcat pe fiecare combinație de fereastră tradabilă × strategie management. "
            "CONFORM = ar fi supraviețuit pe contul de prop pe acel slot."
        )
        ws[f"A{win_prop_note_row}"].font = Font(name="Calibri", size=10, italic=True, color="595959")
        ws[f"A{win_prop_note_row}"].alignment = Alignment(horizontal="left", vertical="center", wrap_text=True)
        ws.merge_cells(f"A{win_prop_note_row}:G{win_prop_note_row}")

        win_prop_header_row = win_prop_note_row + 1
        win_prop_headers = [
            "Fereastra", "Strategie", "Worst Daily Prop $", "Max DD Prop $",
            "Daily Breach", "Max Breach", "Overall Prop",
        ]
        for col_idx, h in enumerate(win_prop_headers, start=1):
            c = ws.cell(row=win_prop_header_row, column=col_idx, value=h)
            c.font = HEADER_FONT
            c.fill = HEADER_FILL
            c.alignment = CENTER
            c.border = BORDER

        # source cols din FERESTRE CANDIDATE: A=Fereastra, D=Strategie, M=Worst Daily,
        # N=Max DD, O=Daily Breach, P=Max Breach, Q=Overall Prop
        source_cols = ["A", "D", "M", "N", "O", "P", "Q"]
        for offset, combo_row in enumerate(combo_rows, start=1):
            r = win_prop_header_row + offset
            for col_idx, source in enumerate(source_cols, start=1):
                target = get_column_letter(col_idx)
                ws[f"{target}{r}"] = f"={source}{combo_row}"
                cell = ws[f"{target}{r}"]
                cell.border = BORDER
                cell.fill = DERIVED_FILL
                cell.alignment = CENTER if col_idx in (1, 2, 5, 6, 7) else RIGHT
            ws[f"C{r}"].number_format = '"$"#,##0.00'
            ws[f"D{r}"].number_format = '"$"#,##0.00'

        # CF pe Overall Prop (col G) și pe Daily/Max Breach (cols E, F)
        win_prop_first = win_prop_header_row + 1
        win_prop_last = win_prop_header_row + len(combo_rows)
        overall_rng_win = f"G{win_prop_first}:G{win_prop_last}"
        ws.conditional_formatting.add(
            overall_rng_win, CellIsRule(operator="equal", formula=['"CONFORM"'], fill=pass_fill)
        )
        ws.conditional_formatting.add(
            overall_rng_win, CellIsRule(operator="equal", formula=['"CONT PIERDUT"'], fill=fail_fill)
        )
        breach_rng_win = f"E{win_prop_first}:F{win_prop_last}"
        ws.conditional_formatting.add(
            breach_rng_win, CellIsRule(operator="equal", formula=['"DA"'], fill=fail_fill)
        )
        ws.conditional_formatting.add(
            breach_rng_win, CellIsRule(operator="equal", formula=['"NU"'], fill=pass_fill)
        )

    # Column widths
    widths = {
        "A": 18, "B": 10, "C": 18, "D": 16, "E": 13, "F": 13, "G": 16,
        "H": 13, "I": 13, "J": 12, "K": 13, "L": 15, "M": 20,
        "N": 18, "O": 13, "P": 14, "Q": 15, "R": 13, "S": 8,
    }
    for col, w in widths.items():
        ws.column_dimensions[col].width = w

    # Row height pentru rândurile cu hint (cu wrap) — explicații multi-line
    for r in range(5, 5 + len(metrics)):
        ws.row_dimensions[r].height = 75

    # Equity curve chart — 5 linii
    chart = LineChart()
    chart.title = "Equity Curve — 5 strategii"
    chart.style = 12
    chart.y_axis.title = "Balance ($)"
    chart.x_axis.title = "Trade #"
    chart.height = 12
    chart.width = 24

    data = Reference(
        wb["Trades"],
        min_col=_col_to_int(COL[f"Bal_{STRAT_KEYS[0]}"]),
        max_col=_col_to_int(COL[f"Bal_{STRAT_KEYS[-1]}"]),
        min_row=1,
        max_row=MAX_ROWS + 1,
    )
    chart.add_data(data, titles_from_data=True)
    cats = Reference(
        wb["Trades"], min_col=1, max_col=1,
        min_row=2, max_row=MAX_ROWS + 1,
    )
    chart.set_categories(cats)
    ws.add_chart(chart, "U4")

    # Equity curve prop — al doilea chart, separat de modelul abstract
    chart_prop = LineChart()
    chart_prop.title = "Equity Curve — Prop ($50k start)"
    chart_prop.style = 12
    chart_prop.y_axis.title = "Balance Prop ($)"
    chart_prop.x_axis.title = "Trade #"
    chart_prop.height = 12
    chart_prop.width = 24

    data_prop = Reference(
        wb["Trades"],
        min_col=_col_to_int(COL[f"BalProp_{STRAT_KEYS[0]}"]),
        max_col=_col_to_int(COL[f"BalProp_{STRAT_KEYS[-1]}"]),
        min_row=1,
        max_row=MAX_ROWS + 1,
    )
    chart_prop.add_data(data_prop, titles_from_data=True)
    chart_prop.set_categories(cats)
    ws.add_chart(chart_prop, "U30")


# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------


def build_workbook() -> Workbook:
    wb = Workbook()
    default = wb.active
    wb.remove(default)
    build_config(wb)
    build_trades(wb)
    build_dashboard(wb)
    wb.active = wb.sheetnames.index("Dashboard")
    return wb


def main() -> int:
    OUTPUT.parent.mkdir(parents=True, exist_ok=True)
    if OUTPUT.exists():
        timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
        backup = OUTPUT.with_name(f"{OUTPUT.stem}.backup-{timestamp}{OUTPUT.suffix}")
        shutil.copy2(OUTPUT, backup)
        print(f"Backup {backup}")
    wb = build_workbook()
    wb.save(OUTPUT)
    print(f"Wrote {OUTPUT}")
    return 0


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