from __future__ import annotations
from typing import Literal, Optional
from pydantic import BaseModel, Field


class NodeMeta(BaseModel):
    name: str
    label: str
    default_params: dict
    param_schema: dict


class NodesResponse(BaseModel):
    score_nodes: list[NodeMeta]
    gate_nodes: list[NodeMeta]


class SettingsBody(BaseModel):
    weights: dict[str, float]
    node_params: dict[str, dict] = Field(default_factory=dict)
    gate_params: dict
    top_n: int = 20
    rr_ratio: float = 2.0
    atr_window: int = 14
    atr_stop_mult: float = 2.0


class SettingsResponse(SettingsBody):
    updated_at: str


class RunRequest(BaseModel):
    mode: Literal["preview", "manual_save", "auto"] = "preview"
    asof: Optional[str] = None
    weights: Optional[dict[str, float]] = None
    node_params: Optional[dict[str, dict]] = None
    gate_params: Optional[dict] = None
    top_n: Optional[int] = None


class ResultRow(BaseModel):
    rank: int
    ticker: str
    name: str
    total_score: float
    scores: dict[str, float]
    close: int
    market_cap: int
    entry_price: Optional[int] = None
    stop_price: Optional[int] = None
    target_price: Optional[int] = None
    atr14: Optional[float] = None
    r_pct: Optional[float] = None


class TelegramPayload(BaseModel):
    chat_target: str
    parse_mode: str
    text: str


class RunResponse(BaseModel):
    asof: str
    mode: str
    status: Literal["success", "failed", "skipped_holiday"]
    run_id: Optional[int] = None
    survivors_count: Optional[int] = None
    weights: dict[str, float]
    top_n: int
    results: list[ResultRow] = Field(default_factory=list)
    telegram_payload: Optional[TelegramPayload] = None
    warnings: list[str] = Field(default_factory=list)
    error: Optional[str] = None


class RunSummary(BaseModel):
    id: int
    asof: str
    mode: str
    status: str
    started_at: str
    finished_at: Optional[str] = None
    top_n: int
    survivors_count: Optional[int] = None
    telegram_sent: bool
