-- Sidelinja: Initial database schema
-- Kunnskapsgraf, jobbkø, meldinger, mediefiler
--
-- Kjøres mot en tom PostgreSQL-database.
-- Krever: PostgreSQL 15+ med pgcrypto (gen_random_uuid)

BEGIN;

-- ============================================================
-- Extensions
-- ============================================================

CREATE EXTENSION IF NOT EXISTS "pgcrypto";   -- gen_random_uuid()
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";  -- uuid_generate_v5() for deterministiske segment-UUIDs

-- ============================================================
-- 1. Brukere (tynn cache fra Authentik)
-- ============================================================

CREATE TABLE users (
    authentik_id  TEXT PRIMARY KEY,
    display_name  TEXT NOT NULL,
    avatar_url    TEXT,
    cached_at     TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- ============================================================
-- 2. Nodes — supertabell for alle grafentiteter
-- ============================================================

CREATE TYPE node_type AS ENUM (
    'tema', 'aktør', 'faktoide', 'episode', 'segment', 'melding'
);

CREATE TABLE nodes (
    id         UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    node_type  node_type NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- ============================================================
-- 3. Relasjonstyper (seeded + utvidbar)
-- ============================================================

CREATE TABLE relation_types (
    name        TEXT PRIMARY KEY,
    label       TEXT NOT NULL,
    description TEXT,
    system      BOOLEAN NOT NULL DEFAULT false
);

INSERT INTO relation_types (name, label, description, system) VALUES
    ('MENTIONS',     'Nevner',       'Refererer til denne aktøren/temaet',    true),
    ('ABOUT',        'Handler om',   'Faktoide eller klipp som omhandler',   true),
    ('DISCUSSED_IN', 'Diskutert i',  'Tema diskutert i dette segmentet',     true),
    ('WORKS_FOR',    'Jobber for',   'Person tilknyttet organisasjon',       true),
    ('CONTRADICTS',  'Motsier',      'Står i motsetning til',                true),
    ('PART_OF',      'Del av',       'Tilhører / er underordnet',            true);

-- ============================================================
-- 4. Graph edges
-- ============================================================

CREATE TABLE graph_edges (
    id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    source_id     UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
    target_id     UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
    relation_type TEXT NOT NULL REFERENCES relation_types(name),
    context_id    UUID REFERENCES nodes(id) ON DELETE SET NULL,
    confidence    REAL CHECK (confidence BETWEEN 0.0 AND 1.0),
    created_by    TEXT REFERENCES users(authentik_id) ON DELETE SET NULL,
    origin        TEXT NOT NULL DEFAULT 'system',
    created_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
    CONSTRAINT no_self_reference CHECK (source_id != target_id)
);

CREATE INDEX idx_edges_source   ON graph_edges(source_id);
CREATE INDEX idx_edges_target   ON graph_edges(target_id);
CREATE INDEX idx_edges_relation ON graph_edges(relation_type);

-- ============================================================
-- 5. Detailtabeller — aktører, temaer, episoder, segmenter
-- ============================================================

CREATE TABLE actors (
    id   UUID PRIMARY KEY REFERENCES nodes(id) ON DELETE CASCADE,
    name TEXT NOT NULL,
    type TEXT  -- 'person', 'organisasjon', etc.
);

CREATE TABLE topics (
    id   UUID PRIMARY KEY REFERENCES nodes(id) ON DELETE CASCADE,
    name TEXT NOT NULL
);

CREATE TABLE episodes (
    id           UUID PRIMARY KEY REFERENCES nodes(id) ON DELETE CASCADE,
    title        TEXT NOT NULL,
    published_at TIMESTAMPTZ,
    guid         TEXT UNIQUE NOT NULL
);

CREATE TABLE segments (
    id         UUID PRIMARY KEY REFERENCES nodes(id) ON DELETE CASCADE,
    episode_id UUID NOT NULL REFERENCES episodes(id) ON DELETE CASCADE,
    start_time INTERVAL NOT NULL,
    end_time   INTERVAL NOT NULL,
    transcript TEXT,
    CONSTRAINT valid_timerange CHECK (end_time > start_time)
);

CREATE INDEX idx_segments_episode ON segments(episode_id);
CREATE INDEX idx_segments_fts     ON segments USING GIN (to_tsvector('norwegian', transcript));

-- ============================================================
-- 6. Faktoider
-- ============================================================

CREATE TABLE factoids (
    id         UUID PRIMARY KEY REFERENCES nodes(id) ON DELETE CASCADE,
    body       TEXT NOT NULL,
    source_url TEXT,
    created_by TEXT NOT NULL REFERENCES users(authentik_id) ON DELETE SET NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE factoid_votes (
    factoid_id UUID NOT NULL REFERENCES factoids(id) ON DELETE CASCADE,
    user_id    TEXT NOT NULL REFERENCES users(authentik_id) ON DELETE CASCADE,
    vote       SMALLINT NOT NULL CHECK (vote IN (-1, 1)),
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    PRIMARY KEY (factoid_id, user_id)
);

-- ============================================================
-- 7. Meldinger (chat)
-- ============================================================

CREATE TYPE message_type AS ENUM (
    'text',            -- Vanlig Markdown-melding
    'research_clip',   -- Innlimt artikkel (Ctrl+A)
    'factoid',         -- Faktoid delt i chat
    'system'           -- Automatisk systemmelding
);

CREATE TABLE messages (
    id           UUID PRIMARY KEY REFERENCES nodes(id) ON DELETE CASCADE,
    topic_id     UUID NOT NULL REFERENCES topics(id) ON DELETE CASCADE,
    reply_to     UUID REFERENCES messages(id) ON DELETE SET NULL,
    author_id    TEXT NOT NULL REFERENCES users(authentik_id) ON DELETE SET NULL,
    message_type message_type NOT NULL DEFAULT 'text',
    body         TEXT NOT NULL,
    metadata     JSONB,
    edited_at    TIMESTAMPTZ,
    created_at   TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX idx_messages_topic ON messages(topic_id, created_at);

CREATE TABLE message_revisions (
    id         UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    message_id UUID NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
    body       TEXT NOT NULL,
    edited_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    CONSTRAINT revision_order UNIQUE (message_id, edited_at)
);

CREATE TABLE message_votes (
    message_id UUID NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
    user_id    TEXT NOT NULL REFERENCES users(authentik_id) ON DELETE CASCADE,
    vote       SMALLINT NOT NULL CHECK (vote IN (-1, 1)),
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    PRIMARY KEY (message_id, user_id)
);

-- ============================================================
-- 8. Mediefiler (content-addressable)
-- ============================================================

CREATE TABLE media_files (
    sha256      TEXT PRIMARY KEY,
    mime_type   TEXT NOT NULL,
    file_size   BIGINT NOT NULL,
    uploaded_by TEXT NOT NULL REFERENCES users(authentik_id) ON DELETE SET NULL,
    uploaded_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE message_attachments (
    id         UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    message_id UUID NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
    sha256     TEXT NOT NULL REFERENCES media_files(sha256),
    sort_order SMALLINT NOT NULL DEFAULT 0
);

-- ============================================================
-- 9. Jobbkø
-- ============================================================

CREATE TYPE job_status AS ENUM ('pending', 'running', 'completed', 'error', 'retry');

CREATE TABLE job_queue (
    id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    job_type      TEXT NOT NULL,
    payload       JSONB NOT NULL,
    status        job_status NOT NULL DEFAULT 'pending',
    priority      SMALLINT NOT NULL DEFAULT 0,
    result        JSONB,
    error_msg     TEXT,
    attempts      SMALLINT NOT NULL DEFAULT 0,
    max_attempts  SMALLINT NOT NULL DEFAULT 3,
    created_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
    started_at    TIMESTAMPTZ,
    completed_at  TIMESTAMPTZ,
    scheduled_for TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX idx_job_queue_pending ON job_queue (priority DESC, scheduled_for ASC)
    WHERE status IN ('pending', 'retry');

COMMIT;
