-- pzsort schema
-- Run as: psql -U pzsort -d pzsort -f schema.sql

CREATE EXTENSION IF NOT EXISTS pgcrypto;

-- -----------------------------------------------------------------------------
-- workshop_meta
-- Cheap, refreshed often via Steam ISteamRemoteStorage/GetPublishedFileDetails.
-- Keyed by Steam publishedfileid (text to avoid bigint surprises).
-- time_updated is the cache-invalidation key for mod_parsed.
-- -----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS workshop_meta (
    workshop_id          TEXT PRIMARY KEY,
    title                TEXT,
    description          TEXT,
    tags                 TEXT[]      NOT NULL DEFAULT '{}',
    creator_steamid      TEXT,
    time_created         BIGINT,                 -- unix ts from Steam
    time_updated         BIGINT      NOT NULL,   -- unix ts; cache invalidation key
    file_size            BIGINT,
    preview_url          TEXT,
    consumer_app_id      INTEGER,                -- 108600 for PZ
    visibility           INTEGER,                -- 0=public, 1=friends, 2=private
    banned               BOOLEAN     NOT NULL DEFAULT FALSE,
    last_checked_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
    created_at           TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS workshop_meta_last_checked_idx
    ON workshop_meta (last_checked_at);

-- -----------------------------------------------------------------------------
-- mod_parsed
-- Expensive: requires DepotDownloader fetch. Only refreshed when
-- workshop_meta.time_updated changes vs parsed_at_time_updated.
-- One workshop item can yield N rows (multi-mod packages).
-- -----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS mod_parsed (
    workshop_id              TEXT NOT NULL REFERENCES workshop_meta(workshop_id) ON DELETE CASCADE,
    mod_id                   TEXT NOT NULL,                -- mod.info `id=`
    name                     TEXT NOT NULL DEFAULT '',
    category                 TEXT NOT NULL DEFAULT 'undefined',
    requirements             TEXT[] NOT NULL DEFAULT '{}',
    load_after               TEXT[] NOT NULL DEFAULT '{}',
    load_before              TEXT[] NOT NULL DEFAULT '{}',
    incompatible_mods        TEXT[] NOT NULL DEFAULT '{}',
    load_first               TEXT NOT NULL DEFAULT 'off',
    load_last                TEXT NOT NULL DEFAULT 'off',
    tags                     TEXT[] NOT NULL DEFAULT '{}',
    maps                     TEXT[] NOT NULL DEFAULT '{}',  -- map folder names
    raw_mod_info             TEXT,                          -- original file for debugging
    version_min              TEXT,                          -- e.g. 41.55
    parsed_at_time_updated   BIGINT NOT NULL,               -- snapshot of workshop_meta.time_updated at parse
    parsed_at                TIMESTAMPTZ NOT NULL DEFAULT now(),
    PRIMARY KEY (workshop_id, mod_id)
);

CREATE INDEX IF NOT EXISTS mod_parsed_mod_id_idx ON mod_parsed (mod_id);

-- -----------------------------------------------------------------------------
-- download_jobs
-- Work queue for the DepotDownloader worker. One job per workshop_id.
-- Worker dequeues (status='queued') ORDER BY priority DESC, created_at ASC.
-- -----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS download_jobs (
    id           UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    workshop_id  TEXT NOT NULL,
    status       TEXT NOT NULL DEFAULT 'queued',  -- queued|downloading|parsing|done|failed
    priority     INTEGER NOT NULL DEFAULT 0,       -- higher first
    attempts     INTEGER NOT NULL DEFAULT 0,
    error        TEXT,
    requested_by TEXT,                              -- IP hash or user token; for rate limiting
    created_at   TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at   TIMESTAMPTZ NOT NULL DEFAULT now(),
    completed_at TIMESTAMPTZ
);

CREATE INDEX IF NOT EXISTS download_jobs_dequeue_idx
    ON download_jobs (status, priority DESC, created_at ASC)
    WHERE status = 'queued';

CREATE INDEX IF NOT EXISTS download_jobs_workshop_idx
    ON download_jobs (workshop_id);

-- Trigger: keep updated_at fresh
CREATE OR REPLACE FUNCTION touch_updated_at() RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = now();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS download_jobs_touch ON download_jobs;
CREATE TRIGGER download_jobs_touch
    BEFORE UPDATE ON download_jobs
    FOR EACH ROW
    EXECUTE FUNCTION touch_updated_at();

-- -----------------------------------------------------------------------------
-- collections
-- Cache for ISteamRemoteStorage/GetCollectionDetails results.
-- Collections expand to N child workshop_ids; we cache that mapping.
-- -----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS collections (
    collection_id        TEXT PRIMARY KEY,
    title                TEXT,
    child_workshop_ids   TEXT[] NOT NULL DEFAULT '{}',
    last_fetched_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- -----------------------------------------------------------------------------
-- sort_requests
-- Optional: log of submitted sort jobs for debugging + abuse triage.
-- Not required for sort to function. Keep TTL short via a cron.
-- -----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS sort_requests (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    input_workshop_ids  TEXT[] NOT NULL,
    input_collection_id TEXT,
    cache_hits      INTEGER NOT NULL DEFAULT 0,
    cache_misses    INTEGER NOT NULL DEFAULT 0,
    requested_by    TEXT,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS sort_requests_created_idx
    ON sort_requests (created_at);
