-- 001_initial_schema.sql
-- Oppretter kjerneskjema for Synops: enums, nodes, edges, node_access, auth_identities.
-- Ref: docs/primitiver/nodes.md, docs/primitiver/edges.md, docs/retninger/bruker_ikke_workspace.md

BEGIN;

-- =============================================================================
-- Enums
-- =============================================================================

CREATE TYPE visibility AS ENUM ('hidden', 'discoverable', 'readable', 'open');
CREATE TYPE access_level AS ENUM ('reader', 'member', 'admin', 'owner');

-- =============================================================================
-- Nodes
-- =============================================================================

CREATE TABLE nodes (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    node_kind   TEXT NOT NULL DEFAULT 'content',
    title       TEXT,
    content     TEXT,
    visibility  visibility NOT NULL DEFAULT 'hidden',
    metadata    JSONB NOT NULL DEFAULT '{}',
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    created_by  UUID REFERENCES nodes(id)
);

CREATE INDEX idx_nodes_kind ON nodes (node_kind);
CREATE INDEX idx_nodes_created_by ON nodes (created_by);
CREATE INDEX idx_nodes_visibility ON nodes (visibility);

-- =============================================================================
-- Edges
-- =============================================================================

CREATE TABLE 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,
    edge_type   TEXT NOT NULL,
    metadata    JSONB NOT NULL DEFAULT '{}',
    system      BOOLEAN NOT NULL DEFAULT false,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    created_by  UUID REFERENCES nodes(id),

    UNIQUE (source_id, target_id, edge_type)
);

CREATE INDEX idx_edges_source ON edges (source_id);
CREATE INDEX idx_edges_target ON edges (target_id);
CREATE INDEX idx_edges_type   ON edges (edge_type);

-- =============================================================================
-- Tilgangsmatrise (node_access)
-- =============================================================================

CREATE TABLE node_access (
    subject_id  UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
    object_id   UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
    access      access_level NOT NULL,
    via_edge    UUID REFERENCES edges(id) ON DELETE CASCADE,
    PRIMARY KEY (subject_id, object_id)
);

CREATE INDEX idx_na_subject ON node_access (subject_id);

-- =============================================================================
-- Auth-identiteter (bro mellom HTTP-sesjon og graf)
-- =============================================================================

CREATE TABLE auth_identities (
    node_id       UUID PRIMARY KEY REFERENCES nodes(id) ON DELETE CASCADE,
    authentik_sub TEXT UNIQUE NOT NULL,
    email         TEXT UNIQUE NOT NULL
);

-- =============================================================================
-- Tilgangsberegning (recompute_access)
-- Oppdaterer node_access-matrisen når edges endres.
-- Ref: docs/retninger/bruker_ikke_workspace.md
-- =============================================================================

CREATE OR REPLACE FUNCTION recompute_access(
    p_subject_id   UUID,
    p_root_node_id UUID,
    p_access       access_level,
    p_via_edge     UUID
) RETURNS void AS $$
BEGIN
    -- Direkte tilgang til roten
    INSERT INTO node_access (subject_id, object_id, access, via_edge)
    VALUES (p_subject_id, p_root_node_id, p_access, p_via_edge)
    ON CONFLICT (subject_id, object_id)
    DO UPDATE SET access = GREATEST(node_access.access, p_access);

    -- Transitiv: noder som tilhører roten
    INSERT INTO node_access (subject_id, object_id, access, via_edge)
    SELECT p_subject_id, e.source_id, p_access, p_via_edge
    FROM edges e
    WHERE e.target_id = p_root_node_id
      AND e.edge_type = 'belongs_to'
    ON CONFLICT (subject_id, object_id)
    DO UPDATE SET access = GREATEST(node_access.access, p_access);

    -- Hvis subject er et team: propager til alle teammedlemmer
    INSERT INTO node_access (subject_id, object_id, access, via_edge)
    SELECT e.source_id, na.object_id, na.access, na.via_edge
    FROM node_access na
    JOIN edges e ON e.target_id = p_subject_id
      AND e.edge_type = 'member_of'
    WHERE na.subject_id = p_subject_id
    ON CONFLICT (subject_id, object_id)
    DO UPDATE SET access = GREATEST(node_access.access, EXCLUDED.access);

    -- Team-transitivitet: arv tilgang fra teamet.
    -- Når en bruker melder seg inn i et team, arver brukeren
    -- all tilgang teamet allerede har til andre noder.
    INSERT INTO node_access (subject_id, object_id, access, via_edge)
    SELECT p_subject_id, na.object_id, na.access, na.via_edge
    FROM node_access na
    WHERE na.subject_id = p_root_node_id
    ON CONFLICT (subject_id, object_id)
    DO UPDATE SET access = GREATEST(node_access.access, EXCLUDED.access);
END;
$$ LANGUAGE plpgsql;

COMMIT;
