from __future__ import annotations

from datetime import date, datetime
from typing import Optional

from sqlalchemy import (
    Boolean,
    Date,
    DateTime,
    ForeignKey,
    Float,
    Index,
    Integer,
    String,
    Text,
    UniqueConstraint,
)
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column


class Base(DeclarativeBase):
    pass


class Tournament(Base):
    __tablename__ = "hockey_tournaments"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    external_id: Mapped[str] = mapped_column(String(64), nullable=False, unique=True)
    level: Mapped[str] = mapped_column(String(32), nullable=False, default="")
    name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
    name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
    common_name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
    common_name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
    season_part: Mapped[str] = mapped_column(String(32), nullable=False, default="")
    season: Mapped[str] = mapped_column(String(32), nullable=False, default="")
    start_date: Mapped[Optional[date]] = mapped_column(Date, nullable=True)
    end_date: Mapped[Optional[date]] = mapped_column(Date, nullable=True)
    round_code: Mapped[str] = mapped_column(String(16), nullable=False, default="")
    game_id_supported: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    raw_xml: Mapped[str] = mapped_column(Text, nullable=False, default="")
    synced_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )

    __table_args__ = (
        Index("ix_hockey_tournaments_level", "level"),
        Index("ix_hockey_tournaments_season", "season"),
        Index("ix_hockey_tournaments_dates", "start_date", "end_date"),
        Index("ix_hockey_tournaments_stage", "season_part"),
    )


class TournamentStanding(Base):
    __tablename__ = "hockey_tournament_standings"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    tournament_external_id: Mapped[str] = mapped_column(
        String(64), ForeignKey("hockey_tournaments.external_id"),
        nullable=False, unique=True,
    )
    source_endpoint: Mapped[str] = mapped_column(Text, nullable=False, default="")
    raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
    synced_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )


class TournamentStatisticResource(Base):
    """Optional tournament XML such as powerplay or rank."""

    __tablename__ = "hockey_tournament_statistic_resources"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    tournament_external_id: Mapped[str] = mapped_column(
        String(64), ForeignKey("hockey_tournaments.external_id"), nullable=False
    )
    resource_type: Mapped[str] = mapped_column(String(32), nullable=False)
    source_endpoint: Mapped[str] = mapped_column(Text, nullable=False, default="")
    raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
    available: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    status_code: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    error_message: Mapped[str] = mapped_column(Text, nullable=False, default="")
    checked_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )
    synced_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)

    __table_args__ = (
        UniqueConstraint(
            "tournament_external_id",
            "resource_type",
            name="uq_hockey_tournament_stat_resource",
        ),
        Index(
            "ix_hockey_tournament_stat_resource_lookup",
            "tournament_external_id",
            "resource_type",
        ),
    )


class TournamentStandingRow(Base):
    """Normalized team row from standings-{tournament}.xml."""

    __tablename__ = "hockey_tournament_standing_rows"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    tournament_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
    variant_id: Mapped[str] = mapped_column(String(32), nullable=False, default="league")
    group_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
    group_name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
    group_name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
    standings_type: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    conference: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    playoff_places: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    team_key: Mapped[str] = mapped_column(String(128), nullable=False)
    team_entry_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
    club_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
    team_name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
    team_name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
    rank: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    games: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    wins: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    overtime_wins: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    shootout_wins: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    shootout_losses: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    overtime_losses: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    losses: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    points: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    points_pct: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
    goals_for: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    goals_against: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    penalty_minutes: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    opponent_penalty_minutes: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    playoffs_in: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    playoffs_out: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
    synced_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)

    __table_args__ = (
        UniqueConstraint(
            "tournament_external_id", "variant_id", "group_id", "team_key",
            name="uq_hockey_standing_row",
        ),
        Index("ix_hockey_standing_rows_tournament", "tournament_external_id"),
        Index("ix_hockey_standing_rows_team", "tournament_external_id", "team_key"),
    )


class TournamentStatisticRow(Base):
    """Normalized row from optional powerplay/rank tournament feeds."""

    __tablename__ = "hockey_tournament_statistic_rows"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    tournament_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
    resource_type: Mapped[str] = mapped_column(String(32), nullable=False)
    section_id: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    section_name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
    section_name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
    period_ru: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    period_en: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    row_key: Mapped[str] = mapped_column(String(192), nullable=False)
    row_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
    entity_type: Mapped[str] = mapped_column(String(32), nullable=False, default="unknown")
    player_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
    team_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
    rank: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    number: Mapped[str] = mapped_column(String(32), nullable=False, default="")
    name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
    name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
    team_name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
    team_name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
    games: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    goals: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    assists: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    points: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    penalty_minutes: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    power_play_goals: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    power_play_assists: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    power_play_points: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    power_play_opportunities: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    power_play_pct: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
    short_handed_goals: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    wins: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    losses: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    save_pct: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
    goals_against_average: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
    time_on_ice: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    values_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
    raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
    synced_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)

    __table_args__ = (
        UniqueConstraint(
            "tournament_external_id", "resource_type", "section_id", "row_key",
            name="uq_hockey_tournament_statistic_row",
        ),
        Index("ix_hockey_tournament_stat_rows_lookup", "tournament_external_id", "resource_type"),
        Index("ix_hockey_tournament_stat_rows_player", "player_external_id"),
        Index("ix_hockey_tournament_stat_rows_team", "team_external_id"),
    )


class TeamSeasonStatistic(Base):
    """One normalized season aggregate per tournament team."""

    __tablename__ = "hockey_team_season_statistics"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    tournament_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
    team_key: Mapped[str] = mapped_column(String(128), nullable=False)
    team_entry_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
    club_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
    team_name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
    team_name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
    rank: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    games: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    wins: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    overtime_wins: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    shootout_wins: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    shootout_losses: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    overtime_losses: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    losses: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    points: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    points_pct: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
    goals_for: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    goals_against: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    penalty_minutes: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    opponent_penalty_minutes: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    power_play_goals: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    power_play_opportunities: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    power_play_pct: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
    power_play_values_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
    standings_raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
    powerplay_raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
    synced_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)

    __table_args__ = (
        UniqueConstraint("tournament_external_id", "team_key", name="uq_hockey_team_season_stat"),
        Index("ix_hockey_team_season_stats_tournament", "tournament_external_id"),
    )


class Team(Base):
    __tablename__ = "hockey_teams"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    external_id: Mapped[str] = mapped_column(String(64), nullable=False, unique=True)
    name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
    name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
    short_name_ru: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    short_name_en: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    city_ru: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    city_en: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    color_hex: Mapped[str] = mapped_column(String(9), nullable=False, default="")
    logo_url: Mapped[str] = mapped_column(Text, nullable=False, default="")
    raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
    synced_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )


class TeamLeague(Base):
    """League membership used by the editable team directory."""

    __tablename__ = "hockey_team_leagues"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    team_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("hockey_teams.id", ondelete="CASCADE"), nullable=False
    )
    league_key: Mapped[str] = mapped_column(String(32), nullable=False)
    tournament_external_id: Mapped[str] = mapped_column(
        String(64), nullable=False, default=""
    )
    source: Mapped[str] = mapped_column(String(32), nullable=False, default="stat2tv")
    active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    synced_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )

    __table_args__ = (
        UniqueConstraint(
            "team_id",
            "league_key",
            name="uq_hockey_team_league_team_level",
        ),
        Index("ix_hockey_team_leagues_level", "league_key"),
        Index(
            "ix_hockey_team_leagues_tournament",
            "tournament_external_id",
        ),
    )


class PenaltyType(Base):
    """Global bilingual infraction directory shared by every hockey league."""

    __tablename__ = "hockey_penalty_types"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    code: Mapped[str] = mapped_column(String(64), nullable=False, unique=True)
    name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
    name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
    default_preset: Mapped[str] = mapped_column(
        String(32), nullable=False, default="2"
    )
    team_penalty: Mapped[bool] = mapped_column(
        Boolean, nullable=False, default=False
    )
    sort_order: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    created_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )

    __table_args__ = (
        Index("ix_hockey_penalty_types_active_order", "active", "sort_order"),
    )


class Country(Base):
    __tablename__ = "hockey_countries"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    external_id: Mapped[str] = mapped_column(String(64), nullable=False, unique=True)
    iso2: Mapped[str] = mapped_column(String(2), nullable=False, default="")
    iso3: Mapped[str] = mapped_column(String(3), nullable=False, default="")
    name_ru: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    name_en: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    flag_emoji: Mapped[str] = mapped_column(String(16), nullable=False, default="")
    active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
    synced_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )

    __table_args__ = (
        Index("ix_hockey_countries_iso2", "iso2"),
        Index("ix_hockey_countries_iso3", "iso3"),
        Index("ix_hockey_countries_name_ru", "name_ru"),
    )


class Player(Base):
    __tablename__ = "hockey_players"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    external_id: Mapped[str] = mapped_column(String(64), nullable=False, unique=True)
    first_name_ru: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    first_name_en: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    last_name_ru: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    last_name_en: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    full_name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
    full_name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
    position_ru: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    position_en: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    birth_date: Mapped[Optional[date]] = mapped_column(Date, nullable=True)
    height_cm: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    weight_kg: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    stick: Mapped[str] = mapped_column(String(32), nullable=False, default="")
    country_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    country_code: Mapped[str] = mapped_column(String(8), nullable=False, default="")
    active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    photo_url: Mapped[str] = mapped_column(Text, nullable=False, default="")
    raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
    synced_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )


class PlayerTournament(Base):
    __tablename__ = "hockey_player_tournaments"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    player_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("hockey_players.id", ondelete="CASCADE"), nullable=False
    )
    tournament_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
    club_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    team_entry_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    team_name_ru: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    team_name_en: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    jersey_number: Mapped[str] = mapped_column(String(16), nullable=False, default="")
    source: Mapped[str] = mapped_column(String(32), nullable=False, default="stat2tv")
    active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    synced_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )

    __table_args__ = (
        UniqueConstraint(
            "player_id", "tournament_external_id", "team_entry_external_id",
            name="uq_hockey_player_tournament_entry",
        ),
        Index("ix_hockey_player_tournaments_tournament", "tournament_external_id"),
    )


class PlayerSeasonStatistic(Base):
    """Normalized season totals from players-{tournament}.xml."""

    __tablename__ = "hockey_player_season_statistics"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    player_id: Mapped[int] = mapped_column(Integer, ForeignKey("hockey_players.id", ondelete="CASCADE"), nullable=False)
    player_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
    tournament_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
    team_entry_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
    club_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
    team_name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
    team_name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
    jersey_number: Mapped[str] = mapped_column(String(16), nullable=False, default="")
    role: Mapped[str] = mapped_column(String(32), nullable=False, default="")
    games: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    goals: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    assists: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    points: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    penalty_minutes: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    plus_minus: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    plus: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    minus: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    power_play_goals: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    short_handed_goals: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    game_winning_goals: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    even_strength_goals: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    overtime_goals: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    shots: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    shot_pct: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
    faceoffs: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    faceoffs_won: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    faceoff_pct: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
    takeaways: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    interceptions: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    hits: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    blocked_shots: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    average_time_on_ice: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    average_shifts: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
    average_shots: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
    average_time_on_attack: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    average_even_strength_time: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    average_power_play_time: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    average_short_handed_time: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    wins: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    losses: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    draws: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    overtime_losses: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    shots_against: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    goals_against: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    saves: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    save_pct: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
    goals_against_average: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
    shutouts: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    time_on_ice: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    shootouts: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    shootout_goals_against: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    shootout_save_pct: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
    raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
    synced_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)

    __table_args__ = (
        UniqueConstraint("player_id", "tournament_external_id", "team_entry_external_id", name="uq_hockey_player_season_stat"),
        Index("ix_hockey_player_season_stats_tournament", "tournament_external_id"),
        Index("ix_hockey_player_season_stats_player", "player_external_id"),
    )


class Coach(Base):
    __tablename__ = "hockey_coaches"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    external_id: Mapped[str] = mapped_column(String(64), nullable=False, unique=True)
    first_name_ru: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    first_name_en: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    last_name_ru: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    last_name_en: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    full_name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
    full_name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
    birth_date: Mapped[Optional[date]] = mapped_column(Date, nullable=True)
    country_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    country_code: Mapped[str] = mapped_column(String(8), nullable=False, default="")
    photo_url: Mapped[str] = mapped_column(Text, nullable=False, default="")
    active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
    synced_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )


class CoachTournament(Base):
    __tablename__ = "hockey_coach_tournaments"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    coach_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("hockey_coaches.id", ondelete="CASCADE"), nullable=False
    )
    tournament_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
    team_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    team_name_ru: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    team_name_en: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    role_ru: Mapped[str] = mapped_column(String(128), nullable=False, default="Главный тренер")
    role_en: Mapped[str] = mapped_column(String(128), nullable=False, default="Head coach")
    source: Mapped[str] = mapped_column(String(32), nullable=False, default="stat2tv")
    active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    synced_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )

    __table_args__ = (
        UniqueConstraint(
            "coach_id", "tournament_external_id", "team_external_id",
            name="uq_hockey_coach_tournament_team",
        ),
        Index("ix_hockey_coach_tournaments_tournament", "tournament_external_id"),
    )


class Game(Base):
    __tablename__ = "hockey_games"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    external_id: Mapped[str] = mapped_column(String(96), nullable=False, unique=True)
    tournament_external_id: Mapped[str] = mapped_column(
        String(64), ForeignKey("hockey_tournaments.external_id"), nullable=False
    )
    tournament_type: Mapped[str] = mapped_column(String(32), nullable=False, default="")

    game_number: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    round_number: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    round_name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
    round_name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
    day_of_week: Mapped[str] = mapped_column(String(16), nullable=False, default="")
    changes: Mapped[int] = mapped_column(Integer, nullable=False, default=0)

    game_date: Mapped[Optional[date]] = mapped_column(Date, nullable=True)
    start_time: Mapped[str] = mapped_column(String(32), nullable=False, default="")
    start_datetime_raw: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    status: Mapped[str] = mapped_column(String(64), nullable=False, default="scheduled")
    approved: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)

    arena_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
    arena_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
    arena_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
    arena_city_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
    arena_city_en: Mapped[str] = mapped_column(Text, nullable=False, default="")

    # teama / teamb are stable club ids; homeId / visitorId identify
    # the tournament-specific team entry.
    home_team_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
    away_team_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
    home_entry_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
    away_entry_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")

    home_team_name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
    home_team_name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
    home_team_short_ru: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    home_team_short_en: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    away_team_name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
    away_team_name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
    away_team_short_ru: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    away_team_short_en: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    home_team_city_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
    home_team_city_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
    away_team_city_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
    away_team_city_en: Mapped[str] = mapped_column(Text, nullable=False, default="")

    home_score: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    away_score: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    score_raw: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    overtime_type: Mapped[str] = mapped_column(String(32), nullable=False, default="")
    period_1_score: Mapped[str] = mapped_column(String(32), nullable=False, default="")
    period_2_score: Mapped[str] = mapped_column(String(32), nullable=False, default="")
    period_3_score: Mapped[str] = mapped_column(String(32), nullable=False, default="")
    period_4_score: Mapped[str] = mapped_column(String(32), nullable=False, default="")
    series_score: Mapped[str] = mapped_column(String(32), nullable=False, default="")
    attendance: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    temporary_value: Mapped[str] = mapped_column(Text, nullable=False, default="")
    period_scores: Mapped[str] = mapped_column(Text, nullable=False, default="")

    details_source_endpoint: Mapped[str] = mapped_column(
        Text, nullable=False, default=""
    )
    details_raw_payload_ru: Mapped[str] = mapped_column(
        Text, nullable=False, default=""
    )
    details_raw_payload_en: Mapped[str] = mapped_column(
        Text, nullable=False, default=""
    )
    details_synced_at: Mapped[Optional[datetime]] = mapped_column(
        DateTime, nullable=True
    )

    shots_resource_available: Mapped[bool] = mapped_column(
        Boolean, nullable=False, default=False
    )
    shot_attempts_home: Mapped[int] = mapped_column(
        Integer, nullable=False, default=0
    )
    shot_attempts_away: Mapped[int] = mapped_column(
        Integer, nullable=False, default=0
    )
    shots_source_endpoint: Mapped[str] = mapped_column(
        Text, nullable=False, default=""
    )
    shots_raw_payload: Mapped[str] = mapped_column(
        Text, nullable=False, default=""
    )
    shots_synced_at: Mapped[Optional[datetime]] = mapped_column(
        DateTime, nullable=True
    )

    source_endpoint: Mapped[str] = mapped_column(Text, nullable=False, default="")
    raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
    synced_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )

    __table_args__ = (
        Index("ix_hockey_games_date", "game_date"),
        Index("ix_hockey_games_tournament", "tournament_external_id"),
        Index("ix_hockey_games_tournament_date", "tournament_external_id", "game_date"),
        Index("ix_hockey_games_status", "status"),
    )


class GameTeamStatistic(Base):
    """Normalized team statistics for a game total and each period."""

    __tablename__ = "hockey_game_team_statistics"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    game_external_id: Mapped[str] = mapped_column(String(96), ForeignKey("hockey_games.external_id", ondelete="CASCADE"), nullable=False)
    tournament_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
    team_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
    side: Mapped[str] = mapped_column(String(16), nullable=False)
    segment_key: Mapped[str] = mapped_column(String(32), nullable=False, default="total")
    period_number: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    goals: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    shots: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    shot_attempts: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    faceoffs: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    faceoffs_won: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    faceoffs_won_pct: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
    hits: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    blocked_shots: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    takeaways: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    giveaways: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    interceptions: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    penalty_minutes: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    penalties: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    time_on_attack: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    even_strength_time: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    power_play_time: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    short_handed_time: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    empty_net_time: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
    synced_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)

    __table_args__ = (
        UniqueConstraint("game_external_id", "side", "segment_key", name="uq_hockey_game_team_stat_segment"),
        Index("ix_hockey_game_team_stats_game", "game_external_id"),
        Index("ix_hockey_game_team_stats_tournament", "tournament_external_id"),
    )


class GameRoster(Base):
    __tablename__ = "hockey_game_rosters"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    game_external_id: Mapped[str] = mapped_column(
        String(64), ForeignKey("hockey_games.external_id"), nullable=False
    )
    team_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
    player_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
    side: Mapped[str] = mapped_column(String(16), nullable=False)
    number: Mapped[str] = mapped_column(String(16), nullable=False, default="")
    role: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    position_ru: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    position_en: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    line_number: Mapped[str] = mapped_column(String(16), nullable=False, default="")
    # BUILD122: the API line stays available as a baseline, while operators may
    # override the effective line/order without a later Stat2TV refresh erasing it.
    api_line_number: Mapped[str] = mapped_column(String(16), nullable=False, default="")
    api_line_sort_order: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    line_sort_order: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    line_manual: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    captain_role: Mapped[str] = mapped_column(String(32), nullable=False, default="")
    lineup_status: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
    synced_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )

    __table_args__ = (
        UniqueConstraint(
            "game_external_id",
            "team_external_id",
            "player_external_id",
            name="uq_hockey_roster_game_team_player",
        ),
    )


class Referee(Base):
    __tablename__ = "hockey_referees"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    external_id: Mapped[str] = mapped_column(String(64), nullable=False, unique=True)
    first_name_ru: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    first_name_en: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    last_name_ru: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    last_name_en: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    full_name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
    full_name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
    position_code: Mapped[str] = mapped_column(String(16), nullable=False, default="")
    number: Mapped[str] = mapped_column(String(16), nullable=False, default="")
    active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    birth_date: Mapped[Optional[date]] = mapped_column(Date, nullable=True)
    country_ru: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    country_en: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    country_code: Mapped[str] = mapped_column(String(8), nullable=False, default="")
    town_ru: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    town_en: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
    synced_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )


class RefereeTournament(Base):
    __tablename__ = "hockey_referee_tournaments"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    referee_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("hockey_referees.id", ondelete="CASCADE"), nullable=False
    )
    tournament_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
    source: Mapped[str] = mapped_column(String(32), nullable=False, default="stat2tv")
    active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    synced_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )

    __table_args__ = (
        UniqueConstraint(
            "referee_id", "tournament_external_id",
            name="uq_hockey_referee_tournament",
        ),
        Index("ix_hockey_referee_tournaments_tournament", "tournament_external_id"),
    )


class GameOfficial(Base):
    __tablename__ = "hockey_game_officials"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    game_external_id: Mapped[str] = mapped_column(
        String(64), ForeignKey("hockey_games.external_id"), nullable=False
    )
    referee_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
    role: Mapped[str] = mapped_column(String(32), nullable=False)
    slot: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
    number: Mapped[str] = mapped_column(String(16), nullable=False, default="")
    raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
    synced_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )

    __table_args__ = (
        UniqueConstraint(
            "game_external_id", "role", "slot",
            name="uq_hockey_game_official_role_slot",
        ),
        Index("ix_hockey_game_officials_game", "game_external_id"),
    )


class GameEvent(Base):
    __tablename__ = "hockey_game_events"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    external_id: Mapped[str] = mapped_column(String(96), nullable=False)
    game_external_id: Mapped[str] = mapped_column(
        String(64), ForeignKey("hockey_games.external_id"), nullable=False
    )
    event_type: Mapped[str] = mapped_column(String(64), nullable=False)
    period: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    event_time: Mapped[str] = mapped_column(String(32), nullable=False, default="")
    team_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    player_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    title_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
    title_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
    raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
    synced_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )

    __table_args__ = (
        UniqueConstraint(
            "game_external_id", "external_id", name="uq_hockey_event_game_external"
        ),
        Index("ix_hockey_events_game_period", "game_external_id", "period"),
    )


class GameControlState(Base):
    """Operator-controlled state that is not supplied by Stat2TV.

    This table stores the currently selected period and the configuration of
    the manual shootout panel.  There is exactly one row per game.
    """

    __tablename__ = "hockey_game_control_states"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    game_external_id: Mapped[str] = mapped_column(
        String(64), ForeignKey("hockey_games.external_id", ondelete="CASCADE"),
        nullable=False, unique=True,
    )
    current_period: Mapped[str] = mapped_column(
        String(16), nullable=False, default="1"
    )
    shootout_started: Mapped[bool] = mapped_column(
        Boolean, nullable=False, default=False
    )
    shootout_initial_attempts: Mapped[int] = mapped_column(
        Integer, nullable=False, default=3
    )
    shootout_extra_rounds: Mapped[int] = mapped_column(
        Integer, nullable=False, default=0
    )
    # Operator timers are stored as one JSON document per game.  This keeps the
    # main clock and all active penalty clocks together and makes future timer
    # fields additive without another database migration.
    timer_state_json: Mapped[str] = mapped_column(
        Text, nullable=False, default=""
    )
    timer_state_updated_at: Mapped[Optional[datetime]] = mapped_column(
        DateTime, nullable=True
    )
    # Match-scoped operator flags used by runtime controls and shortcut
    # conditions (delayed penalty, empty net, prematch toggles, etc.).
    # Stored separately from timer_state_json so timer saves cannot wipe them.
    runtime_flags_json: Mapped[str] = mapped_column(
        Text, nullable=False, default="{}"
    )
    # Match-scoped string values used by parameterised operator controls.
    # Example: panel.stat_period.value = "2". Kept separate from boolean flags.
    runtime_values_json: Mapped[str] = mapped_column(
        Text, nullable=False, default="{}"
    )
    updated_by: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    created_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )

    __table_args__ = (
        Index("ix_hockey_game_control_game", "game_external_id"),
    )


class ShootoutAttempt(Base):
    """One manually registered shootout attempt."""

    __tablename__ = "hockey_shootout_attempts"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    game_external_id: Mapped[str] = mapped_column(
        String(64), ForeignKey("hockey_games.external_id", ondelete="CASCADE"),
        nullable=False,
    )
    sequence_number: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
    team_attempt_number: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
    series_number: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
    side: Mapped[str] = mapped_column(String(16), nullable=False)
    team_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    player_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    player_number: Mapped[str] = mapped_column(String(16), nullable=False, default="")
    player_name: Mapped[str] = mapped_column(Text, nullable=False, default="")
    scored: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    created_by: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    created_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )

    __table_args__ = (
        UniqueConstraint(
            "game_external_id", "sequence_number",
            name="uq_hockey_shootout_game_sequence",
        ),
        Index("ix_hockey_shootout_game_side", "game_external_id", "side"),
    )


class OperatorSession(Base):
    __tablename__ = "hockey_operator_sessions"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    session_token: Mapped[str] = mapped_column(String(128), nullable=False, unique=True)
    wfl_user_id: Mapped[str] = mapped_column(String(128), nullable=False)
    login_snapshot: Mapped[str] = mapped_column(String(100), nullable=False, default="")
    display_name_snapshot: Mapped[str] = mapped_column(
        String(200), nullable=False, default=""
    )
    tournament_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
    game_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    display_language: Mapped[str] = mapped_column(String(8), nullable=False, default="ru")
    vmix_language: Mapped[str] = mapped_column(String(16), nullable=False, default="ru")
    vmix_device_uuid: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    status: Mapped[str] = mapped_column(String(32), nullable=False, default="active")
    created_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )
    last_activity_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )
    closed_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)

    __table_args__ = (
        Index("ix_hockey_sessions_user_status", "wfl_user_id", "status"),
        Index("ix_hockey_sessions_game_status", "game_external_id", "status"),
    )


class VmixDevice(Base):
    __tablename__ = "hockey_vmix_devices"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    device_uuid: Mapped[str] = mapped_column(String(128), nullable=False, unique=True)
    device_secret_hash: Mapped[str] = mapped_column(String(64), nullable=False)
    name: Mapped[str] = mapped_column(String(200), nullable=False, default="")
    hostname: Mapped[str] = mapped_column(String(200), nullable=False, default="")
    agent_version: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    wfl_user_id: Mapped[Optional[str]] = mapped_column(String(128), nullable=True)
    login_snapshot: Mapped[str] = mapped_column(String(100), nullable=False, default="")
    is_active_for_account: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    paired_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
    first_seen_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
    last_seen_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
    last_ip: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    vmix_connected: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    vmix_version: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    vmix_url: Mapped[str] = mapped_column(String(500), nullable=False, default="")
    current_match_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    current_assignment_key: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    last_error: Mapped[str] = mapped_column(Text, nullable=False, default="")
    project_fingerprint: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    project_inventory_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
    project_input_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    project_field_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    project_scanned_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)

    __table_args__ = (
        Index("ix_hockey_vmix_devices_user", "wfl_user_id"),
        Index("ix_hockey_vmix_devices_last_seen", "last_seen_at"),
    )


class VmixAssignment(Base):
    __tablename__ = "hockey_vmix_assignments"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    assignment_key: Mapped[str] = mapped_column(String(128), nullable=False, unique=True)
    device_id: Mapped[int] = mapped_column(
        ForeignKey("hockey_vmix_devices.id", ondelete="CASCADE"), nullable=False
    )
    wfl_user_id: Mapped[str] = mapped_column(String(128), nullable=False)
    operator_session_token: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    tournament_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    game_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
    active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
    last_activity_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
    ended_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)

    __table_args__ = (
        Index("ix_hockey_vmix_assignments_device_active", "device_id", "active"),
        Index("ix_hockey_vmix_assignments_user_game", "wfl_user_id", "game_external_id"),
    )


class VmixPreparedTitle(Base):
    """A match-scoped vMix title clone prepared by an operator.

    The actual graphic lives in vMix.  This row only remembers which source was
    cloned, where the clone ended up and the field snapshot that was applied.
    """

    __tablename__ = "hockey_vmix_prepared_titles"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    wfl_user_id: Mapped[str] = mapped_column(String(128), nullable=False, index=True)
    game_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="", index=True)
    device_uuid: Mapped[str] = mapped_column(String(128), nullable=False, default="", index=True)
    name: Mapped[str] = mapped_column(String(200), nullable=False, default="")
    source_kind: Mapped[str] = mapped_column(String(32), nullable=False, default="manual")
    source_ref: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    source_input_key: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    source_input_number: Mapped[str] = mapped_column(String(32), nullable=False, default="")
    source_input_title: Mapped[str] = mapped_column(String(300), nullable=False, default="")
    clone_input_key: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    clone_input_number: Mapped[str] = mapped_column(String(32), nullable=False, default="")
    clone_input_title: Mapped[str] = mapped_column(String(300), nullable=False, default="")
    field_values_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
    created_by: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
    updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)

    __table_args__ = (
        Index("ix_hockey_prepared_titles_user_game", "wfl_user_id", "game_external_id", "created_at"),
        Index("ix_hockey_prepared_titles_device", "device_uuid", "created_at"),
    )


class VmixMappingProfile(Base):
    __tablename__ = "hockey_vmix_mapping_profiles"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    name: Mapped[str] = mapped_column(String(200), nullable=False, unique=True)
    description: Mapped[str] = mapped_column(Text, nullable=False, default="")
    project_fingerprint: Mapped[str] = mapped_column(String(64), nullable=False, default="", index=True)
    inventory_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
    version: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
    active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    created_by: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    updated_by: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
    updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)

    __table_args__ = (
        Index("ix_hockey_vmix_mapping_profiles_fingerprint_active", "project_fingerprint", "active"),
    )


class VmixMappingField(Base):
    __tablename__ = "hockey_vmix_mapping_fields"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    profile_id: Mapped[int] = mapped_column(
        ForeignKey("hockey_vmix_mapping_profiles.id", ondelete="CASCADE"), nullable=False
    )
    graphic: Mapped[str] = mapped_column(String(100), nullable=False, default="")
    data_key: Mapped[str] = mapped_column(String(200), nullable=False)
    vmix_input_key: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    vmix_input_number: Mapped[str] = mapped_column(String(32), nullable=False, default="")
    vmix_input_title: Mapped[str] = mapped_column(String(300), nullable=False, default="")
    vmix_field: Mapped[str] = mapped_column(String(300), nullable=False)
    field_type: Mapped[str] = mapped_column(String(32), nullable=False, default="text")
    # Optional conditional action for the target (text colour / deterministic visibility).
    # JSON keeps the rule engine extensible without creating a migration for every new operator.
    rule_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
    enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    sort_order: Mapped[int] = mapped_column(Integer, nullable=False, default=0)

    __table_args__ = (
        Index("ix_hockey_vmix_mapping_fields_profile", "profile_id", "sort_order"),
        UniqueConstraint("profile_id", "graphic", "data_key", "vmix_input_key", "vmix_field", name="uq_hockey_vmix_mapping_target"),
    )


class MappingContextVariable(Base):
    __tablename__ = "hockey_mapping_context_variables"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    key: Mapped[str] = mapped_column(String(128), nullable=False, unique=True)
    label: Mapped[str] = mapped_column(String(200), nullable=False)
    category: Mapped[str] = mapped_column(String(100), nullable=False, default="Контекст")
    description: Mapped[str] = mapped_column(Text, nullable=False, default="")
    value_type: Mapped[str] = mapped_column(String(32), nullable=False, default="text")
    entity_type: Mapped[str] = mapped_column(String(32), nullable=False, default="")
    scope: Mapped[str] = mapped_column(String(32), nullable=False, default="match")
    source_type: Mapped[str] = mapped_column(String(32), nullable=False, default="manual")
    default_value: Mapped[str] = mapped_column(Text, nullable=False, default="")
    is_system: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    sort_order: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    created_by: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    updated_by: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
    updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)

    __table_args__ = (
        Index("ix_hockey_mapping_context_variable_group", "category", "sort_order"),
    )


class MappingContextValue(Base):
    __tablename__ = "hockey_mapping_context_values"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    variable_id: Mapped[int] = mapped_column(
        ForeignKey("hockey_mapping_context_variables.id", ondelete="CASCADE"), nullable=False
    )
    scope_key: Mapped[str] = mapped_column(String(500), nullable=False)
    wfl_user_id: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    operator_session_token: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    game_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
    value_text: Mapped[str] = mapped_column(Text, nullable=False, default="")
    updated_by: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)

    __table_args__ = (
        UniqueConstraint("variable_id", "scope_key", name="uq_hockey_mapping_context_value_scope"),
        Index("ix_hockey_mapping_context_values_user_game", "wfl_user_id", "game_external_id"),
    )


class MappingSqlDataSource(Base):
    __tablename__ = "hockey_mapping_sql_sources"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    code: Mapped[str] = mapped_column(String(100), nullable=False, unique=True)
    name: Mapped[str] = mapped_column(String(200), nullable=False)
    category: Mapped[str] = mapped_column(String(100), nullable=False, default="Данные")
    description: Mapped[str] = mapped_column(Text, nullable=False, default="")
    sql_text: Mapped[str] = mapped_column(Text, nullable=False)
    field_metadata_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
    enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    auto_refresh_enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    refresh_interval_ms: Mapped[int] = mapped_column(Integer, nullable=False, default=1000)
    sort_order: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    created_by: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    updated_by: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
    updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)

    __table_args__ = (
        Index("ix_hockey_mapping_sql_sources_enabled", "enabled", "sort_order"),
    )


class HockeyWebSession(Base):
    __tablename__ = "hockey_web_sessions"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    token_hash: Mapped[str] = mapped_column(String(64), nullable=False, unique=True)
    wfl_user_id: Mapped[str] = mapped_column(String(128), nullable=False)
    login_snapshot: Mapped[str] = mapped_column(String(100), nullable=False, default="")
    role_snapshot: Mapped[str] = mapped_column(String(32), nullable=False, default="operator")
    created_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )
    last_activity_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )
    expires_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
    revoked_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)

    __table_args__ = (
        Index("ix_hockey_web_sessions_user", "wfl_user_id"),
        Index("ix_hockey_web_sessions_expires", "expires_at"),
    )


class UserPreference(Base):
    __tablename__ = "hockey_user_preferences"

    wfl_user_id: Mapped[str] = mapped_column("user_id", String(128), primary_key=True)
    display_language: Mapped[str] = mapped_column(String(8), nullable=False, default="ru")
    vmix_language: Mapped[str] = mapped_column(String(16), nullable=False, default="ru")
    # Per-account runtime preference. Store the negative form so additive schema
    # repair safely gives existing accounts FALSE (= events enabled).
    triggers_disabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    selected_tournament_external_id: Mapped[str] = mapped_column(
        String(64), nullable=False, default=""
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )


class SyncRun(Base):
    __tablename__ = "hockey_sync_runs"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    endpoint: Mapped[str] = mapped_column(Text, nullable=False)
    status: Mapped[str] = mapped_column(String(32), nullable=False)
    auth_mode: Mapped[str] = mapped_column(String(16), nullable=False, default="")
    status_code: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    item_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    source: Mapped[str] = mapped_column(String(32), nullable=False, default="")
    error: Mapped[str] = mapped_column(Text, nullable=False, default="")
    snapshot_path: Mapped[str] = mapped_column(Text, nullable=False, default="")
    started_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )
    finished_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)


class EndpointDiscovery(Base):
    __tablename__ = "hockey_endpoint_discoveries"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    resource_type: Mapped[str] = mapped_column(String(32), nullable=False)
    tournament_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="")
    endpoint_template: Mapped[str] = mapped_column(Text, nullable=False, default="")
    resolved_endpoint: Mapped[str] = mapped_column(Text, nullable=False, default="")
    status: Mapped[str] = mapped_column(String(32), nullable=False)
    status_code: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    root_tag: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    item_tag: Mapped[str] = mapped_column(String(128), nullable=False, default="")
    item_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    diagnostics_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
    snapshot_path: Mapped[str] = mapped_column(Text, nullable=False, default="")
    error: Mapped[str] = mapped_column(Text, nullable=False, default="")
    created_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )

    __table_args__ = (
        Index(
            "ix_hockey_endpoint_discovery_resource_tournament",
            "resource_type",
            "tournament_external_id",
        ),
    )
