"""Pydantic schemas for scoring endpoints."""

from __future__ import annotations

from datetime import datetime

from pydantic import BaseModel, Field


class SubScoreResponse(BaseModel):
    """A single sub-score within a dimension breakdown."""

    name: str
    score: float
    weight: float
    raw_value: float | str | None = None
    description: str = ""


class ScoreBreakdownResponse(BaseModel):
    """Breakdown of a dimension score into sub-scores."""

    sub_scores: list[SubScoreResponse]
    formula: str
    unavailable: list[dict[str, str]] = []


class CompositeBreakdownResponse(BaseModel):
    """Breakdown of the composite score showing dimension weights and re-normalization."""

    weights: dict[str, float]
    available_dimensions: list[str]
    missing_dimensions: list[str]
    renormalized_weights: dict[str, float]
    formula: str
    # Sentiment is applied as a signed adjustment on top of the non-sentiment base
    # rather than averaged in.
    base_score: float | None = None
    sentiment_score: float | None = None
    sentiment_adjustment: float | None = None
    max_sentiment_adjustment: float | None = None


class DimensionScoreResponse(BaseModel):
    """A single dimension score."""

    dimension: str
    score: float
    is_stale: bool
    computed_at: datetime | None = None
    breakdown: ScoreBreakdownResponse | None = None


class ScoreResponse(BaseModel):
    """Full score response for a ticker: composite + all dimensions."""

    symbol: str
    composite_score: float | None = None
    composite_stale: bool = False
    weights: dict[str, float] = {}
    dimensions: list[DimensionScoreResponse] = []
    missing_dimensions: list[str] = []
    computed_at: datetime | None = None
    composite_breakdown: CompositeBreakdownResponse | None = None


class WeightUpdateRequest(BaseModel):
    """Request to update dimension weights."""

    weights: dict[str, float] = Field(
        ...,
        description="Dimension name → weight mapping. All weights must be positive.",
    )


class RankingEntry(BaseModel):
    """A single entry in the rankings list."""

    symbol: str
    composite_score: float
    composite_stale: bool = False
    dimensions: list[DimensionScoreResponse] = []


class RankingResponse(BaseModel):
    """Rankings response: tickers sorted by composite score descending."""

    rankings: list[RankingEntry] = []
    weights: dict[str, float] = {}
