"""Pydantic data models for the dual-pipeline signal engine.

Defines all input, intermediate, and output models consumed by the heuristic
pipeline, probabilistic pipeline, delta analyzer, exit engine, and output
formatter.  Every model is a Pydantic ``BaseModel`` subclass with field-level
constraints where applicable.
"""

from __future__ import annotations

import uuid
from datetime import datetime
from enum import Enum

from pydantic import BaseModel, Field

# ---------------------------------------------------------------------------
# Market data
# ---------------------------------------------------------------------------


class OHLCVBar(BaseModel):
    """Single OHLCV bar for a timeframe."""

    timestamp: datetime
    open: float
    high: float
    low: float
    close: float
    volume: float


# ---------------------------------------------------------------------------
# Position state (for exit engine)
# ---------------------------------------------------------------------------


class OpenPositionState(BaseModel):
    """Snapshot of an open position for exit evaluation."""

    position_id: str
    ticker: str
    entry_price: float
    current_price: float
    stop_loss: float
    target_1: float
    target_2: float
    trailing_stop: float | None = None
    partial_exit_done: bool = False
    atr: float | None = None


# ---------------------------------------------------------------------------
# Normalized input consumed by both pipelines
# ---------------------------------------------------------------------------


class NormalizedInput(BaseModel):
    """Unified input structure consumed by both pipelines."""

    ticker: str
    evaluated_at: datetime

    # Multi-timeframe OHLCV bars keyed by timeframe label
    bars: dict[str, list[OHLCVBar]]  # {"M30": [...], "H1": [...], ...}

    # Fundamental / macro context
    valuation_score: float | None = None  # [0.0, 1.0]
    earnings_proximity_days: int | None = None
    macro_bias: float = 0.0  # [-1.0, 1.0]

    # Open positions for exit evaluation
    open_positions: list[OpenPositionState] = Field(default_factory=list)

    # Price series helpers (used by probabilistic pipeline)
    closing_prices: list[float] = Field(default_factory=list)
    returns: list[float] = Field(default_factory=list)
    current_price: float | None = None


# ---------------------------------------------------------------------------
# Signal evaluation primitives
# ---------------------------------------------------------------------------


class SignalDirection(str, Enum):
    BULLISH = "bullish"
    BEARISH = "bearish"
    NEUTRAL = "neutral"


class SignalResult(BaseModel):
    """Output from a single signal evaluator on a single timeframe."""

    signal_type: str
    timeframe: str
    strength: float = Field(ge=0.0, le=1.0)
    direction: SignalDirection
    confidence: float = Field(ge=0.0, le=1.0)
    metadata: dict = Field(default_factory=dict)


# ---------------------------------------------------------------------------
# Multi-timeframe confluence
# ---------------------------------------------------------------------------


class ConfluenceSignal(BaseModel):
    """A signal that passed multi-timeframe confluence filtering."""

    signal_type: str
    direction: SignalDirection
    confluence_score: float
    active_timeframes: list[str]
    per_timeframe: dict[str, float]


# ---------------------------------------------------------------------------
# Pipeline verdicts
# ---------------------------------------------------------------------------


class Verdict(str, Enum):
    BUY = "BUY"
    WATCH = "WATCH"
    SKIP = "SKIP"


# ---------------------------------------------------------------------------
# Heuristic pipeline output
# ---------------------------------------------------------------------------


class HeuristicResult(BaseModel):
    """Output from the heuristic (deterministic) pipeline."""

    verdict: Verdict
    confidence: float = Field(ge=0.0, le=1.0)
    s_total: float
    s_company: float
    s_macro: float
    s_competitive: float
    signal_weights: list[dict] = Field(default_factory=list)
    reasoning: list[str] = Field(default_factory=list)


# ---------------------------------------------------------------------------
# Probabilistic pipeline output
# ---------------------------------------------------------------------------


class LikelihoodRatio(BaseModel):
    """A single signal's likelihood ratio for Bayesian updating."""

    signal_type: str
    cluster: str
    lr: float
    log_lr: float
    penalized_log_lr: float
    hit_rate: float
    strength: float


class ProbabilisticResult(BaseModel):
    """Output from the probabilistic (Bayesian) pipeline."""

    verdict: Verdict
    p_up: float = Field(ge=0.0, le=1.0)
    entropy: float = Field(ge=0.0, le=1.0)
    ev_r: float
    prior: float
    posterior: float
    likelihood_ratios: list[LikelihoodRatio] = Field(default_factory=list)
    regime: str
    reasoning: list[str] = Field(default_factory=list)


# ---------------------------------------------------------------------------
# Delta analyzer output
# ---------------------------------------------------------------------------


class DeltaResult(BaseModel):
    """Output from the delta analyzer comparing both pipelines."""

    agreement: bool
    confidence_delta: float
    heuristic_verdict: str
    probabilistic_verdict: str
    disagreement_reasons: list[str] = Field(default_factory=list)
    rolling_agreement_rate: float | None = None


# ---------------------------------------------------------------------------
# Exit engine
# ---------------------------------------------------------------------------


class ExitType(str, Enum):
    EXIT_HALF = "EXIT_HALF"
    EXIT_FULL = "EXIT_FULL"


class ExitSignal(BaseModel):
    """An exit signal for an open position."""

    position_id: str
    ticker: str
    exit_type: ExitType
    reason: str
    price: float


# ---------------------------------------------------------------------------
# Trade plan
# ---------------------------------------------------------------------------


class TradePlan(BaseModel):
    """Optional trade plan attached to a BUY signal."""

    entry_price: float
    stop_loss: float
    target_1: float
    target_2: float
    position_size_pct: float = Field(ge=0.0, le=1.0)
    max_loss_pct: float = Field(ge=0.0, le=1.0)
    dual_confirmed: bool = False
    probabilistic_only: bool = False


# ---------------------------------------------------------------------------
# Structured output contract
# ---------------------------------------------------------------------------


class SignalOutput(BaseModel):
    """The structured output contract consumed by the trading engine and audit systems."""

    output_id: str = Field(default_factory=lambda: str(uuid.uuid4()))
    ticker: str
    timestamp: datetime
    price: float

    # Heuristic pipeline section
    heuristic_verdict: str
    heuristic_confidence: float
    heuristic_s_total: float

    # Probabilistic pipeline section
    probabilistic_verdict: str
    probabilistic_p_up: float
    probabilistic_entropy: float
    probabilistic_ev_r: float

    # Delta analysis section
    delta_agreement: bool
    delta_confidence_delta: float
    delta_reasons: list[str] = Field(default_factory=list)

    # Optional trade plan and exit signals
    trade_plan: TradePlan | None = None
    exit_signals: list[ExitSignal] = Field(default_factory=list)

    # Detail payloads for audit / dashboard
    heuristic_detail: dict = Field(default_factory=dict)
    probabilistic_detail: dict = Field(default_factory=dict)

    # Pipeline mode metadata
    pipeline_mode: str = "dual_pipeline"
    shadow_mode: bool = False
