"""
Data models for AltSportsData API

All models are generated from the official OpenAPI specification at:
https://api.dev.altsportsdata.com/public/docs/swagger
"""

from typing import Optional, List, Any
from enum import Enum
from pydantic import BaseModel, Field, ConfigDict


# ── Enums ──────────────────────────────────────────────────────────────────────


class SportType(str, Enum):
    """Supported sport types"""
    WSL = "wsl"
    SLS = "sls"
    NRX = "nrx"
    SPR = "spr"
    MASL = "masl"
    PBR = "pbr"
    BKFC = "bkfc"
    F1 = "f1"
    MOTOGP = "motogp"
    MOTOCRS = "motocrs"
    FDRIFT = "fdrift"
    MXGP = "mxgp"
    JAIALAI = "jaialai"
    GSOC = "gsoc"
    MOTOAMERICA = "motoamerica"
    BYB = "byb"
    POWERSLAP = "powerslap"
    USAC = "usac"
    NHRA = "nhra"
    CDC = "cdc"
    SPRMTCRS = "sprmtcrs"
    HLRS = "hlrs"
    XGAME = "xgame"
    WORLDOUTLAWS = "worldoutlaws"
    DGPT = "dgpt"
    ATHLETESUNLIMITED = "athletesunlimited"
    LUX = "lux"
    RAF = "raf"
    NLL = "nll"
    MLTT = "mltt"
    SPECTATION = "spectation"


class OddType(str, Enum):
    """Types of odds available for events"""
    FASTEST_LAP = "fastestLap"
    EVENT_WINNER = "eventWinner"
    SECOND_PLACE = "secondPlace"
    HOLE_SHOT = "holeShot"
    HEAT_WINNER = "heatWinner"
    OVER_UNDER = "overUnder"
    MULTI_OVER_UNDER = "multiOverUnder"
    HEAD_TO_HEAD = "headToHead"
    SHOWS = "shows"
    PODIUMS = "podiums"
    PROP_BETS = "propBets"
    DREAM_TEAM = "dreamTeam"
    EVENT_EXACTA = "eventExacta"
    HEAT_EXACTA = "heatExacta"
    RACE_TOP_10 = "raceTop10"
    RACE_TOP_5 = "raceTop5"
    RACE_TOP_3 = "raceTop3"


class EventStatus(str, Enum):
    """Event status values"""
    LIVE = "LIVE"
    UPCOMING = "UPCOMING"
    NEXT = "NEXT"
    IN_WINDOW = "IN_WINDOW"
    COMPLETED = "COMPLETED"
    CANCELLED = "CANCELLED"
    POSTPONED = "POSTPONED"


class MarketStatus(str, Enum):
    """Market/odds status"""
    OPEN = "OPEN"
    VOID = "VOID"
    CLOSED = "CLOSED"
    SUSPENDED = "SUSPENDED"
    COMPLETED = "COMPLETED"


class Settlement(str, Enum):
    """Settlement outcome"""
    WIN = "WIN"
    LOSE = "LOSE"
    VOID = "VOID"
    NOT_SET = "NOT_SET"


class PropSettlement(str, Enum):
    """Prop bet settlement outcome"""
    OPEN = "OPEN"
    PAYOUT = "PAYOUT"
    DRAW = "DRAW"
    VOID = "VOID"


class LapStatus(str, Enum):
    """Lap status for racing events"""
    ACT = "ACT"
    DNF = "DNF"
    DNS = "DNS"
    BYE = "BYE"
    DSQ = "DSQ"


class FutureType(str, Enum):
    """Types of futures odds"""
    WINNER = "winner"
    TOP_2 = "top2"
    TOP_3 = "top3"
    TOP_5 = "top5"
    TOP_10 = "top10"
    MAKE_CUT = "makeCut"
    MAKE_PLAY_ODDS = "makePlayOdds"


class ExactasType(int, Enum):
    """Exactas combination type"""
    TWO = 2
    THREE = 3
    FOUR = 4


class OverUnderSubMarketType(str, Enum):
    """Over/under sub-market types"""
    FINISHING_POSITION = "finishingPosition"
    POINTS = "points"
    GAP_TO_LEADERS = "gapToLeaders"


class SortOrder(str, Enum):
    """Sort order for results"""
    DESC = "DESC"
    ASC = "ASC"


# ── Core Models ────────────────────────────────────────────────────────────────


class Team(BaseModel):
    """Team information"""
    model_config = ConfigDict(extra="allow")

    id: str
    referenceId: Optional[float] = None
    name: str


class Athlete(BaseModel):
    """Athlete information"""
    model_config = ConfigDict(extra="allow")

    id: str
    referenceId: Optional[float] = None
    firstName: str
    middleName: Optional[str] = None
    lastName: str
    nationality: Optional[str] = None
    stance: Optional[str] = None
    hometown: Optional[str] = None
    height: Optional[str] = None
    weight: Optional[str] = None
    gender: Optional[str] = None
    weightClass: Optional[str] = None
    dob: Optional[str] = None
    providerId: Optional[str] = None
    team: Optional[Team] = None


# ── Score / Heat Models ────────────────────────────────────────────────────────


class RoundScore(BaseModel):
    """Round/heat score for an athlete"""
    model_config = ConfigDict(extra="allow")

    id: str
    referenceId: Optional[float] = None
    heatStatus: Optional[str] = None
    eventId: Optional[str] = None
    numericEventId: Optional[float] = None
    heatId: Optional[str] = None
    athleteRoundSeed: Optional[float] = None
    athleteRoundRank: Optional[float] = None
    athleteHeatScore: Optional[float] = None
    athleteLapTime: Optional[float] = None
    athletePenaltyTime: Optional[float] = None
    athleteJokerLapTime: Optional[float] = None
    athleteJokerLap: Optional[float] = None
    athleteLapStatus: Optional[LapStatus] = None
    totalLaps: Optional[float] = None
    roundScore: Optional[float] = None
    lineScore1: Optional[float] = None
    lineScore2: Optional[float] = None
    trick1Score: Optional[float] = None
    trick2Score: Optional[float] = None
    trick3Score: Optional[float] = None
    trick4Score: Optional[float] = None
    trick5Score: Optional[float] = None
    trick6Score: Optional[float] = None
    athlete: Optional[Athlete] = None
    updatedAt: Optional[str] = None


# ── Event Models ───────────────────────────────────────────────────────────────


class ScoreDetail(BaseModel):
    """Score detail for Jai Alai sets"""
    model_config = ConfigDict(extra="allow")

    id: str
    label: str
    score: float


class SetDetail(BaseModel):
    """Set detail for Jai Alai matches"""
    model_config = ConfigDict(extra="allow")

    id: str
    startDate: Optional[str] = None
    endDate: Optional[str] = None
    roundStatus: Optional[str] = None
    scores: List[ScoreDetail] = Field(default_factory=list)
    name: Optional[str] = None


class TeamDetail(BaseModel):
    """Team detail for Jai Alai events"""
    model_config = ConfigDict(extra="allow")

    id: str
    name: str
    isHomeTeam: Optional[bool] = None


class JaiAlaiEvent(BaseModel):
    """Jai Alai event/match details"""
    model_config = ConfigDict(extra="allow")

    id: str
    name: Optional[str] = None
    startDate: Optional[str] = None
    endDate: Optional[str] = None
    eventNumber: Optional[float] = None
    eventType: Optional[str] = None
    streamUrl: Optional[str] = None
    eventStatus: Optional[str] = None
    matchStatus: Optional[str] = None
    onServeNow: Optional[str] = None
    winner: Optional[str] = None
    eventLocation: Optional[str] = None
    sets: List[SetDetail] = Field(default_factory=list)
    homeTeam: Optional[str] = None
    awayTeam: Optional[str] = None
    homeTeamFinalScore: Optional[float] = None
    awayTeamFinalScore: Optional[float] = None
    homeTeamMatchScore: Optional[float] = None
    awayTeamMatchScore: Optional[float] = None
    teams: List[TeamDetail] = Field(default_factory=list)


class EventParticipant(BaseModel):
    """Event participant wrapper"""
    model_config = ConfigDict(extra="allow")

    athlete: Athlete


class EventRoundHeatListing(BaseModel):
    """Heat listing within a round"""
    model_config = ConfigDict(extra="allow")

    id: Optional[str] = None
    heatNo: Optional[float] = None
    heatStatus: Optional[str] = None
    startDate: Optional[str] = None
    endDate: Optional[str] = None


class ScoresListing(BaseModel):
    """Score listing for events"""
    model_config = ConfigDict(extra="allow")

    id: Optional[str] = None
    eventId: Optional[str] = None
    roundId: Optional[str] = None
    score: Optional[float] = None


class EventRoundListing(BaseModel):
    """Round listing within an event"""
    model_config = ConfigDict(extra="allow")

    id: Optional[str] = None
    name: Optional[str] = None
    roundNo: Optional[float] = None
    roundStatus: Optional[str] = None
    startDate: Optional[str] = None
    endDate: Optional[str] = None
    heats: List[EventRoundHeatListing] = Field(default_factory=list)


class TeamsListing(BaseModel):
    """Teams listing in an event"""
    model_config = ConfigDict(extra="allow")

    id: Optional[str] = None
    name: Optional[str] = None
    logo: Optional[str] = None
    shortName: Optional[str] = None
    score: Optional[float] = None


class EventResponse(BaseModel):
    """Full event response"""
    model_config = ConfigDict(extra="allow")

    id: Optional[str] = None
    sportType: Optional[str] = None
    name: Optional[str] = None
    tourId: Optional[str] = None
    tourName: Optional[str] = None
    tourYear: Optional[float] = None
    startDate: Optional[str] = None
    endDate: Optional[str] = None
    eventStatus: Optional[str] = None
    eventLocation: Optional[str] = None
    eventLocationGroup: Optional[str] = None
    eventNumber: Optional[float] = None
    rounds: List[EventRoundListing] = Field(default_factory=list)


class EventListing(BaseModel):
    """Event listing (summary)"""
    model_config = ConfigDict(extra="allow")

    id: Optional[str] = None
    sportType: Optional[str] = None
    name: Optional[str] = None
    tourId: Optional[str] = None
    tourName: Optional[str] = None
    tourYear: Optional[float] = None
    startDate: Optional[str] = None
    endDate: Optional[str] = None
    eventStatus: Optional[str] = None
    eventLocation: Optional[str] = None
    eventLocationGroup: Optional[str] = None
    eventNumber: Optional[float] = None
    rounds: List[EventRoundListing] = Field(default_factory=list)
    teams: List[TeamsListing] = Field(default_factory=list)
    scores: List[ScoresListing] = Field(default_factory=list)


# ── Odds Models ────────────────────────────────────────────────────────────────


class EventWinnerOdd(BaseModel):
    """Event winner / second place / shows / podiums odds"""
    model_config = ConfigDict(extra="allow")

    outcomeId: Optional[str] = None
    id: Optional[str] = None
    referenceId: Optional[float] = None
    position: Optional[float] = None
    probability: Optional[float] = None
    odds: Optional[float] = None
    status: Optional[MarketStatus] = None
    settlement: Optional[Settlement] = None
    updatedAt: Optional[str] = None
    athlete: Optional[Athlete] = None


class FastestLapOdd(BaseModel):
    """Fastest lap odds"""
    model_config = ConfigDict(extra="allow")

    outcomeId: Optional[str] = None
    id: Optional[str] = None
    referenceId: Optional[float] = None
    position: Optional[float] = None
    probability: Optional[float] = None
    odds: Optional[float] = None
    status: Optional[MarketStatus] = None
    settlement: Optional[Settlement] = None
    updatedAt: Optional[str] = None
    athlete: Optional[Athlete] = None


class HeatOddsAthlete(BaseModel):
    """Athlete within a heat odds response"""
    model_config = ConfigDict(extra="allow")

    outcomeId: Optional[str] = None
    id: Optional[str] = None
    referenceId: Optional[float] = None
    firstName: Optional[str] = None
    middleName: Optional[str] = None
    lastName: Optional[str] = None
    gender: Optional[str] = None
    nationality: Optional[str] = None
    stance: Optional[str] = None
    hometown: Optional[str] = None
    height: Optional[str] = None
    weight: Optional[str] = None
    weightClass: Optional[str] = None
    dob: Optional[str] = None
    odds: Optional[float] = None
    probability: Optional[float] = None
    seedNo: Optional[float] = None
    providerId: Optional[str] = None
    settlement: Optional[str] = None
    status: Optional[MarketStatus] = None


class HeatOddsEvent(BaseModel):
    """Heat within a heat odds response"""
    model_config = ConfigDict(extra="allow")

    heatId: Optional[str] = None
    heatName: Optional[str] = None
    heatNo: Optional[float] = None
    heatStatus: Optional[str] = None
    providerId: Optional[str] = None
    athletes: List[HeatOddsAthlete] = Field(default_factory=list)


class HeatOddsRound(BaseModel):
    """Round with heat odds"""
    model_config = ConfigDict(extra="allow")

    id: Optional[str] = None
    referenceId: Optional[float] = None
    roundId: Optional[str] = None
    roundName: Optional[str] = None
    roundNo: Optional[float] = None
    roundStatus: Optional[str] = None
    providerId: Optional[str] = None
    heats: List[HeatOddsEvent] = Field(default_factory=list)
    updatedAt: Optional[str] = None


class PlayerAthlete(BaseModel):
    """Player/athlete in head-to-head or event participant odds"""
    model_config = ConfigDict(extra="allow")

    id: Optional[str] = None
    referenceId: Optional[float] = None
    firstName: Optional[str] = None
    middleName: Optional[str] = None
    lastName: Optional[str] = None
    nationality: Optional[str] = None
    stance: Optional[str] = None
    hometown: Optional[str] = None
    height: Optional[str] = None
    weight: Optional[str] = None
    gender: Optional[str] = None
    weightClass: Optional[str] = None
    dob: Optional[str] = None
    providerId: Optional[str] = None


class PlayerEventParticipant(BaseModel):
    """Player event participant with odds"""
    model_config = ConfigDict(extra="allow")

    id: Optional[str] = None
    referenceId: Optional[float] = None
    position: Optional[float] = None
    odds: Optional[float] = None
    settlement: Optional[Settlement] = None
    status: Optional[MarketStatus] = None
    athlete: Optional[PlayerAthlete] = None


class HeadToHead(BaseModel):
    """Head-to-head matchup odds"""
    model_config = ConfigDict(extra="allow")

    eventParticipant1: Optional[PlayerEventParticipant] = None
    eventParticipant2: Optional[PlayerEventParticipant] = None
    winnerParticipantId: Optional[str] = None


class PropBet(BaseModel):
    """Proposition bet odds"""
    model_config = ConfigDict(extra="allow")

    outcomeId: Optional[str] = None
    id: Optional[str] = None
    referenceId: Optional[float] = None
    athlete: Optional[Athlete] = None
    proposition: Optional[str] = None
    odds: Optional[float] = None
    probability: Optional[float] = None
    settlement: Optional[PropSettlement] = None
    status: Optional[MarketStatus] = None


class DreamTeamTeam(BaseModel):
    """Team within a dream team bet"""
    model_config = ConfigDict(extra="allow")

    team: Optional[str] = None
    odds: Optional[float] = None
    participants: Optional[Any] = None


class DreamTeam(BaseModel):
    """Dream team odds"""
    model_config = ConfigDict(extra="allow")

    outcomeId: Optional[str] = None
    id: Optional[str] = None
    numericEventId: Optional[float] = None
    eventId: Optional[str] = None
    settlement: Optional[Settlement] = None
    teams: List[DreamTeamTeam] = Field(default_factory=list)


class ExactasAthlete(BaseModel):
    """Athlete in exactas"""
    model_config = ConfigDict(extra="allow")

    athleteId: Optional[str] = None
    firstName: Optional[str] = None
    lastName: Optional[str] = None


class ProjectionExacta(BaseModel):
    """Exacta projection odds"""
    model_config = ConfigDict(extra="allow")

    outcomeId: Optional[str] = None
    id: Optional[str] = None
    numericEventId: Optional[float] = None
    eventId: Optional[str] = None
    probability: Optional[float] = None
    odds: Optional[float] = None
    vopiProbability: Optional[float] = None
    vopiOdds: Optional[float] = None
    trueProbability: Optional[float] = None
    trueOdds: Optional[float] = None
    status: Optional[MarketStatus] = None
    settlement: Optional[Settlement] = None
    athletes: List[ExactasAthlete] = Field(default_factory=list)


class OverUnderOdd(BaseModel):
    """Individual over/under odds"""
    model_config = ConfigDict(extra="allow")

    outcomeId: Optional[str] = None
    id: Optional[str] = None
    referenceId: Optional[float] = None
    probability: Optional[float] = None
    odds: Optional[float] = None
    status: Optional[MarketStatus] = None
    settlement: Optional[Settlement] = None
    label: Optional[str] = None


class OverUnderGroup(BaseModel):
    """Over/under group (e.g. for a specific athlete)"""
    model_config = ConfigDict(extra="allow")

    athlete: Optional[Athlete] = None
    overUnderSubMarketType: Optional[str] = None
    line: Optional[float] = None
    odds: List[OverUnderOdd] = Field(default_factory=list)


class SelectionDto(BaseModel):
    """Selection within a market"""
    model_config = ConfigDict(extra="allow")

    outcomeId: Optional[str] = None
    id: Optional[str] = None
    referenceId: Optional[float] = None
    odds: Optional[float] = None
    probability: Optional[float] = None
    status: Optional[MarketStatus] = None
    settlement: Optional[Settlement] = None
    label: Optional[str] = None


class MultiOverUnder(BaseModel):
    """Multi over/under odds"""
    model_config = ConfigDict(extra="allow")

    outcomeId: Optional[str] = None
    id: Optional[str] = None
    referenceId: Optional[float] = None
    line: Optional[float] = None
    overUnderSubMarketType: Optional[str] = None
    selections: List[SelectionDto] = Field(default_factory=list)
    athlete: Optional[Athlete] = None


class EventOdds(BaseModel):
    """Event odds response (polymorphic based on odd type)"""
    model_config = ConfigDict(extra="allow")

    # The actual shape depends on oddType. These cover common patterns.
    odds: Optional[List[Any]] = None
    # For eventWinner, secondPlace, shows, podiums, fastestLap, raceTop*
    data: Optional[Any] = None


# ── SGP Models ─────────────────────────────────────────────────────────────────


class SgpRequest(BaseModel):
    """Same Game Parlay request"""
    model_config = ConfigDict(extra="allow")

    oddIds: List[str]


class SgpResponse(BaseModel):
    """Same Game Parlay response"""
    model_config = ConfigDict(extra="allow")

    viggedProbability: Optional[float] = None
    probability: Optional[float] = None
    decimalOdds: Optional[float] = None
    combinedOdds: Optional[float] = None
    combinedProbability: Optional[float] = None
    selections: Optional[List[Any]] = None


# ── Futures Models ─────────────────────────────────────────────────────────────


class FutureListing(BaseModel):
    """Futures listing"""
    model_config = ConfigDict(extra="allow")

    id: Optional[str] = None
    sportType: Optional[str] = None
    tourId: Optional[str] = None
    tourName: Optional[str] = None
    tourYear: Optional[float] = None
    futureType: Optional[str] = None
    futureStatus: Optional[str] = None


class FutureOddAthlete(BaseModel):
    """Athlete in a futures odd"""
    model_config = ConfigDict(extra="allow")

    id: Optional[str] = None
    referenceId: Optional[float] = None
    firstName: Optional[str] = None
    middleName: Optional[str] = None
    lastName: Optional[str] = None
    nationality: Optional[str] = None
    stance: Optional[str] = None


class FutureOdd(BaseModel):
    """Individual future odd entry"""
    model_config = ConfigDict(extra="allow")

    id: Optional[str] = None
    odds: Optional[float] = None
    probability: Optional[float] = None
    status: Optional[MarketStatus] = None
    settlement: Optional[Settlement] = None


class FutureOdds(BaseModel):
    """Future odds response"""
    model_config = ConfigDict(extra="allow")

    id: Optional[str] = None
    tourId: Optional[str] = None
    athlete: Optional[FutureOddAthlete] = None
    odds: List[FutureOdd] = Field(default_factory=list)


# ── Sports Models ──────────────────────────────────────────────────────────────


class SportsListing(BaseModel):
    """Sports listing response"""
    model_config = ConfigDict(extra="allow")

    id: Optional[str] = None
    name: Optional[str] = None
    sportType: Optional[str] = None
    sportGroup: Optional[str] = None
    homePageUrl: Optional[str] = None
    logo: Optional[str] = None
    tours: Optional[List[Any]] = None
    markets: Optional[List[Any]] = None


# ── Jai Alai Odds ──────────────────────────────────────────────────────────────


class BaseSelection(BaseModel):
    """Base selection for Jai Alai odds"""
    model_config = ConfigDict(extra="allow")

    outcomeId: Optional[str] = None
    id: Optional[str] = None
    referenceId: Optional[float] = None
    odds: Optional[float] = None
    probability: Optional[float] = None
    status: Optional[MarketStatus] = None
    settlement: Optional[Settlement] = None
    label: Optional[str] = None


class MoneyLineGroup(BaseModel):
    """Moneyline group for Jai Alai"""
    model_config = ConfigDict(extra="allow")

    selections: List[BaseSelection] = Field(default_factory=list)


class EventWinnerMarket(BaseModel):
    """Event winner market for Jai Alai"""
    model_config = ConfigDict(extra="allow")

    selections: List[BaseSelection] = Field(default_factory=list)


class SpreadSelection(BaseModel):
    """Spread selection"""
    model_config = ConfigDict(extra="allow")

    outcomeId: Optional[str] = None
    id: Optional[str] = None
    referenceId: Optional[float] = None
    odds: Optional[float] = None
    probability: Optional[float] = None
    status: Optional[MarketStatus] = None
    settlement: Optional[Settlement] = None
    label: Optional[str] = None
    line: Optional[float] = None


class SpreadGroup(BaseModel):
    """Spread group for Jai Alai"""
    model_config = ConfigDict(extra="allow")

    selections: List[SpreadSelection] = Field(default_factory=list)


class TotalSetsSelection(BaseModel):
    """Total sets selection"""
    model_config = ConfigDict(extra="allow")

    outcomeId: Optional[str] = None
    id: Optional[str] = None
    referenceId: Optional[float] = None
    odds: Optional[float] = None
    probability: Optional[float] = None
    status: Optional[MarketStatus] = None
    settlement: Optional[Settlement] = None
    label: Optional[str] = None
    line: Optional[float] = None


class TotalSetsGroup(BaseModel):
    """Total sets group for Jai Alai"""
    model_config = ConfigDict(extra="allow")

    selections: List[TotalSetsSelection] = Field(default_factory=list)


class MatchWinnerMarketType(BaseModel):
    """Match winner market type for Jai Alai"""
    model_config = ConfigDict(extra="allow")

    selections: List[BaseSelection] = Field(default_factory=list)


class SetOdds(BaseModel):
    """Set-level odds for Jai Alai"""
    model_config = ConfigDict(extra="allow")

    id: Optional[str] = None
    name: Optional[str] = None
    moneyLine: Optional[MoneyLineGroup] = None
    spread: Optional[SpreadGroup] = None
    totalSets: Optional[TotalSetsGroup] = None


class JaiAlaiOdds(BaseModel):
    """Jai Alai odds response"""
    model_config = ConfigDict(extra="allow")

    id: Optional[str] = None
    matchName: Optional[str] = None
    moneyLine: Optional[MoneyLineGroup] = None
    eventWinner: Optional[EventWinnerMarket] = None
    spread: Optional[SpreadGroup] = None
    totalSets: Optional[TotalSetsGroup] = None
    matchWinner: Optional[MatchWinnerMarketType] = None
    sets: List[SetOdds] = Field(default_factory=list)


# ── Error Model ────────────────────────────────────────────────────────────────


class ServerError(BaseModel):
    """Server error response"""
    model_config = ConfigDict(extra="allow")

    stack: Optional[str] = None
    message: Optional[str] = None
    metadata: Optional[Any] = None
    statusCode: Optional[Any] = None
    timestamp: Optional[str] = None
