"""매매 기법 실행 및 결과 저장."""

from __future__ import annotations

import json
from datetime import datetime
from pathlib import Path
from typing import Any

import pandas as pd

from deepcoin.data.candle_loader import load_candles
from deepcoin.data.intervals import interval_label
from deepcoin.evaluation.gt_align import align_with_ground_truth
from deepcoin.ground_truth.pnl import simulate_gt_pnl
from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueResult
from deepcoin.techniques.legs import legs_to_signal_dicts, signals_to_legs, summarize_legs
from deepcoin.techniques.registry import get_all_techniques, get_technique


def run_technique(
    technique: BaseTechnique,
    df: pd.DataFrame,
    params: TechniqueParams,
    gt_result: dict[str, Any] | None = None,
    tolerance_bars: int = 480,
) -> TechniqueResult:
    """단일 기법을 실행하고 GT 정합을 계산한다.

    Args:
        technique: 실행할 기법.
        df: 캔들 DataFrame.
        params: 실행 파라미터.
        gt_result: Ground Truth JSON dict (정합 평가용).
        tolerance_bars: GT 신호 매칭 허용 봉 수.

    Returns:
        TechniqueResult.
    """
    merged_extra = {**technique.default_extra_params(), **params.extra}
    run_params = TechniqueParams(
        interval_min=params.interval_min,
        lookback_days=params.lookback_days,
        min_leg_pct=params.min_leg_pct,
        initial_cash_krw=params.initial_cash_krw,
        fee_rate=params.fee_rate,
        extra=merged_extra,
    )

    raw_signals = technique.generate_signals(df, run_params)
    legs = signals_to_legs(raw_signals, min_leg_pct=run_params.min_leg_pct)
    summary = summarize_legs(legs)
    pnl = simulate_gt_pnl(
        legs,
        initial_cash_krw=run_params.initial_cash_krw,
        fee_rate=run_params.fee_rate,
    )

    alignment = None
    if gt_result is not None:
        alignment = align_with_ground_truth(
            gt_result=gt_result,
            technique_signals=[s.to_dict() for s in raw_signals],
            technique_legs=legs,
            tolerance_bars=tolerance_bars,
        )

    return TechniqueResult(
        technique_id=technique.technique_id,
        technique_name=technique.technique_name,
        category=technique.category,
        causal=technique.causal,
        description=technique.description,
        params={
            "interval_min": run_params.interval_min,
            "lookback_days": run_params.lookback_days,
            "min_leg_pct": run_params.min_leg_pct,
            "initial_cash_krw": run_params.initial_cash_krw,
            "fee_rate": run_params.fee_rate,
            **merged_extra,
        },
        signals=[s.to_dict() for s in raw_signals],
        legs=legs,
        summary=summary,
        pnl=pnl,
        alignment=alignment,
    )


def run_all_techniques(
    db_path: Path,
    symbol: str,
    params: TechniqueParams,
    gt_result: dict[str, Any] | None = None,
    tolerance_bars: int = 480,
    technique_ids: list[str] | None = None,
) -> list[TechniqueResult]:
    """등록된 기법을 일괄 실행한다."""
    df = load_candles(
        db_path=db_path,
        symbol=symbol,
        interval_min=params.interval_min,
        lookback_days=params.lookback_days,
    )

    techniques = get_all_techniques()
    if technique_ids:
        techniques = [t for t in techniques if t.technique_id in technique_ids]

    results: list[TechniqueResult] = []
    for technique in techniques:
        results.append(
            run_technique(
                technique=technique,
                df=df,
                params=params,
                gt_result=gt_result,
                tolerance_bars=tolerance_bars,
            )
        )
    return results


def save_technique_result(result: TechniqueResult, output_dir: Path) -> Path:
    """기법 결과를 JSON으로 저장한다."""
    output_dir.mkdir(parents=True, exist_ok=True)
    out_path = output_dir / f"{result.technique_id}.json"
    payload = result.to_dict()
    payload["meta"] = {
        "generated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
        "interval_label": interval_label(int(result.params["interval_min"])),
    }
    with out_path.open("w", encoding="utf-8") as fp:
        json.dump(payload, fp, ensure_ascii=False, indent=2)
    return out_path


def load_ground_truth(gt_path: Path) -> dict[str, Any]:
    """Ground Truth JSON을 로드한다."""
    with gt_path.open(encoding="utf-8") as fp:
        return json.load(fp)
