-- Polymodel SQLite projection schema.
-- Derived views projected from Hydrant ATProto events for space.polymodel.* collections.
--
-- NOTE: WAL mode is intentionally NOT set here. sqlx runs migrations inside a
-- transaction and SQLite silently ignores journal_mode changes within one.
-- WAL is enabled programmatically on the connection pool (see src/indexing/db.rs).

-- Projection state (durable cursor for crash recovery)
CREATE TABLE projection_state (
    key TEXT PRIMARY KEY,
    value TEXT NOT NULL,
    updated_at INTEGER NOT NULL
);

-- Derived content views
CREATE TABLE things (
    did TEXT NOT NULL,
    rkey TEXT NOT NULL,
    uri TEXT NOT NULL UNIQUE,
    cid TEXT NOT NULL,
    name TEXT NOT NULL,
    summary TEXT,
    license TEXT NOT NULL,
    tags_json TEXT,
    tags_text TEXT,
    instructions_text TEXT,
    cover_json TEXT,
    derived_from_uri TEXT,
    created_at INTEGER NOT NULL,
    indexed_at INTEGER NOT NULL,
    PRIMARY KEY (did, rkey)
);
CREATE INDEX idx_things_did ON things(did, created_at DESC);
CREATE INDEX idx_things_created ON things(created_at DESC);

CREATE TABLE models (
    did TEXT NOT NULL,
    rkey TEXT NOT NULL,
    uri TEXT NOT NULL UNIQUE,
    cid TEXT NOT NULL,
    name TEXT NOT NULL,
    summary TEXT,
    created_at INTEGER NOT NULL,
    indexed_at INTEGER NOT NULL,
    PRIMARY KEY (did, rkey)
);

CREATE TABLE parts (
    did TEXT NOT NULL,
    rkey TEXT NOT NULL,
    uri TEXT NOT NULL UNIQUE,
    cid TEXT NOT NULL,
    name TEXT NOT NULL,
    format TEXT,
    file_json TEXT NOT NULL,
    created_at INTEGER NOT NULL,
    indexed_at INTEGER NOT NULL,
    PRIMARY KEY (did, rkey)
);

-- Junction tables (relationship lookups + ordering)
CREATE TABLE thing_models (
    thing_uri TEXT NOT NULL,
    model_uri TEXT NOT NULL,
    position INTEGER NOT NULL,
    PRIMARY KEY (thing_uri, model_uri)
);
CREATE INDEX idx_thing_models_model ON thing_models(model_uri);
CREATE INDEX idx_thing_models_thing_pos ON thing_models(thing_uri, position);

CREATE TABLE model_parts (
    model_uri TEXT NOT NULL,
    part_uri TEXT NOT NULL,
    position INTEGER NOT NULL,
    PRIMARY KEY (model_uri, part_uri)
);
CREATE INDEX idx_model_parts_part ON model_parts(part_uri);
CREATE INDEX idx_model_parts_model_pos ON model_parts(model_uri, position);

-- Social record projections
CREATE TABLE likes (
    did TEXT NOT NULL,
    rkey TEXT NOT NULL,
    subject_uri TEXT NOT NULL,
    created_at INTEGER NOT NULL,
    PRIMARY KEY (did, rkey),
    UNIQUE (did, subject_uri)
);
CREATE INDEX idx_likes_subject ON likes(subject_uri);

CREATE TABLE saves (
    did TEXT NOT NULL,
    rkey TEXT NOT NULL,
    subject_uri TEXT NOT NULL,
    note TEXT,
    created_at INTEGER NOT NULL,
    PRIMARY KEY (did, rkey),
    UNIQUE (did, subject_uri)
);
CREATE INDEX idx_saves_subject ON saves(subject_uri);

CREATE TABLE tags (
    did TEXT NOT NULL,
    rkey TEXT NOT NULL,
    subject_uri TEXT NOT NULL,
    tag TEXT NOT NULL,
    created_at INTEGER NOT NULL,
    PRIMARY KEY (did, rkey)
);
CREATE INDEX idx_tags_subject ON tags(subject_uri);
CREATE INDEX idx_tags_tag ON tags(tag);

CREATE TABLE listitems (
    did TEXT NOT NULL,
    rkey TEXT NOT NULL,
    list_uri TEXT NOT NULL,
    subject_uri TEXT NOT NULL,
    created_at INTEGER NOT NULL,
    PRIMARY KEY (did, rkey)
);
CREATE INDEX idx_listitems_list ON listitems(list_uri, created_at);

-- Denormalized counts
CREATE TABLE content_stats (
    uri TEXT PRIMARY KEY,
    like_count INTEGER NOT NULL DEFAULT 0,
    save_count INTEGER NOT NULL DEFAULT 0,
    tag_count INTEGER NOT NULL DEFAULT 0
);

-- Full-text search (FTS5, external content)
CREATE VIRTUAL TABLE things_fts USING fts5(
    name, summary, tags_text, instructions_text,
    content=things, content_rowid=rowid,
    tokenize='porter unicode61'
);

CREATE TRIGGER things_ai AFTER INSERT ON things BEGIN
    INSERT INTO things_fts(rowid, name, summary, tags_text, instructions_text)
    VALUES (new.rowid, new.name, new.summary, new.tags_text, new.instructions_text);
END;
CREATE TRIGGER things_ad AFTER DELETE ON things BEGIN
    INSERT INTO things_fts(things_fts, rowid, name, summary, tags_text, instructions_text)
    VALUES ('delete', old.rowid, old.name, old.summary, old.tags_text, old.instructions_text);
END;
CREATE TRIGGER things_au AFTER UPDATE ON things BEGIN
    INSERT INTO things_fts(things_fts, rowid, name, summary, tags_text, instructions_text)
    VALUES ('delete', old.rowid, old.name, old.summary, old.tags_text, old.instructions_text);
    INSERT INTO things_fts(rowid, name, summary, tags_text, instructions_text)
    VALUES (new.rowid, new.name, new.summary, new.tags_text, new.instructions_text);
END;

-- OAuth session persistence (DB-backed ClientAuthStore for Jacquard 0.12 OAuth).
--
-- Each row mirrors the durable surfaces of `jacquard::oauth::session::ClientSessionData`:
-- the structured columns (account_did, session_id, endpoints, timestamps) power
-- key enumeration, by-DID/any selection, and deletes *without* deserializing the
-- payload, while `payload` holds the canonical JSON serialization of the full
-- `ClientSessionData` (token set, DPoP key + nonces, scopes). DPoP nonces are
-- persisted here, not in an ad-hoc store; the in-process `SessionRegistry`
-- mutex is concurrency-only and not durable.
CREATE TABLE oauth_sessions (
    account_did TEXT NOT NULL,
    session_id TEXT NOT NULL,
    host_url TEXT NOT NULL,
    authserver_url TEXT NOT NULL,
    token_endpoint TEXT NOT NULL,
    revocation_endpoint TEXT,
    created_at INTEGER NOT NULL,
    updated_at INTEGER NOT NULL,
    payload TEXT NOT NULL,
    PRIMARY KEY (account_did, session_id)
);
CREATE INDEX idx_oauth_sessions_did ON oauth_sessions(account_did);

-- Pending OAuth authorization requests (PAR `state` round-trip).
-- `payload` holds the canonical JSON serialization of
-- `jacquard::oauth::session::AuthRequestData` (PKCE verifier, DPoP key + nonce,
-- request_uri). `account_did` is nullable because the initiating identifier is
-- not always known when the request is created.
CREATE TABLE oauth_auth_requests (
    state TEXT NOT NULL,
    authserver_url TEXT NOT NULL,
    account_did TEXT,
    request_uri TEXT NOT NULL,
    token_endpoint TEXT NOT NULL,
    revocation_endpoint TEXT,
    created_at INTEGER NOT NULL,
    payload TEXT NOT NULL,
    PRIMARY KEY (state)
);
CREATE INDEX idx_oauth_auth_req_did ON oauth_auth_requests(account_did);
