-- 001_initial_schema.sql
-- Phase 1: Complete tournament engine schema
-- All financial columns use INTEGER (int64 cents, never REAL/FLOAT)
-- All timestamps use INTEGER (Unix epoch seconds)
-- All UUIDs stored as TEXT

-- =============================================================================
-- Venue & Settings
-- =============================================================================

CREATE TABLE IF NOT EXISTS venue_settings (
    id INTEGER PRIMARY KEY CHECK (id = 1),  -- singleton row
    venue_name TEXT NOT NULL DEFAULT '',
    currency_code TEXT NOT NULL DEFAULT 'DKK',
    currency_symbol TEXT NOT NULL DEFAULT 'kr',
    rounding_denomination INTEGER NOT NULL DEFAULT 5000,  -- 50.00 kr in cents
    receipt_mode TEXT NOT NULL DEFAULT 'digital' CHECK (receipt_mode IN ('off', 'digital', 'print', 'both')),
    timezone TEXT NOT NULL DEFAULT 'Europe/Copenhagen',
    created_at INTEGER NOT NULL DEFAULT (unixepoch()),
    updated_at INTEGER NOT NULL DEFAULT (unixepoch())
);

-- =============================================================================
-- Building Blocks (venue-level reusable)
-- =============================================================================

-- Chip Sets
CREATE TABLE IF NOT EXISTS chip_sets (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    is_builtin INTEGER NOT NULL DEFAULT 0,
    created_at INTEGER NOT NULL DEFAULT (unixepoch()),
    updated_at INTEGER NOT NULL DEFAULT (unixepoch())
);

CREATE TABLE IF NOT EXISTS chip_denominations (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    chip_set_id INTEGER NOT NULL REFERENCES chip_sets(id) ON DELETE CASCADE,
    value INTEGER NOT NULL,  -- cents
    color_hex TEXT NOT NULL DEFAULT '#FFFFFF',
    label TEXT NOT NULL DEFAULT '',
    sort_order INTEGER NOT NULL DEFAULT 0,
    UNIQUE(chip_set_id, value)
);

-- Blind Structures
CREATE TABLE IF NOT EXISTS blind_structures (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    is_builtin INTEGER NOT NULL DEFAULT 0,
    game_type_default TEXT NOT NULL DEFAULT 'nlhe',
    notes TEXT NOT NULL DEFAULT '',
    created_at INTEGER NOT NULL DEFAULT (unixepoch()),
    updated_at INTEGER NOT NULL DEFAULT (unixepoch())
);

CREATE TABLE IF NOT EXISTS blind_levels (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    structure_id INTEGER NOT NULL REFERENCES blind_structures(id) ON DELETE CASCADE,
    position INTEGER NOT NULL,  -- sort order
    level_type TEXT NOT NULL DEFAULT 'round' CHECK (level_type IN ('round', 'break')),
    game_type TEXT NOT NULL DEFAULT 'nlhe',
    small_blind INTEGER NOT NULL DEFAULT 0,
    big_blind INTEGER NOT NULL DEFAULT 0,
    ante INTEGER NOT NULL DEFAULT 0,
    bb_ante INTEGER NOT NULL DEFAULT 0,
    duration_seconds INTEGER NOT NULL DEFAULT 900,  -- 15 min default
    chip_up_denomination_value INTEGER,  -- nullable, cents
    notes TEXT NOT NULL DEFAULT '',
    UNIQUE(structure_id, position)
);

-- Payout Structures
CREATE TABLE IF NOT EXISTS payout_structures (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    is_builtin INTEGER NOT NULL DEFAULT 0,
    created_at INTEGER NOT NULL DEFAULT (unixepoch()),
    updated_at INTEGER NOT NULL DEFAULT (unixepoch())
);

CREATE TABLE IF NOT EXISTS payout_brackets (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    structure_id INTEGER NOT NULL REFERENCES payout_structures(id) ON DELETE CASCADE,
    min_entries INTEGER NOT NULL,
    max_entries INTEGER NOT NULL,
    CHECK (min_entries <= max_entries)
);

CREATE TABLE IF NOT EXISTS payout_tiers (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    bracket_id INTEGER NOT NULL REFERENCES payout_brackets(id) ON DELETE CASCADE,
    position INTEGER NOT NULL,
    percentage_basis_points INTEGER NOT NULL,  -- e.g. 5000 = 50.00%
    UNIQUE(bracket_id, position)
);

-- Buy-in Configs
CREATE TABLE IF NOT EXISTS buyin_configs (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    buyin_amount INTEGER NOT NULL DEFAULT 0,       -- cents
    starting_chips INTEGER NOT NULL DEFAULT 0,
    rake_total INTEGER NOT NULL DEFAULT 0,          -- cents
    bounty_amount INTEGER NOT NULL DEFAULT 0,       -- cents
    bounty_chip INTEGER NOT NULL DEFAULT 0,
    rebuy_allowed INTEGER NOT NULL DEFAULT 0,
    rebuy_cost INTEGER NOT NULL DEFAULT 0,          -- cents
    rebuy_chips INTEGER NOT NULL DEFAULT 0,
    rebuy_rake INTEGER NOT NULL DEFAULT 0,          -- cents
    rebuy_limit INTEGER NOT NULL DEFAULT 0,         -- 0 = unlimited
    rebuy_level_cutoff INTEGER,                     -- nullable
    rebuy_time_cutoff_seconds INTEGER,              -- nullable
    rebuy_chip_threshold INTEGER,                   -- nullable
    addon_allowed INTEGER NOT NULL DEFAULT 0,
    addon_cost INTEGER NOT NULL DEFAULT 0,          -- cents
    addon_chips INTEGER NOT NULL DEFAULT 0,
    addon_rake INTEGER NOT NULL DEFAULT 0,          -- cents
    addon_level_start INTEGER,                      -- nullable
    addon_level_end INTEGER,                        -- nullable
    reentry_allowed INTEGER NOT NULL DEFAULT 0,
    reentry_limit INTEGER NOT NULL DEFAULT 0,
    late_reg_level_cutoff INTEGER,                  -- nullable
    late_reg_time_cutoff_seconds INTEGER,            -- nullable
    created_at INTEGER NOT NULL DEFAULT (unixepoch()),
    updated_at INTEGER NOT NULL DEFAULT (unixepoch())
);

CREATE TABLE IF NOT EXISTS rake_splits (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    buyin_config_id INTEGER NOT NULL REFERENCES buyin_configs(id) ON DELETE CASCADE,
    category TEXT NOT NULL CHECK (category IN ('house', 'staff', 'league', 'season_reserve')),
    amount INTEGER NOT NULL DEFAULT 0,  -- cents
    UNIQUE(buyin_config_id, category)
);

-- Points Formulas
CREATE TABLE IF NOT EXISTS points_formulas (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    expression TEXT NOT NULL DEFAULT '',
    variables TEXT NOT NULL DEFAULT '{}',  -- JSON
    is_builtin INTEGER NOT NULL DEFAULT 0,
    created_at INTEGER NOT NULL DEFAULT (unixepoch()),
    updated_at INTEGER NOT NULL DEFAULT (unixepoch())
);

-- =============================================================================
-- Tournament Templates
-- =============================================================================

CREATE TABLE IF NOT EXISTS tournament_templates (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    description TEXT NOT NULL DEFAULT '',
    chip_set_id INTEGER NOT NULL REFERENCES chip_sets(id),
    blind_structure_id INTEGER NOT NULL REFERENCES blind_structures(id),
    payout_structure_id INTEGER NOT NULL REFERENCES payout_structures(id),
    buyin_config_id INTEGER NOT NULL REFERENCES buyin_configs(id),
    points_formula_id INTEGER REFERENCES points_formulas(id),  -- nullable
    min_players INTEGER NOT NULL DEFAULT 2,
    max_players INTEGER,  -- nullable
    early_signup_bonus_chips INTEGER NOT NULL DEFAULT 0,
    early_signup_cutoff TEXT,  -- nullable, datetime or player count
    punctuality_bonus_chips INTEGER NOT NULL DEFAULT 0,
    is_pko INTEGER NOT NULL DEFAULT 0,
    is_builtin INTEGER NOT NULL DEFAULT 0,
    created_at INTEGER NOT NULL DEFAULT (unixepoch()),
    updated_at INTEGER NOT NULL DEFAULT (unixepoch())
);

-- =============================================================================
-- Tournaments (runtime instances)
-- =============================================================================

CREATE TABLE IF NOT EXISTS tournaments (
    id TEXT PRIMARY KEY,  -- UUID
    name TEXT NOT NULL,
    template_id INTEGER REFERENCES tournament_templates(id),  -- nullable, reference only

    -- Copied config (local changes don't affect template)
    chip_set_id INTEGER NOT NULL REFERENCES chip_sets(id),
    blind_structure_id INTEGER NOT NULL REFERENCES blind_structures(id),
    payout_structure_id INTEGER NOT NULL REFERENCES payout_structures(id),
    buyin_config_id INTEGER NOT NULL REFERENCES buyin_configs(id),
    points_formula_id INTEGER REFERENCES points_formulas(id),  -- nullable

    status TEXT NOT NULL DEFAULT 'created' CHECK (status IN (
        'created', 'registering', 'running', 'paused',
        'final_table', 'completed', 'cancelled'
    )),

    min_players INTEGER NOT NULL DEFAULT 2,
    max_players INTEGER,  -- nullable
    early_signup_bonus_chips INTEGER NOT NULL DEFAULT 0,
    early_signup_cutoff TEXT,  -- nullable
    punctuality_bonus_chips INTEGER NOT NULL DEFAULT 0,
    is_pko INTEGER NOT NULL DEFAULT 0,

    -- Runtime state
    current_level INTEGER NOT NULL DEFAULT 0,
    clock_state TEXT NOT NULL DEFAULT 'stopped' CHECK (clock_state IN ('stopped', 'running', 'paused')),
    clock_remaining_ns INTEGER NOT NULL DEFAULT 0,
    total_elapsed_ns INTEGER NOT NULL DEFAULT 0,
    hand_for_hand INTEGER NOT NULL DEFAULT 0,

    started_at INTEGER,   -- nullable
    ended_at INTEGER,     -- nullable
    created_at INTEGER NOT NULL DEFAULT (unixepoch()),
    updated_at INTEGER NOT NULL DEFAULT (unixepoch())
);

-- =============================================================================
-- Players
-- =============================================================================

CREATE TABLE IF NOT EXISTS players (
    id TEXT PRIMARY KEY,  -- UUID
    name TEXT NOT NULL,
    nickname TEXT,
    email TEXT,
    phone TEXT,
    photo_url TEXT,
    notes TEXT,
    custom_fields TEXT,  -- JSON
    created_at INTEGER NOT NULL DEFAULT (unixepoch()),
    updated_at INTEGER NOT NULL DEFAULT (unixepoch())
);

CREATE TABLE IF NOT EXISTS tournament_players (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    tournament_id TEXT NOT NULL REFERENCES tournaments(id) ON DELETE CASCADE,
    player_id TEXT NOT NULL REFERENCES players(id),
    status TEXT NOT NULL DEFAULT 'registered' CHECK (status IN (
        'registered', 'active', 'busted', 'deal'
    )),

    seat_table_id INTEGER REFERENCES tables(id),   -- nullable
    seat_position INTEGER,                          -- nullable

    buy_in_at INTEGER,          -- nullable
    bust_out_at INTEGER,        -- nullable
    bust_out_order INTEGER,     -- nullable, position when busted
    finishing_position INTEGER, -- nullable, final position

    current_chips INTEGER NOT NULL DEFAULT 0,
    rebuys INTEGER NOT NULL DEFAULT 0,
    addons INTEGER NOT NULL DEFAULT 0,
    reentries INTEGER NOT NULL DEFAULT 0,
    bounty_value INTEGER NOT NULL DEFAULT 0,       -- cents, for PKO
    bounties_collected INTEGER NOT NULL DEFAULT 0,
    prize_amount INTEGER NOT NULL DEFAULT 0,       -- cents
    points_awarded INTEGER NOT NULL DEFAULT 0,

    early_signup_bonus_applied INTEGER NOT NULL DEFAULT 0,
    punctuality_bonus_applied INTEGER NOT NULL DEFAULT 0,
    hitman_player_id TEXT REFERENCES players(id),  -- nullable, who busted them

    created_at INTEGER NOT NULL DEFAULT (unixepoch()),
    updated_at INTEGER NOT NULL DEFAULT (unixepoch()),

    UNIQUE(tournament_id, player_id)
);

-- =============================================================================
-- Tables & Seating
-- =============================================================================

CREATE TABLE IF NOT EXISTS tables (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    tournament_id TEXT NOT NULL REFERENCES tournaments(id) ON DELETE CASCADE,
    name TEXT NOT NULL,
    seat_count INTEGER NOT NULL DEFAULT 9 CHECK (seat_count >= 6 AND seat_count <= 10),
    dealer_button_position INTEGER,  -- nullable
    is_active INTEGER NOT NULL DEFAULT 1,
    created_at INTEGER NOT NULL DEFAULT (unixepoch()),
    updated_at INTEGER NOT NULL DEFAULT (unixepoch())
);

CREATE TABLE IF NOT EXISTS table_blueprints (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    table_configs TEXT NOT NULL DEFAULT '[]',  -- JSON array of {name, seat_count}
    created_at INTEGER NOT NULL DEFAULT (unixepoch()),
    updated_at INTEGER NOT NULL DEFAULT (unixepoch())
);

CREATE TABLE IF NOT EXISTS balance_suggestions (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    tournament_id TEXT NOT NULL REFERENCES tournaments(id) ON DELETE CASCADE,
    status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN (
        'pending', 'accepted', 'cancelled', 'expired'
    )),
    from_table_id INTEGER NOT NULL REFERENCES tables(id),
    to_table_id INTEGER NOT NULL REFERENCES tables(id),
    player_id TEXT REFERENCES players(id),    -- nullable
    from_seat INTEGER,                        -- nullable
    to_seat INTEGER,                          -- nullable
    reason TEXT NOT NULL DEFAULT '',
    created_at INTEGER NOT NULL DEFAULT (unixepoch()),
    resolved_at INTEGER                       -- nullable
);

-- =============================================================================
-- Financial Transactions
-- =============================================================================

CREATE TABLE IF NOT EXISTS transactions (
    id TEXT PRIMARY KEY,  -- UUID
    tournament_id TEXT NOT NULL REFERENCES tournaments(id) ON DELETE CASCADE,
    player_id TEXT NOT NULL REFERENCES players(id),
    type TEXT NOT NULL CHECK (type IN (
        'buyin', 'rebuy', 'addon', 'reentry',
        'bounty_collected', 'bounty_paid',
        'payout', 'rake', 'chop', 'bubble_prize'
    )),
    amount INTEGER NOT NULL DEFAULT 0,  -- cents
    chips INTEGER NOT NULL DEFAULT 0,   -- chips given/removed
    operator_id TEXT NOT NULL,          -- who performed the action
    receipt_data TEXT,                  -- nullable, JSON
    undone INTEGER NOT NULL DEFAULT 0,
    undone_by TEXT,                     -- nullable, FK audit_entries.id
    metadata TEXT,                     -- nullable, JSON (bounty chain, chop details, etc)
    created_at INTEGER NOT NULL DEFAULT (unixepoch())
);

CREATE TABLE IF NOT EXISTS bubble_prizes (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    tournament_id TEXT NOT NULL REFERENCES tournaments(id) ON DELETE CASCADE,
    amount INTEGER NOT NULL DEFAULT 0,  -- cents
    funded_from TEXT NOT NULL DEFAULT '[]',  -- JSON array of {position, reduction_amount}
    status TEXT NOT NULL DEFAULT 'proposed' CHECK (status IN (
        'proposed', 'confirmed', 'cancelled'
    )),
    created_at INTEGER NOT NULL DEFAULT (unixepoch())
);

-- =============================================================================
-- Audit Trail (append-only)
-- =============================================================================

CREATE TABLE IF NOT EXISTS audit_entries (
    id TEXT PRIMARY KEY,  -- UUID
    tournament_id TEXT,   -- nullable, some are venue-level
    timestamp INTEGER NOT NULL DEFAULT (unixepoch()),  -- UnixNano in Go, epoch seconds in SQL default
    operator_id TEXT NOT NULL,
    action TEXT NOT NULL,  -- e.g. 'player.bust', 'financial.buyin', 'clock.pause', 'seat.move'
    target_type TEXT NOT NULL DEFAULT '',
    target_id TEXT NOT NULL DEFAULT '',
    previous_state TEXT,  -- JSON
    new_state TEXT,       -- JSON
    metadata TEXT,        -- nullable, JSON
    undone_by TEXT         -- nullable, references audit_entries.id
    -- NO UPDATE OR DELETE -- enforced by triggers below
);

-- Tamper protection: REJECT any UPDATE except setting undone_by on a row where undone_by is currently NULL
CREATE TRIGGER IF NOT EXISTS audit_entries_no_update
BEFORE UPDATE ON audit_entries
WHEN OLD.undone_by IS NOT NULL OR NEW.undone_by IS NULL
   OR OLD.id != NEW.id OR OLD.timestamp != NEW.timestamp
   OR OLD.operator_id != NEW.operator_id OR OLD.action != NEW.action
BEGIN
    SELECT RAISE(ABORT, 'audit_entries is append-only: only undone_by may be set once');
END;

-- Tamper protection: REJECT any DELETE on audit_entries entirely
CREATE TRIGGER IF NOT EXISTS audit_entries_no_delete
BEFORE DELETE ON audit_entries
BEGIN
    SELECT RAISE(ABORT, 'audit_entries is append-only: deletion is prohibited');
END;

-- =============================================================================
-- Operators
-- =============================================================================

CREATE TABLE IF NOT EXISTS operators (
    id TEXT PRIMARY KEY,  -- UUID
    name TEXT NOT NULL,
    pin_hash TEXT NOT NULL,  -- bcrypt
    role TEXT NOT NULL DEFAULT 'floor' CHECK (role IN ('admin', 'floor', 'viewer')),
    created_at INTEGER NOT NULL DEFAULT (unixepoch()),
    updated_at INTEGER NOT NULL DEFAULT (unixepoch())
);

-- =============================================================================
-- Indexes
-- =============================================================================

-- Tournament players: active player lookups and rankings
CREATE INDEX IF NOT EXISTS idx_tournament_players_tournament_status
    ON tournament_players(tournament_id, status);

CREATE INDEX IF NOT EXISTS idx_tournament_players_tournament_bust_order
    ON tournament_players(tournament_id, bust_out_order);

-- Transactions: financial summaries and player history
CREATE INDEX IF NOT EXISTS idx_transactions_tournament_type
    ON transactions(tournament_id, type);

CREATE INDEX IF NOT EXISTS idx_transactions_tournament_player
    ON transactions(tournament_id, player_id);

-- Audit entries: log browsing and action filtering
CREATE INDEX IF NOT EXISTS idx_audit_entries_tournament_timestamp
    ON audit_entries(tournament_id, timestamp);

CREATE INDEX IF NOT EXISTS idx_audit_entries_action
    ON audit_entries(action);

-- Tables: active table lookups per tournament
CREATE INDEX IF NOT EXISTS idx_tables_tournament_active
    ON tables(tournament_id, is_active);

-- Foreign key indexes on child tables
CREATE INDEX IF NOT EXISTS idx_chip_denominations_chip_set
    ON chip_denominations(chip_set_id);

CREATE INDEX IF NOT EXISTS idx_blind_levels_structure
    ON blind_levels(structure_id);

CREATE INDEX IF NOT EXISTS idx_payout_brackets_structure
    ON payout_brackets(structure_id);

CREATE INDEX IF NOT EXISTS idx_payout_tiers_bracket
    ON payout_tiers(bracket_id);

CREATE INDEX IF NOT EXISTS idx_rake_splits_buyin_config
    ON rake_splits(buyin_config_id);

CREATE INDEX IF NOT EXISTS idx_balance_suggestions_tournament
    ON balance_suggestions(tournament_id);

CREATE INDEX IF NOT EXISTS idx_bubble_prizes_tournament
    ON bubble_prizes(tournament_id);

CREATE INDEX IF NOT EXISTS idx_tournament_players_player
    ON tournament_players(player_id);
