from dataclasses import dataclass, field
from datetime import datetime, timezone

from models.signal import Signal, SignalSide, SignalStatus
from services.trade_ledger import record_event


@dataclass
class PaperPosition:
    ticker: str
    side: SignalSide
    quantity: float
    entry_price: float
    opened_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))


class PaperExecutor:
    """Simulateur d'exécution — remplaçable par un connecteur broker plus tard."""

    def __init__(self) -> None:
        self.positions: dict[str, PaperPosition] = {}
        self.history: list[dict] = []

    def open_positions_count(self) -> int:
        return len(self.positions)

    def execute(self, signal: Signal) -> Signal:
        alert = signal.alert
        qty = alert.quantity or 1.0
        price = alert.price or 0.0

        if alert.action in (SignalSide.CLOSE, SignalSide.FLAT):
            existing = self.positions.pop(alert.ticker, None)
            if existing is None:
                signal.status = SignalStatus.FAILED
                signal.execution_note = f"No open paper position for {alert.ticker}"
                return signal

            exit_price = alert.price or existing.entry_price
            if existing.side == SignalSide.BUY:
                pnl = (exit_price - existing.entry_price) * existing.quantity
            else:
                pnl = (existing.entry_price - exit_price) * existing.quantity

            signal.status = SignalStatus.EXECUTED
            signal.execution_note = (
                f"Paper closed {alert.ticker} @ {exit_price} (PnL ${pnl:.2f})"
            )
            self._record(signal, "close", existing.quantity, exit_price, pnl=pnl)
            return signal

        self.positions[alert.ticker] = PaperPosition(
            ticker=alert.ticker,
            side=alert.action,
            quantity=qty,
            entry_price=price,
        )
        signal.status = SignalStatus.EXECUTED
        signal.execution_note = (
            f"Paper {alert.action.value} {qty} {alert.ticker} @ {price or 'market'}"
        )
        self._record(signal, "open", qty, price)
        return signal

    def _record(
        self,
        signal: Signal,
        event: str,
        quantity: float,
        price: float,
        pnl: float | None = None,
    ) -> None:
        row: dict = {
            "signal_id": signal.id,
            "event": event,
            "ticker": signal.alert.ticker,
            "side": signal.alert.action.value,
            "quantity": quantity,
            "price": price,
            "at": datetime.now(timezone.utc).isoformat(),
        }
        if pnl is not None:
            row["pnl"] = pnl
        self.history.append(row)
        record_event(
            event=event,
            ticker=signal.alert.ticker,
            side=signal.alert.action.value,
            quantity=quantity,
            price=price,
            source="paper",
            pnl=pnl,
            signal_id=signal.id,
        )


paper_executor = PaperExecutor()
