-- Requires: CREATE EXTENSION IF NOT EXISTS vector; CREATE EXTENSION IF NOT EXISTS ltree;

CREATE SCHEMA IF NOT EXISTS komp;
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS ltree;

CREATE TABLE IF NOT EXISTS komp.source (
  id         BIGSERIAL PRIMARY KEY,
  kind       TEXT NOT NULL,       -- filesystem|repo|url|note
  uri        TEXT NOT NULL,       -- path or URL
  repo       TEXT,
  ref        TEXT,
  meta       JSONB DEFAULT '{}'::jsonb,
  created_at TIMESTAMPTZ DEFAULT now()
);

CREATE TABLE IF NOT EXISTS komp.chunk (
  id         BIGSERIAL PRIMARY KEY,
  source_id  BIGINT REFERENCES komp.source(id) ON DELETE CASCADE,
  lineno     INT,
  text       TEXT NOT NULL,
  sha256     TEXT NOT NULL,
  tokens     INT,
  created_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_chunk_source ON komp.chunk(source_id);

-- A space is a distinct memory with its own model+dim & policy
CREATE TABLE IF NOT EXISTS komp.space (
  id     SERIAL PRIMARY KEY,
  name   TEXT UNIQUE,   -- dev_knowledge | pattern_exchange | runtime_memory
  model  TEXT NOT NULL,
  dim    INT  NOT NULL,
  metric TEXT NOT NULL DEFAULT 'cosine'
);

-- Embedding tables per common dimension (add more as needed)
CREATE TABLE IF NOT EXISTS komp.embedding_768 (
  id           BIGSERIAL PRIMARY KEY,
  chunk_id     BIGINT REFERENCES komp.chunk(id) ON DELETE CASCADE,
  space_id     INT REFERENCES komp.space(id) ON DELETE CASCADE,
  embedding    VECTOR(768) NOT NULL,
  created_at   TIMESTAMPTZ DEFAULT now(),
  UNIQUE(chunk_id, space_id)
);
CREATE INDEX IF NOT EXISTS idx_embed768_space ON komp.embedding_768(space_id);
CREATE INDEX IF NOT EXISTS ivf_embed768 ON komp.embedding_768 USING ivfflat (embedding vector_cosine_ops) WITH (lists=100);

CREATE TABLE IF NOT EXISTS komp.embedding_1024 (
  id           BIGSERIAL PRIMARY KEY,
  chunk_id     BIGINT REFERENCES komp.chunk(id) ON DELETE CASCADE,
  space_id     INT REFERENCES komp.space(id) ON DELETE CASCADE,
  embedding    VECTOR(1024) NOT NULL,
  created_at   TIMESTAMPTZ DEFAULT now(),
  UNIQUE(chunk_id, space_id)
);
CREATE INDEX IF NOT EXISTS idx_embed1024_space ON komp.embedding_1024(space_id);
CREATE INDEX IF NOT EXISTS ivf_embed1024 ON komp.embedding_1024 USING ivfflat (embedding vector_cosine_ops) WITH (lists=100);

-- Branch hierarchy (Branch Embeddings): path encodes the cluster tree (e.g., physics.quantum.tunneling)
CREATE TABLE IF NOT EXISTS komp.branch (
  id       BIGSERIAL PRIMARY KEY,
  space_id INT REFERENCES komp.space(id) ON DELETE CASCADE,
  path     LTREE NOT NULL,
  label    TEXT,
  meta     JSONB DEFAULT '{}'::jsonb,
  UNIQUE(space_id, path)
);
CREATE INDEX IF NOT EXISTS gist_branch_path ON komp.branch USING GIST (path);

-- Centroids per dimension (store only the dim matching the space)
CREATE TABLE IF NOT EXISTS komp.branch_centroid_768 (
  branch_id  BIGINT PRIMARY KEY REFERENCES komp.branch(id) ON DELETE CASCADE,
  embedding  VECTOR(768) NOT NULL,
  updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS komp.branch_centroid_1024 (
  branch_id  BIGINT PRIMARY KEY REFERENCES komp.branch(id) ON DELETE CASCADE,
  embedding  VECTOR(1024) NOT NULL,
  updated_at TIMESTAMPTZ DEFAULT now()
);

-- Soft membership of chunks to branches
CREATE TABLE IF NOT EXISTS komp.chunk_branch (
  chunk_id  BIGINT REFERENCES komp.chunk(id) ON DELETE CASCADE,
  branch_id BIGINT REFERENCES komp.branch(id) ON DELETE CASCADE,
  weight    REAL NOT NULL CHECK (weight >= 0 AND weight <= 1),
  PRIMARY KEY(chunk_id, branch_id)
);

-- Relations between chunks (similarity / cites / derives / contradicts / …)
CREATE TABLE IF NOT EXISTS komp.chunk_edge (
  src_chunk_id BIGINT REFERENCES komp.chunk(id) ON DELETE CASCADE,
  dst_chunk_id BIGINT REFERENCES komp.chunk(id) ON DELETE CASCADE,
  relation     TEXT NOT NULL,
  weight       REAL,
  meta         JSONB DEFAULT '{}'::jsonb,
  PRIMARY KEY(src_chunk_id, dst_chunk_id, relation)
);

CREATE OR REPLACE VIEW komp.latest_sources AS
  SELECT s.*, max(c.created_at) AS last_chunk_at
  FROM komp.source s LEFT JOIN komp.chunk c ON c.source_id = s.id
  GROUP BY s.id;

