"""SQL operational repository — implements OperationalRepository over SQLAlchemy (Postgres or SQLite).
Maps the domain ResearchRun <-> the ORM row; the domain never sees SQLAlchemy."""
from __future__ import annotations

from sqlalchemy import create_engine, select
from sqlalchemy.orm import Session

from fxhnt.adapters.persistence.sql_models import Base, ResearchRunRow
from fxhnt.domain.models import AssetClass, BacktestStats, Market, ResearchRun, StrategySpec, Verdict


class SqlOperationalRepository:
    def __init__(self, dsn: str) -> None:
        self._engine = create_engine(dsn, future=True)
        Base.metadata.create_all(self._engine)

    @staticmethod
    def _to_row(run: ResearchRun) -> ResearchRunRow:
        return ResearchRunRow(
            run_id=run.run_id, symbol=run.market.symbol, asset_class=run.market.asset_class.value,
            venue=run.market.venue, currency=run.market.currency, strategy_kind=run.spec.kind,
            params=dict(run.spec.params), cagr=run.stats.cagr, ann_vol=run.stats.ann_vol,
            sharpe=run.stats.sharpe, max_drawdown=run.stats.max_drawdown, n_obs=run.stats.n_obs,
            passed=run.verdict.passed, dsr=run.verdict.dsr, is_sharpe=run.verdict.is_sharpe,
            oos_sharpe=run.verdict.oos_sharpe, n_trials=run.n_trials, reasons=list(run.verdict.reasons),
            created_at=run.created_at,
        )

    @staticmethod
    def _to_domain(row: ResearchRunRow) -> ResearchRun:
        return ResearchRun(
            run_id=row.run_id,
            market=Market(symbol=row.symbol, asset_class=AssetClass(row.asset_class), venue=row.venue, currency=row.currency),
            spec=StrategySpec(kind=row.strategy_kind, params=row.params),
            stats=BacktestStats(cagr=row.cagr, ann_vol=row.ann_vol, sharpe=row.sharpe, max_drawdown=row.max_drawdown, n_obs=row.n_obs),
            verdict=Verdict(passed=row.passed, dsr=row.dsr, is_sharpe=row.is_sharpe, oos_sharpe=row.oos_sharpe, n_trials=row.n_trials, reasons=row.reasons),
            n_trials=row.n_trials, created_at=row.created_at,
        )

    def save_run(self, run: ResearchRun) -> None:
        with Session(self._engine) as s:
            s.merge(self._to_row(run))
            s.commit()

    def get_run(self, run_id: str) -> ResearchRun | None:
        with Session(self._engine) as s:
            row = s.get(ResearchRunRow, run_id)
            return self._to_domain(row) if row else None

    def list_runs(self, *, passed_only: bool = False) -> list[ResearchRun]:
        stmt = select(ResearchRunRow).order_by(ResearchRunRow.created_at.desc())
        if passed_only:
            stmt = stmt.where(ResearchRunRow.passed.is_(True))
        with Session(self._engine) as s:
            return [self._to_domain(r) for r in s.scalars(stmt)]
