-- Migration 002: Entity instances (UUIDv7 + slug, R3-5/D1)
-- The inventory graph: entities + typed relationships + blast_radius function.

CREATE TABLE entities (
    id            UUID PRIMARY KEY,
    slug          TEXT NOT NULL UNIQUE,
    type          TEXT NOT NULL REFERENCES entity_types(name),
    name          TEXT NOT NULL,
    state         TEXT,
    attributes    JSONB NOT NULL DEFAULT '{}',
    maintenance_until TIMESTAMPTZ,
    version       INTEGER NOT NULL DEFAULT 1,
    created_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
    UNIQUE (type, name)
);
CREATE INDEX idx_entities_type  ON entities(type);
CREATE INDEX idx_entities_state ON entities(state);
CREATE INDEX idx_entities_attrs ON entities USING GIN(attributes);

CREATE TABLE relationships (
    source_id     UUID NOT NULL REFERENCES entities(id) ON DELETE RESTRICT,
    target_id     UUID NOT NULL REFERENCES entities(id) ON DELETE RESTRICT,
    type          TEXT NOT NULL REFERENCES relationship_types(name),
    attributes    JSONB,
    valid_from    TIMESTAMPTZ NOT NULL DEFAULT now(),
    valid_to      TIMESTAMPTZ,
    PRIMARY KEY (source_id, target_id, type, valid_from)
);
CREATE INDEX idx_rel_source ON relationships(source_id) WHERE valid_to IS NULL;
CREATE INDEX idx_rel_target ON relationships(target_id) WHERE valid_to IS NULL;
CREATE INDEX idx_rel_type   ON relationships(type)      WHERE valid_to IS NULL;

-- Cycle-safe traversal (P1): path accumulator prevents revisits; depth capped.
CREATE OR REPLACE FUNCTION blast_radius(start_id UUID, max_depth INT DEFAULT 3,
                                        rel_types TEXT[] DEFAULT NULL)
RETURNS TABLE(entity_id UUID, depth INT) AS $$
    WITH RECURSIVE walk AS (
        SELECT start_id AS entity_id, 0 AS depth, ARRAY[start_id] AS path
        UNION ALL
        SELECT r.target_id, w.depth + 1, w.path || r.target_id
        FROM relationships r
        JOIN walk w ON r.source_id = w.entity_id
        WHERE w.depth < LEAST(max_depth, 5)
          AND r.valid_to IS NULL
          AND NOT r.target_id = ANY(w.path)
          AND (rel_types IS NULL OR r.type = ANY(rel_types))
    )
    SELECT entity_id, MIN(depth) FROM walk GROUP BY entity_id;
$$ LANGUAGE sql STABLE;
