"""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.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]
)
PRIMA_HELPERS = [f"PrimaWin_{idx}" for idx in range(len(TRADABLE_WINDOWS))]

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]
    + PRIMA_HELPERS
)
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 real TradeLocker / Prop Firm ----
    # Lanț de calcul transparent: Lot size -> $/punct -> $ risc la 1% mișcare preț.
    # Toate coloanele $ din Trades folosesc B10*B11 (= $ risc la 1% preț pe poziția ta).
    # Scalare la 100k: schimbi B9 (Account) și B10 (Lot size) — restul se recalculează.
    ws["A8"] = "Cont real TradeLocker (prop firm)"
    ws["A8"].font = SUBTITLE_FONT
    ws.merge_cells("A8:C8")

    ws["A9"] = "Account Start ($)"
    ws["B9"] = 50000
    ws["C9"] = "Balanța contului. Schimbă în 100000 pentru un cont de 100k."

    ws["A10"] = "Lot size per semnal (US30)"
    ws["B10"] = 0.08
    ws["C10"] = (
        "Câte loturi US30 intri pe fiecare semnal (TradeLocker). "
        "Pe 100k cu același risc %: 0.16."
    )

    ws["A11"] = "$ per 1% preț, la 1.0 lot"
    ws["B11"] = "=B24*B25/100"
    ws["C11"] = (
        "Auto = ($/punct la 1 lot, B24) × (Preț reper, B25 / 100). "
        "Înmulțit cu Lot size (B10) dă riscul real per 1% — vezi B27."
    )

    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 Account Start (B9) și %"

    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 Account Start (B9) și %"

    # Escape hatch performanță: activează/dezactivează filtrul Prima per Indicator
    ws["A17"] = "Activează filtru Prima"
    ws["B17"] = "DA"
    ws["C17"] = (
        "DA = adaugi rândurile Prima în window grid. "
        "NU = doar Toate (workbook mai rapid)."
    )
    ws["B17"].fill = INPUT_FILL
    ws["B17"].border = BORDER
    ws["B17"].alignment = CENTER
    dv_prima = DataValidation(
        type="list", formula1='"DA,NU"', allow_blank=False,
    )
    dv_prima.add("B17")
    ws.add_data_validation(dv_prima)

    # ---- Calibrare $/punct dintr-un ordin reper + derivate informative ----
    # Helper universal: dintr-un singur ordin TradeLocker (orice indicator) derivi
    # $/punct, apoi $/1% preț. Pentru alt indicator schimbi cele 4 inputuri reper
    # (B19-B22) + Preț reper curent (B25). Restul se recalculează singur.
    ws["A18"] = "— Calibrare $/punct (dintr-un ordin reper TradeLocker) —"
    ws["A18"].font = Font(name="Calibri", size=10, italic=True, bold=True, color="1F3864")
    ws.merge_cells("A18:C18")

    ws["A19"] = "Preț entry (ordin reper)"
    ws["B19"] = 50680.15
    ws["C19"] = "Prețul de intrare afișat în ticketul TradeLocker (orice ordin reper)."

    ws["A20"] = "Preț SL (ordin reper)"
    ws["B20"] = 50618.24
    ws["C20"] = "Prețul SL din același ticket."

    ws["A21"] = "$ risc afișat (ordin reper)"
    ws["B21"] = 495.24
    ws["C21"] = "Cifra $ pe care TradeLocker o arată la SL pentru ordinul reper."

    ws["A22"] = "Lot size (ordin reper)"
    ws["B22"] = 0.08
    ws["C22"] = "Câte loturi avea ordinul reper (poate diferi de lotul tău curent, B10)."

    ws["A23"] = "↳ Distanță reper (puncte)"
    ws["B23"] = "=ABS(B19-B20)"
    ws["C23"] = "Auto = |entry − SL|. Aici ⇒ 61.91 puncte."

    ws["A24"] = "↳ $ per punct la 1.0 lot"
    ws["B24"] = "=IF(OR(B23=0,B22=0),0,B21/B23/B22)"
    ws["C24"] = (
        "Auto = $risc ÷ distanță ÷ lot reper. Pe US30 ⇒ $100/punct la 1 lot. "
        "Asta intră în B11. Pe alt indicator se recalibrează singur."
    )

    ws["A25"] = "Preț reper (curent)"
    ws["B25"] = 50700
    ws["C25"] = (
        "Nivelul curent al indicatorului; convertește % mișcare preț în puncte/$. "
        "Pentru alt indicator pui prețul lui."
    )

    ws["A26"] = "↳ $ per punct (la lotul tău)"
    ws["B26"] = "=B10*B24"
    ws["C26"] = "Auto = Lot size (B10) × $/punct la 1 lot (B24). La 0.08 loturi ⇒ $8/punct."

    ws["A27"] = "↳ $ risc la 1% mișcare preț"
    ws["B27"] = "=B10*B11"
    ws["C27"] = (
        "Auto — INIMA calculului. $ pe trade = R × SL% × această valoare. "
        "La 0.08 loturi US30 ⇒ ~$4,056."
    )

    for r in (9, 10, 12, 14, 19, 20, 21, 22, 25):  # inputuri galbene
        ws.cell(row=r, column=2).fill = INPUT_FILL
        ws.cell(row=r, column=2).border = BORDER
    for r in (11, 13, 15, 23, 24, 26, 27):  # 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.00"
    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"
    ws["B19"].number_format = "#,##0.00"
    ws["B20"].number_format = "#,##0.00"
    ws["B21"].number_format = '"$"#,##0.00'
    ws["B22"].number_format = "0.00"
    ws["B23"].number_format = "#,##0.00"
    ws["B24"].number_format = '"$"#,##0.00'
    ws["B25"].number_format = "#,##0"
    ws["B26"].number_format = '"$"#,##0.00'
    ws["B27"].number_format = '"$"#,##0.00'

    # ---- Utilitar: calcul preț SL pentru TradeLocker (pas cu pas) ----
    # Forward helper: din SL% (TradeStation) + preț intrare (TradeLocker) + direcție
    # → prețul exact la care pui SL în TradeLocker. Celule intermediare vizibile.
    ws["A29"] = "— Utilitar: ce prețuri SL/TP pun în TradeLocker —"
    ws["A29"].font = Font(name="Calibri", size=10, italic=True, bold=True, color="1F3864")
    ws.merge_cells("A29:C29")

    ws["A30"] = "SL % (din TradeStation)"
    ws["B30"] = 0.08
    ws["C30"] = "Procentul SL al semnalului (% mișcare de preț), ex: 0.08."

    ws["A31"] = "Preț intrare (TradeLocker)"
    ws["B31"] = 50649.50
    ws["C31"] = "Prețul tău de intrare US30 (nu prețul reper de la B25)."

    ws["A32"] = "Direcție"
    ws["B32"] = "Sell"
    ws["C32"] = "Sell ⇒ SL DEASUPRA intrării. Buy ⇒ SL DEDESUBT."

    ws["A33"] = "↳ Distanță SL (puncte)"
    ws["B33"] = "=B31*B30/100"
    ws["C33"] = "Auto = Preț intrare × SL% / 100. Ex: 50649.50 × 0.08/100 ⇒ ~40.5 puncte."

    ws["A34"] = "↳ PREȚ SL de setat în TradeLocker"
    ws["B34"] = '=IF(B32="Sell",B31+B33,B31-B33)'
    ws["C34"] = "Sell: intrare + distanță. Buy: intrare − distanță. ASTA pui în TradeLocker."

    ws["A35"] = "↳ $ risc la acest SL"
    ws["B35"] = "=B33*B26"
    ws["C35"] = (
        "Auto = Distanță (puncte) × $/punct la lotul tău (B26). "
        "Verificare: ar trebui să se potrivească cu cifra $ din ticketul TradeLocker."
    )

    for r in (30, 31, 32):  # inputuri galbene
        ws.cell(row=r, column=2).fill = INPUT_FILL
        ws.cell(row=r, column=2).border = BORDER
    for r in (33, 34, 35):  # derived blue
        ws.cell(row=r, column=2).fill = DERIVED_FILL
        ws.cell(row=r, column=2).border = BORDER
    # Output principal evidențiat
    ws["A34"].font = Font(name="Calibri", size=11, bold=True, color="1F3864")
    ws["B34"].font = Font(name="Calibri", size=11, bold=True)
    ws["B32"].alignment = CENTER

    ws["B30"].number_format = '0.000"%"'
    ws["B31"].number_format = "#,##0.00"
    ws["B33"].number_format = "#,##0.00"
    ws["B34"].number_format = "#,##0.00"
    ws["B35"].number_format = '"$"#,##0.00'

    # --- TP0/TP1/TP2: aceeași logică, dar în direcția profitului (opus SL) ---
    ws["A36"] = "TP0 % (din TradeStation)"
    ws["B36"] = 0.03
    ws["C36"] = "Procentul TP0 al semnalului (% mișcare de preț)."

    ws["A37"] = "TP1 % (din TradeStation)"
    ws["B37"] = 0.06
    ws["C37"] = "Procentul TP1 al semnalului."

    ws["A38"] = "TP2 % (din TradeStation)"
    ws["B38"] = 0.08
    ws["C38"] = "Procentul TP2 al semnalului."

    ws["A39"] = "↳ PREȚ TP0 de setat"
    ws["B39"] = '=IF(B32="Sell",B31-B31*B36/100,B31+B31*B36/100)'
    ws["C39"] = (
        "Distanță = intrare × TP0% / 100. Sell: intrare − distanță. "
        "Buy: intrare + distanță (TP-ul e opus SL-ului)."
    )

    ws["A40"] = "↳ PREȚ TP1 de setat"
    ws["B40"] = '=IF(B32="Sell",B31-B31*B37/100,B31+B31*B37/100)'
    ws["C40"] = "La fel, cu TP1%."

    ws["A41"] = "↳ PREȚ TP2 de setat"
    ws["B41"] = '=IF(B32="Sell",B31-B31*B38/100,B31+B31*B38/100)'
    ws["C41"] = "La fel, cu TP2%."

    for r in (36, 37, 38):  # inputuri galbene
        ws.cell(row=r, column=2).fill = INPUT_FILL
        ws.cell(row=r, column=2).border = BORDER
        ws.cell(row=r, column=2).number_format = '0.000"%"'
    for r in (39, 40, 41):  # output evidențiat (albastru, bold)
        ws.cell(row=r, column=2).fill = DERIVED_FILL
        ws.cell(row=r, column=2).border = BORDER
        ws.cell(row=r, column=2).number_format = "#,##0.00"
        ws.cell(row=r, column=2).font = Font(name="Calibri", size=11, bold=True)
        ws.cell(row=r, column=1).font = Font(
            name="Calibri", size=11, bold=True, color="1F3864"
        )

    dv_dir = DataValidation(type="list", formula1='"Buy,Sell"', allow_blank=False)
    dv_dir.add("B32")
    ws.add_data_validation(dv_dir)

    # 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": 30, "B": 14, "C": 40, "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_prima_in_window(r: int, win_idx: int) -> str:
    """1 dacă trade-ul este prima cronologic pe (Data, Indicator) ÎN fereastra cu indexul win_idx.

    Guard suplimentar pe Config!B17 (escape hatch performanță): dacă utilizatorul
    setează "NU", toate PrimaWin_* devin 0 instant, fără recalcularea COUNTIFS.
    Outcome inclus în COUNTIFS ca să nu blocheze rândurile parțial completate.
    """
    _, start_t, end_t = TRADABLE_WINDOWS[win_idx]
    start_s = f"TIME({start_t.hour},{start_t.minute},0)"
    end_s = f"TIME({end_t.hour},{end_t.minute},0)"
    d = f'{COL["Data"]}{r}'
    ind = f'{COL["Indicator"]}{r}'
    t = f'{COL["Ora RO"]}{r}'
    o = f'{COL["Outcome"]}{r}'
    data_rng = _range("Data")
    ind_rng = _range("Indicator")
    ora_rng = _range("Ora RO")
    outcome_rng = _range("Outcome")
    return (
        f'=IF(Config!$B$17<>"DA",0,'
        f'IF(OR({d}="",{t}="",{ind}="",{o}=""),0,'
        f'IF(AND({t}>={start_s},{t}<{end_s}),'
        f'IF(COUNTIFS({data_rng},{d},{ind_rng},{ind},'
        f'{ora_rng},">="&{start_s},{ora_rng},"<"&{end_s},'
        f'{ora_rng},"<"&{t},'
        f'{outcome_rng},"<>"'
        f')=0,1,0),0)))'
    )


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}"
            )

        # Coloanele PrimaWin_<idx> — 1 dacă trade-ul e prima per (Data, Indicator) în fereastră
        for win_idx in range(len(TRADABLE_WINDOWS)):
            ws[f'{COL[f"PrimaWin_{win_idx}"]}{r}'] = _f_prima_in_window(r, win_idx)

    # Sample rows
    SAMPLE_ROWS = [
        # (data, ora, strat, ind, tf, dir, sl, tp0, tp1, tp2, outcome, notes)
        (date(2026, 5, 13), time(17, 33), "M2D", "DIA", "1min", "Sell", 0.30, 0.10, 0.15, 0.30, "TP1", "Prima DIA în 16:30-18:00"),
        (date(2026, 5, 13), time(17, 50), "M2D", "DIA", "1min", "Buy",  0.25, 0.10, 0.15, 0.25, "SL",  "DIA a doua oară — NU Prima în 16:30-18:00, dar Prima în 17:30-19:00"),
        (date(2026, 5, 13), time(17, 34), "M2D", "SPY", "1min", "Sell", 0.20, 0.08, 0.12, 0.20, "TP1", "SPY — indicator diferit, Prima independent"),
        (date(2026, 5, 13), time(17, 40), "M2D", "DIA", "1min", "Sell", 0.20, 0.08, 0.12, 0.20, "",    "Outcome gol — test D1: NU blochează Prima pentru row 2/3"),
        (date(2026, 5, 14), time(22, 15), "M2D", "DIA", "1min", "Sell", 0.30, 0.10, 0.15, 0.30, "TP0", "Zi diferită — Prima reset per (Data, Indicator, Fereastră)"),
    ]
    for offset, sample in enumerate(SAMPLE_ROWS):
        r = 2 + offset
        data_v, ora, strat_v, ind, tf, dirn, sl, tp0, tp1, tp2, outcome, notes = sample
        ws[f"B{r}"] = data_v
        ws[f"C{r}"] = ora
        ws[f'{COL["Strategie"]}{r}'] = strat_v
        ws[f'{COL["Indicator"]}{r}'] = ind
        ws[f'{COL["TF"]}{r}'] = tf
        ws[f'{COL["Direcție"]}{r}'] = dirn
        ws[f'{COL["SL %"]}{r}'] = sl
        ws[f'{COL["TP0 %"]}{r}'] = tp0
        ws[f'{COL["TP1 %"]}{r}'] = tp1
        ws[f'{COL["TP2 %"]}{r}'] = tp2
        ws[f'{COL["Outcome"]}{r}'] = outcome
        ws[f'{COL["Notes"]}{r}'] = notes

    # 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 win_idx in range(len(TRADABLE_WINDOWS)):
        helper_letters.add(COL[f"PrimaWin_{win_idx}"])

    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

    # Helper-ele PrimaWin_<idx> — ~40 coloane la sfârșit, ascunse în outline
    for win_idx in range(len(TRADABLE_WINDOWS)):
        cl = COL[f"PrimaWin_{win_idx}"]
        ws.column_dimensions[cl].width = 3
        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% × $ risc la 1% preț (Config B27).\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% × $ risc la 1% preț (Config B27).\n"
        "La 0.08 loturi US30: SL=0.30% × ~$4,056 ≈ −$1,217. Dacă e mult 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% × $ risc la 1% preț (Config B27).\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."
    ),
    "$ risc la 1% preț ($)": (
        "Câți $ riști pe poziția ta reală la 1% mișcare de preț (Config B27).\n"
        "Lanț: Lot size (B10) × $/punct la 1 lot (B24) = $/punct; × Preț reper (B25)/100 = $ per 1%.\n"
        "$/punct la 1 lot (B24) e calibrat dintr-un ordin reper TradeLocker — merge pe orice indicator.\n"
        "La 0.08 loturi US30 ⇒ ~$4,056. Pe 100k cu lot 0.16 se dublează (~$8,112)."
    ),
    "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 = Account Start (Config B9) + Cumulative P&L Prop.\n"
        "Compar-o cu pragul de stop-out: Account Start − Max Loss Limit $ (Config B15)."
    ),
    "Worst Daily Loss ($)": (
        "Cea mai proastă pierdere cumulativă într-o zi calendaristică.\n"
        "Dacă e mai mică decât −Daily Loss Limit $ (Config B13), ai depășit limita zilnică — 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 $ (Config B13, auto din Account × %).\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ă > Max Loss Limit $ (Config B15, auto din Account × %), ai depășit limita — cont mort."
    ),
    "Max Loss Status": (
        "PASS dacă Max Account Drawdown ≤ Max Loss Limit $ (Config B15).\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

    # 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 = 5 + len(metrics) + 2
    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',
        ),
        (
            "$ risc la 1% preț ($)",
            lambda s: "=Config!$B$27",
            '"$"#,##0.00',
        ),
        (
            "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

    # Column widths — metric table + breakdowns + prop compliance
    for _c, _w in {"A": 24, "B": 15, "C": 15, "D": 15,
                   "E": 15, "F": 15, "G": 60}.items():
        ws.column_dimensions[_c].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

    # Notă: graficele de echitate au fost eliminate (nu sunt folosite). Dashboard-ul
    # rămâne pur tabelar — metrici + breakdown-uri + ferestre + compliance prop.


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


def build_workbook() -> Workbook:
    # backtest.xlsx = doar Config + Trades (fișierul editat zilnic, ușor/rapid).
    # Dashboard-ul trăiește separat în data/Dashboard.xlsx, generat la comandă de
    # scripts/generate_dashboard.py (vezi refresh_dashboard.bat). build_dashboard()
    # rămâne aici și e refolosit de acel script.
    wb = Workbook()
    default = wb.active
    wb.remove(default)
    build_config(wb)
    build_trades(wb)
    wb.active = wb.sheetnames.index("Trades")
    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())
