-- ============================================================================
-- IT Knowledge Base Pipeline — Postgres schema
-- Implements Storage Layer (C) from the design doc: 1 database instead of
-- Postgres + Qdrant + Elasticsearch + Neo4j + Redis/Celery.
-- ============================================================================

CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS pgcrypto;   -- gen_random_uuid()

-- ----------------------------------------------------------------------------
-- A3.5 — section content hashes (change detection between ingest runs)
-- ----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS section_hashes (
    source_id     TEXT NOT NULL,
    section_id    TEXT NOT NULL,
    content_hash  TEXT NOT NULL,
    updated_at    TIMESTAMP DEFAULT now(),
    PRIMARY KEY (source_id, section_id)
);

-- ----------------------------------------------------------------------------
-- A4 — job queue, replaces Celery + Redis
-- ----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS section_jobs (
    id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    batch_id      UUID NOT NULL,
    source_id     TEXT NOT NULL,
    section_id    TEXT NOT NULL,
    section_text  TEXT NOT NULL,
    status        TEXT NOT NULL DEFAULT 'pending',  -- pending|processing|done|manual_review|failed
    attempt       INT NOT NULL DEFAULT 0,
    max_attempts  INT NOT NULL DEFAULT 3,
    llm_tier      TEXT NOT NULL DEFAULT 'light',     -- light|strong
    next_run_at   TIMESTAMP NOT NULL DEFAULT now(),
    last_error    TEXT,
    created_at    TIMESTAMP DEFAULT now(),
    updated_at    TIMESTAMP DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_section_jobs_poll
    ON section_jobs (status, next_run_at);

-- ----------------------------------------------------------------------------
-- Knowledge atoms == verified elements after A4 (+ dedup B1 + embeddings B2)
-- ----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS knowledge_atoms (
    id                TEXT PRIMARY KEY,           -- stable element id
    source_id         TEXT NOT NULL,
    section_id        TEXT NOT NULL,
    chapter           TEXT,
    page_start        INT,
    label             TEXT,                       -- FACT|DEFINITION|CONSTRAINT|EXAMPLE|WARNING|OTHER
    raw               TEXT NOT NULL,               -- verbatim substring from source (A4 verification)
    normalized        TEXT,                        -- LLM-normalized/paraphrased form
    confidence        REAL,
    contradicts_flag  BOOLEAN DEFAULT FALSE,
    embedding         vector(768),
    search_vector     tsvector,
    created_at        TIMESTAMP DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_atoms_embedding
    ON knowledge_atoms USING hnsw (embedding vector_cosine_ops);
CREATE INDEX IF NOT EXISTS idx_atoms_search
    ON knowledge_atoms USING gin (search_vector);
CREATE INDEX IF NOT EXISTS idx_atoms_source_section
    ON knowledge_atoms (source_id, section_id);

CREATE OR REPLACE FUNCTION knowledge_atoms_search_vector_trigger() RETURNS trigger AS $$
BEGIN
  NEW.search_vector := to_tsvector('english', coalesce(NEW.normalized, NEW.raw, ''));
  RETURN NEW;
END
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS trg_atoms_search_vector ON knowledge_atoms;
CREATE TRIGGER trg_atoms_search_vector
BEFORE INSERT OR UPDATE ON knowledge_atoms
FOR EACH ROW EXECUTE FUNCTION knowledge_atoms_search_vector_trigger();

-- ----------------------------------------------------------------------------
-- B2 — Knowledge Graph (replaces Neo4j)
-- ----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS kg_nodes (
    id    TEXT PRIMARY KEY,
    label TEXT,
    type  TEXT   -- entity|atom
);

CREATE TABLE IF NOT EXISTS kg_edges (
    id           UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    source_id    TEXT REFERENCES kg_nodes(id),
    target_id    TEXT REFERENCES kg_nodes(id),
    relation     TEXT NOT NULL,       -- DEFINES|EXTENDS|CONTRADICTS|...
    evidence_raw TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_edges_source ON kg_edges(source_id);
CREATE INDEX IF NOT EXISTS idx_edges_target ON kg_edges(target_id);

-- ----------------------------------------------------------------------------
-- B2 — chunk records (parent/child, late chunking)
-- ----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS chunk_records (
    id         TEXT PRIMARY KEY,
    type       TEXT NOT NULL,          -- parent|child
    content    TEXT NOT NULL,
    parent_id  TEXT REFERENCES chunk_records(id),
    embedding  vector(768),
    metadata   JSONB
);
CREATE INDEX IF NOT EXISTS idx_chunk_embedding
    ON chunk_records USING hnsw (embedding vector_cosine_ops);

-- ----------------------------------------------------------------------------
-- C — batch monitoring (replaces Sentry + Flower)
-- ----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS batch_runs (
    id                      UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    started_at              TIMESTAMP DEFAULT now(),
    finished_at             TIMESTAMP,
    total_documents         INT DEFAULT 0,
    total_sections          INT DEFAULT 0,
    failed_sections         INT DEFAULT 0,
    manual_review_sections  INT DEFAULT 0
);
