"""Domain models for TGTC strategy."""
from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any


@dataclass
class TGTCCollectionParams:
    start_et: str = "09:29:30"
    end_et: str = "10:00:00"
    interval_seconds: int = 20


@dataclass
class TGTCFilterParams:
    min_price: float = 5.0
    min_market_cap: float = 2_000_000_000.0
    min_30m_dollar_volume: float = 10_000_000.0
    min_avg_dollar_volume_20d: float = 50_000_000.0  # backtest surrogate for market_cap
    min_day_change_at_10: float = 0.04
    max_day_change_at_10: float = 0.35
    must_be_above_vwap: bool = True
    max_pullback_from_hod: float = 0.35
    min_qqq_pct_change_at_10: float | None = None  # 시장 레짐 floor (None=비활성)


@dataclass
class TGTCScoreWeights:
    rank_persistence: float = 0.30
    rank_velocity: float = 0.20
    price_structure: float = 0.25
    volume_quality: float = 0.15
    relative_strength_vs_qqq: float = 0.10


@dataclass
class TGTCEntryParams:
    type: str = "vwap_pullback_reclaim"
    min_first3bar_gain_pct: float = 0.04   # 09:30~09:45 (3×5m bars) min cumulative gain
    pullback_volume_ratio_max: float = 0.70
    stop_atr_multiple: float = 1.5
    max_candidates_to_scan: int | None = None  # None이면 기존 max_positions*3 유지
    reclaim_volume_ratio_min: float | None = None  # None=비활성. reclaim봉 volume >= pullback봉 volume * ratio
    no_entry_after_et: str | None = None  # "12:00" 형식. None=비활성. 이 시각 이후 신규 진입 금지
    no_entry_before_et: str | None = None  # "11:00" 형식. None=비활성. 이 시각 이전 신규 진입 금지
    fade_min_gain_pct: float | None = None  # fade_short: first-3-bar gain threshold (default 0.08)


@dataclass
class TGTCExitParams:
    partial_at_1r: float = 0.33
    stop_to_be_after_1r: bool = True
    min_trend_health_score: int = 4
    eod_exit_et: str = "15:55"
    vwap_break_bars_to_exit: int = 2
    # Stop exit mode: "conservative" (default, uses min(stop, bar_open) to model gap fills)
    # "optimistic" = assume stop always fills exactly at stop price (upper bound)
    # "moderate" = exit at stop * (1 - stop_slippage_bps/10000) — middle assumption
    stop_exit_mode: str = "conservative"
    stop_slippage_bps: float = 10.0
    take_profit_pct: float | None = None          # e.g., 0.015 → exit at +1.5% from entry
    disable_partial_at_1r: bool = False
    force_exit_after_minutes: int | None = None   # time-stop: exit after N minutes in trade
    partial_levels: list | None = None            # multi-level partial scale-out
    tp_requires_no_stop_touch: bool = False       # if True, only fire quick_tp when bar.low > current_stop (avoids same-bar collision artifact)


@dataclass
class TGTCRiskParams:
    risk_per_trade_pct: float = 0.30
    max_positions: int = 3
    daily_loss_limit_pct: float = 1.0
    initial_equity: float = 10000.0


@dataclass
class TGTCStrategyParams:
    collection: TGTCCollectionParams = field(default_factory=TGTCCollectionParams)
    filters: TGTCFilterParams = field(default_factory=TGTCFilterParams)
    score_weights: TGTCScoreWeights = field(default_factory=TGTCScoreWeights)
    entry: TGTCEntryParams = field(default_factory=TGTCEntryParams)
    exit: TGTCExitParams = field(default_factory=TGTCExitParams)
    risk: TGTCRiskParams = field(default_factory=TGTCRiskParams)


@dataclass
class TGTCConfig:
    """Top-level config loaded from YAML."""
    strategy_mode: str = "tgtc"
    tgtc_strategy: TGTCStrategyParams = field(default_factory=TGTCStrategyParams)
    # universe for backtest pre-screening
    universe: str = "broad"
    meta: dict[str, Any] = field(default_factory=dict)


def load_tgtc_config(path: str) -> TGTCConfig:
    """Load a TGTC yaml config. Applies _meta, strategy_mode, tgtc_strategy fields."""
    import yaml
    from pathlib import Path

    raw: dict[str, Any] = yaml.safe_load(Path(path).read_text()) or {}

    meta = raw.get("_meta", {})
    tgtc_raw = raw.get("tgtc_strategy", {})

    col_raw = tgtc_raw.get("collection", {})
    flt_raw = tgtc_raw.get("filters", {})
    sw_raw = tgtc_raw.get("score_weights", {})
    ent_raw = tgtc_raw.get("entry", {})
    ex_raw = tgtc_raw.get("exit", {})
    rsk_raw = tgtc_raw.get("risk", {})

    col = TGTCCollectionParams(**{k: v for k, v in col_raw.items() if k in TGTCCollectionParams.__dataclass_fields__})
    flt = TGTCFilterParams(**{k: v for k, v in flt_raw.items() if k in TGTCFilterParams.__dataclass_fields__})
    sw = TGTCScoreWeights(**{k: v for k, v in sw_raw.items() if k in TGTCScoreWeights.__dataclass_fields__})
    ent = TGTCEntryParams(**{k: v for k, v in ent_raw.items() if k in TGTCEntryParams.__dataclass_fields__})
    ex = TGTCExitParams(**{k: v for k, v in ex_raw.items() if k in TGTCExitParams.__dataclass_fields__})
    rsk = TGTCRiskParams(**{k: v for k, v in rsk_raw.items() if k in TGTCRiskParams.__dataclass_fields__})

    params = TGTCStrategyParams(
        collection=col, filters=flt, score_weights=sw, entry=ent, exit=ex, risk=rsk
    )
    return TGTCConfig(
        strategy_mode=raw.get("strategy_mode", "tgtc"),
        tgtc_strategy=params,
        universe=raw.get("universe", "broad"),
        meta=meta,
    )
