-- 20260717000000_schema.sql — Consolidated schema for x0gp catalog, races, and drivers.

-- ---------------------------------------------------------------------------
-- Tracks & Waypoints
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS tracks (
    id              TEXT PRIMARY KEY,
    name            TEXT NOT NULL,
    description     TEXT NOT NULL DEFAULT '',
    author_id       TEXT NOT NULL DEFAULT 'system',
    visibility      TEXT NOT NULL DEFAULT 'system' CHECK (visibility IN ('system', 'public', 'private')),
    scale           INT  NOT NULL DEFAULT 27,
    length_m        DOUBLE PRECISION NOT NULL DEFAULT 0,
    width_m         DOUBLE PRECISION NOT NULL DEFAULT 0,
    lane_width_m    DOUBLE PRECISION NOT NULL DEFAULT 0.20,
    surface         TEXT NOT NULL DEFAULT 'carpet' CHECK (surface IN ('carpet', 'tile', 'wood', 'paper', 'mixed')),
    best_lap_ms     BIGINT NOT NULL DEFAULT 0,
    best_lap_holder TEXT NOT NULL DEFAULT '',
    bounds_min_x    DOUBLE PRECISION NOT NULL DEFAULT 0,
    bounds_min_y    DOUBLE PRECISION NOT NULL DEFAULT 0,
    bounds_max_x    DOUBLE PRECISION NOT NULL DEFAULT 0,
    bounds_max_y    DOUBLE PRECISION NOT NULL DEFAULT 0,
    created_ms      BIGINT NOT NULL,
    updated_ms      BIGINT NOT NULL
);

CREATE INDEX IF NOT EXISTS tracks_visibility_idx ON tracks (visibility);
CREATE INDEX IF NOT EXISTS tracks_author_idx     ON tracks (author_id);

CREATE TABLE IF NOT EXISTS track_waypoints (
    track_id    TEXT NOT NULL REFERENCES tracks(id) ON DELETE CASCADE,
    seq         INT  NOT NULL,
    x           DOUBLE PRECISION NOT NULL,
    y           DOUBLE PRECISION NOT NULL,
    heading_rad DOUBLE PRECISION NOT NULL,
    speed_ms    DOUBLE PRECISION NOT NULL,
    curvature   DOUBLE PRECISION NOT NULL,
    PRIMARY KEY (track_id, seq)
);

CREATE TABLE IF NOT EXISTS track_tags (
    track_id TEXT NOT NULL REFERENCES tracks(id) ON DELETE CASCADE,
    tag      TEXT NOT NULL,
    PRIMARY KEY (track_id, tag)
);

CREATE INDEX IF NOT EXISTS track_tags_tag_idx ON track_tags (tag);

-- ---------------------------------------------------------------------------
-- Cars
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS cars (
    id              TEXT PRIMARY KEY,
    name            TEXT NOT NULL,
    owner_id        TEXT NOT NULL DEFAULT 'system',
    visibility      TEXT NOT NULL DEFAULT 'system' CHECK (visibility IN ('system', 'public', 'private')),
    scale           INT  NOT NULL DEFAULT 27,

    length_mm       DOUBLE PRECISION NOT NULL,
    width_mm        DOUBLE PRECISION NOT NULL,
    height_mm       DOUBLE PRECISION NOT NULL DEFAULT 0,
    weight_g        DOUBLE PRECISION NOT NULL DEFAULT 0,

    chassis_model       TEXT NOT NULL DEFAULT '',
    chassis_material    TEXT NOT NULL DEFAULT '',
    chassis_printed     BOOLEAN NOT NULL DEFAULT FALSE,
    chassis_wheelbase_mm DOUBLE PRECISION NOT NULL DEFAULT 0,
    chassis_track_mm    DOUBLE PRECISION NOT NULL DEFAULT 0,

    motor_kind     TEXT NOT NULL DEFAULT 'brushed',
    motor_class    TEXT NOT NULL DEFAULT '',
    motor_kv       INT  NOT NULL DEFAULT 0,
    motor_power_w  DOUBLE PRECISION NOT NULL DEFAULT 0,

    battery_voltage_v    DOUBLE PRECISION NOT NULL DEFAULT 0,
    battery_capacity_mah INT NOT NULL DEFAULT 0,
    battery_cells        INT NOT NULL DEFAULT 0,
    battery_chemistry    TEXT NOT NULL DEFAULT 'li-po',

    drive          TEXT NOT NULL DEFAULT '2WD' CHECK (drive IN ('2WD', '4WD')),

    top_speed_ms   DOUBLE PRECISION NOT NULL DEFAULT 0,
    color_hex      TEXT NOT NULL DEFAULT '#ff6b1a',
    avatar_url     TEXT NOT NULL DEFAULT '',
    active         BOOLEAN NOT NULL DEFAULT TRUE,

    total_distance_m DOUBLE PRECISION NOT NULL DEFAULT 0,
    total_races      INT NOT NULL DEFAULT 0,
    total_laps       INT NOT NULL DEFAULT 0,
    best_lap_ms      BIGINT NOT NULL DEFAULT 0,

    created_ms BIGINT NOT NULL,
    updated_ms BIGINT NOT NULL,
    device_id  INT
);

CREATE INDEX IF NOT EXISTS cars_visibility_idx ON cars (visibility);
CREATE INDEX IF NOT EXISTS cars_owner_idx      ON cars (owner_id);

-- ---------------------------------------------------------------------------
-- Clans & Drivers
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS clans (
    id          TEXT PRIMARY KEY,
    tag         TEXT NOT NULL UNIQUE,
    name        TEXT NOT NULL,
    avatar_url  TEXT NOT NULL DEFAULT '',
    created_ms  BIGINT NOT NULL,
    updated_ms  BIGINT NOT NULL
);

CREATE INDEX IF NOT EXISTS clans_tag_idx ON clans (tag);

CREATE TABLE IF NOT EXISTS drivers (
    id          TEXT PRIMARY KEY,
    nickname    TEXT NOT NULL UNIQUE,
    name        TEXT NOT NULL,
    avatar_url  TEXT NOT NULL DEFAULT '',
    clan_id     TEXT REFERENCES clans(id) ON DELETE SET NULL,
    created_ms  BIGINT NOT NULL,
    updated_ms  BIGINT NOT NULL
);

CREATE INDEX IF NOT EXISTS drivers_nickname_idx ON drivers (nickname);
CREATE INDEX IF NOT EXISTS drivers_clan_idx     ON drivers (clan_id);

-- ---------------------------------------------------------------------------
-- Races & Race Drivers
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS races (
    id              TEXT PRIMARY KEY,
    name            TEXT NOT NULL,
    track_id        TEXT NOT NULL DEFAULT '',
    max_cars        INT  NOT NULL,
    laps            INT  NOT NULL,
    time_limit_s    INT  NOT NULL,
    status          TEXT NOT NULL DEFAULT 'lobby' CHECK (status IN ('lobby', 'countdown', 'racing', 'finished', 'cancelled')),
    created_ms      BIGINT NOT NULL,
    started_ms      BIGINT NOT NULL DEFAULT 0,
    finished_ms     BIGINT NOT NULL DEFAULT 0,
    duration_ms     BIGINT NOT NULL DEFAULT 0,
    total_laps      INT  NOT NULL DEFAULT 0,
    total_drivers   INT  NOT NULL DEFAULT 0,
    updated_ms      BIGINT NOT NULL DEFAULT 0
);

CREATE INDEX IF NOT EXISTS races_status_finished_idx ON races (status, finished_ms DESC, id DESC);
CREATE INDEX IF NOT EXISTS races_started_idx ON races (status, started_ms DESC, id DESC);
CREATE INDEX IF NOT EXISTS races_track_idx ON races (track_id);
CREATE INDEX IF NOT EXISTS races_status_idx ON races (status);

CREATE TABLE IF NOT EXISTS race_drivers (
    race_id     TEXT NOT NULL REFERENCES races(id) ON DELETE CASCADE,
    driver_id   TEXT NOT NULL,
    slot        INT  NOT NULL DEFAULT 0,
    joined_ms   BIGINT NOT NULL,
    total_time_ms BIGINT NOT NULL DEFAULT 0,
    best_lap_ms   BIGINT NOT NULL DEFAULT 0,
    position      INT,
    PRIMARY KEY (race_id, driver_id)
);

CREATE INDEX IF NOT EXISTS race_drivers_driver_idx ON race_drivers (driver_id);
CREATE INDEX IF NOT EXISTS race_drivers_podium_idx ON race_drivers (race_id, total_time_ms ASC, position ASC) WHERE total_time_ms > 0;

-- ---------------------------------------------------------------------------
-- Race Plans & Queue
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS race_plans (
    id            TEXT PRIMARY KEY,
    name          TEXT NOT NULL,
    track_id      TEXT NOT NULL DEFAULT 'default',
    max_cars      INT  NOT NULL,
    laps          INT  NOT NULL DEFAULT 0,
    time_limit_s  INT  NOT NULL DEFAULT 0,
    start_at_ms   BIGINT NOT NULL,
    interval_s    INT  NOT NULL DEFAULT 0,  -- 0 = single-shot
    count         INT  NOT NULL DEFAULT 0,  -- 0 = repeat forever
    enabled       BOOLEAN NOT NULL DEFAULT TRUE,
    created_ms    BIGINT NOT NULL,
    updated_ms    BIGINT NOT NULL,
    next_fire_ms  BIGINT NOT NULL,          -- last materialised fire time
    fires_done    INT  NOT NULL DEFAULT 0
);

CREATE INDEX IF NOT EXISTS race_plans_start_idx ON race_plans (start_at_ms ASC, id ASC);
CREATE INDEX IF NOT EXISTS race_plans_enabled_idx ON race_plans (enabled, next_fire_ms);

CREATE TABLE IF NOT EXISTS race_queue (
    driver_id  TEXT NOT NULL,
    race_id    TEXT NOT NULL,
    plan_id    TEXT NOT NULL DEFAULT '',
    enqueued_ms BIGINT NOT NULL,
    PRIMARY KEY (driver_id, race_id)
);

CREATE INDEX IF NOT EXISTS race_queue_race_idx ON race_queue (race_id);
CREATE INDEX IF NOT EXISTS race_queue_driver_idx ON race_queue (driver_id, enqueued_ms);

-- ---------------------------------------------------------------------------
-- Lobby Drivers
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS lobby_drivers (
    driver_id     TEXT PRIMARY KEY,
    name          TEXT NOT NULL DEFAULT '',
    nickname      TEXT NOT NULL DEFAULT '',
    avatar_url    TEXT NOT NULL DEFAULT '',
    clan_id       TEXT NOT NULL DEFAULT '',
    clan_tag      TEXT NOT NULL DEFAULT '',
    status        TEXT NOT NULL DEFAULT 'idle' CHECK (status IN ('idle', 'racing', 'offline')),
    current_race_id TEXT NOT NULL DEFAULT '',
    connected_ms  BIGINT NOT NULL,
    last_seen_ms  BIGINT NOT NULL,
    car_id        TEXT
);

CREATE INDEX IF NOT EXISTS lobby_drivers_status_idx ON lobby_drivers (status);

-- ---------------------------------------------------------------------------
-- Track Calendar
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS track_calendar (
    id SERIAL PRIMARY KEY,
    track_id TEXT NOT NULL REFERENCES tracks(id) ON DELETE CASCADE,
    start_at TIMESTAMP WITH TIME ZONE NOT NULL,
    end_at TIMESTAMP WITH TIME ZONE NOT NULL,
    CHECK (start_at < end_at)
);

CREATE INDEX IF NOT EXISTS track_calendar_dates_idx ON track_calendar (start_at, end_at);

-- ---------------------------------------------------------------------------
-- Go Migration Metadata (Bridges Go server startup migrations and Supabase)
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS schema_migrations (
    version    TEXT PRIMARY KEY,
    applied_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

INSERT INTO schema_migrations (version) VALUES
    ('001_init.sql'),
    ('002_seed.sql'),
    ('003_f1_seed.sql'),
    ('004_monaco_rescale.sql'),
    ('005_races.sql'),
    ('006_drop_host.sql'),
    ('007_podium.sql'),
    ('008_drivers_clans.sql'),
    ('009_live_persistence.sql'),
    ('010_unify_races.sql'),
    ('011_drop_driver_ids.sql'),
    ('012_normalize_race_results.sql'),
    ('013_driver_device.sql'),
    ('014_track_calendar.sql'),
    ('015_driver_car_id.sql')
ON CONFLICT (version) DO NOTHING;

