from config import settings
from models.signal import (
    ApproveSignalRequest,
    RejectSignalRequest,
    Signal,
    SignalStatus,
    TradingViewAlert,
)
from services.paper_executor import paper_executor
from services.risk_manager import apply_quantity_defaults, validate_signal


class SignalQueue:
    def __init__(self) -> None:
        self._signals: dict[str, Signal] = {}

    def ingest(self, alert: TradingViewAlert) -> Signal:
        signal = Signal(alert=alert)

        if settings.execution_mode == "auto":
            return self._try_auto_execute(signal)

        self._signals[signal.id] = signal
        return signal

    def list_signals(self, status: SignalStatus | None = None) -> list[Signal]:
        items = list(self._signals.values())
        if status is not None:
            items = [s for s in items if s.status == status]
        return sorted(items, key=lambda s: s.received_at, reverse=True)

    def get(self, signal_id: str) -> Signal | None:
        return self._signals.get(signal_id)

    def approve(self, signal_id: str, body: ApproveSignalRequest) -> Signal:
        signal = self._require_pending(signal_id)
        if body.quantity is not None:
            signal.alert.quantity = body.quantity

        apply_quantity_defaults(signal)
        decision = validate_signal(signal, paper_executor.open_positions_count())
        if not decision.allowed:
            signal.status = SignalStatus.REJECTED
            signal.rejection_reason = decision.reason
            return signal

        if body.note:
            signal.execution_note = body.note

        return paper_executor.execute(signal)

    def reject(self, signal_id: str, body: RejectSignalRequest) -> Signal:
        signal = self._require_pending(signal_id)
        signal.status = SignalStatus.REJECTED
        signal.rejection_reason = body.reason
        return signal

    def _require_pending(self, signal_id: str) -> Signal:
        signal = self._signals.get(signal_id)
        if signal is None:
            raise KeyError(f"Signal not found: {signal_id}")
        if signal.status != SignalStatus.PENDING:
            raise ValueError(f"Signal {signal_id} is not pending ({signal.status})")
        return signal

    def _try_auto_execute(self, signal: Signal) -> Signal:
        apply_quantity_defaults(signal)
        decision = validate_signal(signal, paper_executor.open_positions_count())
        if not decision.allowed:
            signal.status = SignalStatus.REJECTED
            signal.rejection_reason = decision.reason
        else:
            signal = paper_executor.execute(signal)

        self._signals[signal.id] = signal
        return signal


signal_queue = SignalQueue()
