"""Pydantic models for micro-scanner.

These are standalone — micro-scanner never imports from src/.
Schema must stay aligned with migrations/002_micro_trading_up.sql.
"""

from __future__ import annotations

from datetime import datetime, timezone
from decimal import Decimal
from typing import Literal
from uuid import UUID, uuid4

from pydantic import BaseModel, Field


class MicroSignal(BaseModel):
    signal_id: UUID = Field(default_factory=uuid4)
    symbol: str
    direction: Literal["buy"] = "buy"
    strategy_tier: Literal["micro"] = "micro"
    confidence: float = Field(ge=0.0, le=1.0)
    composite_score: float = Field(ge=0.0, le=1.0)
    expected_return_pct: float = Field(ge=0.0)
    suggested_quantity_pct: float = Field(ge=0.0, le=100.0)
    max_hold_seconds: int = 1800
    model_version: str = "momentum-hybrid-v1"
    emitted_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
    # Shadow mean-reversion indicators (zero-cost data collection)
    rsi7_at_entry: float | None = None
    bb_position_at_entry: float | None = None
    volume_ratio_at_entry: float | None = None
    mean_reversion_triggered: bool = False


class MicroPosition(BaseModel):
    id: str  # UUID as string
    signal_id: str
    symbol: str
    mode: Literal["draft", "live"]
    status: Literal["open", "closed", "cancelled", "partial_fill"]
    closed_by: str | None = None  # CAS column for double-exit prevention
    entry_price: Decimal
    exit_price: Decimal | None = None
    qty: Decimal
    filled_qty: Decimal
    avg_fill_price: Decimal
    sl_order_id: str | None = None
    trailing_stop_active: bool = False
    trailing_stop_distance_pct: Decimal | None = None
    peak_price: Decimal | None = None
    stop_price: Decimal
    exit_reason: str | None = None
    pnl_pct: float | None = None
    opened_at: datetime
    closed_at: datetime | None = None


class CandidateScore(BaseModel):
    symbol: str
    score: float = Field(ge=0.0, le=1.0)
    scored_at: datetime
    data_freshness: dict  # JSON with _v:1 schema version
    is_valid: bool
    status: Literal["active", "dormant", "blacklisted", "deferred", "position_open"]


class ExitDecision(BaseModel):
    action: Literal[
        "hold",
        "sell_partial_tp",
        "sell_trailing",
        "sell_time_exit",
        "sell_dead_man",
        "sell_spread_exit",
        "sell_kill_switch",
    ]
    reason: str
    target_price: Decimal | None = None
    quantity: Decimal | None = None  # None = full position
