-- IHF Phase 1 + Phase 2 Schema
-- Hub, Widget, WidgetVersion, InteractionEvent, Annotation
-- Phase 2: AnnotationThread, RequirementCandidate, TriageState, ReviewerAssignment
-- See workplans/IHUB-WP-0001-ihf-phase1-minimal-interaction-core.md
-- See workplans/IHUB-WP-0002-ihf-phase2-structured-feedback-and-triage.md

CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

-- Users (T10 — authentication)
CREATE TABLE users (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    email TEXT NOT NULL UNIQUE,
    password_hash TEXT NOT NULL,
    name TEXT NOT NULL,
    locked_at TIMESTAMP WITH TIME ZONE DEFAULT NULL,
    failed_login_attempts INT NOT NULL DEFAULT 0,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

-- Hubs — bounded domains of responsibility
CREATE TABLE hubs (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    slug TEXT NOT NULL UNIQUE,
    name TEXT NOT NULL,
    domain TEXT NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

-- Widgets — smallest semantically governable interaction units
CREATE TABLE widgets (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    hub_id UUID NOT NULL REFERENCES hubs(id) ON DELETE RESTRICT,
    name TEXT NOT NULL,
    widget_type TEXT NOT NULL,
    capability_ref TEXT,
    view_context TEXT,
    policy_scope TEXT NOT NULL DEFAULT 'internal',
    status TEXT NOT NULL DEFAULT 'active',
    version INT NOT NULL DEFAULT 1,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

-- Widget version history
CREATE TABLE widget_versions (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    widget_id UUID NOT NULL REFERENCES widgets(id) ON DELETE CASCADE,
    version INT NOT NULL,
    schema_snapshot JSONB NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
    UNIQUE (widget_id, version)
);

-- Interaction events — append-only capture
CREATE TABLE interaction_events (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    widget_id UUID NOT NULL REFERENCES widgets(id) ON DELETE CASCADE,
    event_type TEXT NOT NULL,
    actor_id UUID,
    actor_type TEXT NOT NULL DEFAULT 'user',
    view_context_ref TEXT,
    metadata JSONB DEFAULT '{}' NOT NULL,
    occurred_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE INDEX interaction_events_widget_id_idx ON interaction_events (widget_id);
CREATE INDEX interaction_events_occurred_at_idx ON interaction_events (occurred_at DESC);

-- Enforce append-only on interaction_events
CREATE OR REPLACE FUNCTION prevent_interaction_event_mutation()
RETURNS TRIGGER AS $$
BEGIN
    RAISE EXCEPTION 'interaction_events is append-only: UPDATE and DELETE are not permitted';
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER interaction_events_no_update
    BEFORE UPDATE ON interaction_events
    FOR EACH ROW EXECUTE FUNCTION prevent_interaction_event_mutation();

CREATE TRIGGER interaction_events_no_delete
    BEFORE DELETE ON interaction_events
    FOR EACH ROW EXECUTE FUNCTION prevent_interaction_event_mutation();

-- Annotation threads — groups related annotations for triage (Phase 2)
CREATE TABLE annotation_threads (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    widget_id UUID NOT NULL REFERENCES widgets(id) ON DELETE CASCADE,
    title TEXT NOT NULL,
    description TEXT,
    created_by UUID REFERENCES users(id),
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

-- Annotations — structured commentary, also append-only by convention
-- Phase 2 additions: severity, thread_id
CREATE TABLE annotations (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    widget_id UUID NOT NULL REFERENCES widgets(id) ON DELETE CASCADE,
    parent_id UUID REFERENCES annotations(id) ON DELETE CASCADE,
    body TEXT NOT NULL,
    category TEXT NOT NULL DEFAULT 'friction',
    severity TEXT NOT NULL DEFAULT 'medium',
    thread_id UUID REFERENCES annotation_threads(id) ON DELETE SET NULL,
    actor_id UUID,
    actor_type TEXT NOT NULL DEFAULT 'user',
    widget_state_ref TEXT,
    retracted_at TIMESTAMP WITH TIME ZONE DEFAULT NULL,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE INDEX annotations_widget_id_idx ON annotations (widget_id);

-- Requirement candidates — escalated from annotations/threads (Phase 2)
CREATE TABLE requirement_candidates (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    title TEXT NOT NULL,
    description TEXT NOT NULL,
    source_widget_id UUID NOT NULL REFERENCES widgets(id) ON DELETE RESTRICT,
    source_thread_id UUID REFERENCES annotation_threads(id) ON DELETE SET NULL,
    source_annotation_id UUID REFERENCES annotations(id) ON DELETE SET NULL,
    category TEXT NOT NULL DEFAULT 'friction',
    status TEXT NOT NULL DEFAULT 'open',
    created_by UUID REFERENCES users(id),
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE INDEX requirement_candidates_widget_id_idx ON requirement_candidates (source_widget_id);
CREATE INDEX requirement_candidates_status_idx ON requirement_candidates (status);

-- Triage state history — append-only audit trail of status transitions (Phase 2)
CREATE TABLE triage_states (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    candidate_id UUID NOT NULL REFERENCES requirement_candidates(id) ON DELETE CASCADE,
    status TEXT NOT NULL,
    notes TEXT,
    changed_by UUID REFERENCES users(id),
    changed_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE INDEX triage_states_candidate_id_idx ON triage_states (candidate_id);

-- Reviewer assignments — one reviewer per candidate (Phase 2)
CREATE TABLE reviewer_assignments (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    candidate_id UUID NOT NULL REFERENCES requirement_candidates(id) ON DELETE CASCADE,
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    assigned_by UUID REFERENCES users(id),
    assigned_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
    UNIQUE (candidate_id)
);

-- Requirements — promoted from accepted RequirementCandidates (Phase 3)
CREATE TABLE requirements (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    title TEXT NOT NULL,
    description TEXT NOT NULL,
    source_candidate_id UUID NOT NULL REFERENCES requirement_candidates(id) ON DELETE RESTRICT,
    status TEXT NOT NULL DEFAULT 'active',
    created_by UUID REFERENCES users(id),
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE INDEX requirements_source_candidate_id_idx ON requirements (source_candidate_id);

-- Decision records — governance decisions acting on requirements/candidates (Phase 3)
CREATE TABLE decision_records (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    title TEXT NOT NULL,
    rationale TEXT NOT NULL,
    outcome TEXT NOT NULL,
    requirement_id UUID REFERENCES requirements(id) ON DELETE SET NULL,
    candidate_id UUID REFERENCES requirement_candidates(id) ON DELETE SET NULL,
    decided_by UUID REFERENCES users(id),
    decided_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
    notes TEXT,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE INDEX decision_records_outcome_idx ON decision_records (outcome);
CREATE INDEX decision_records_requirement_id_idx ON decision_records (requirement_id);

-- Policy references — editorial links from decisions to policy scope (Phase 3)
CREATE TABLE policy_references (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    decision_id UUID NOT NULL REFERENCES decision_records(id) ON DELETE CASCADE,
    policy_scope TEXT NOT NULL,
    constraint_note TEXT,
    created_by UUID REFERENCES users(id),
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE INDEX policy_references_decision_id_idx ON policy_references (decision_id);

-- Implementation change references — editorial links to work items (Phase 3)
CREATE TABLE implementation_change_references (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    decision_id UUID NOT NULL REFERENCES decision_records(id) ON DELETE CASCADE,
    work_item_ref TEXT NOT NULL,
    system TEXT NOT NULL DEFAULT 'github',
    linked_by UUID REFERENCES users(id),
    linked_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE INDEX impl_change_refs_decision_id_idx ON implementation_change_references (decision_id);

-- Back-reference: which candidate was promoted to a requirement (Phase 3)
ALTER TABLE requirement_candidates ADD COLUMN requirement_id UUID REFERENCES requirements(id) ON DELETE SET NULL;

-- Deployment records — connect decisions to deployed versions (Phase 4)
CREATE TABLE deployment_records (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    impl_ref_id UUID REFERENCES implementation_change_references(id) ON DELETE SET NULL,
    decision_id UUID NOT NULL REFERENCES decision_records(id) ON DELETE RESTRICT,
    version_ref TEXT NOT NULL,
    deployed_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
    deployed_by UUID REFERENCES users(id),
    notes TEXT,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE INDEX deployment_records_decision_id_idx ON deployment_records (decision_id);
CREATE INDEX deployment_records_deployed_at_idx ON deployment_records (deployed_at DESC);

-- Outcome signals — append-only observation of widget behaviour post-deployment (Phase 4)
CREATE TABLE outcome_signals (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    widget_id UUID NOT NULL REFERENCES widgets(id) ON DELETE CASCADE,
    deployment_id UUID NOT NULL REFERENCES deployment_records(id) ON DELETE CASCADE,
    signal_type TEXT NOT NULL,
    value NUMERIC,
    observed_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE INDEX outcome_signals_widget_id_idx ON outcome_signals (widget_id);
CREATE INDEX outcome_signals_deployment_id_idx ON outcome_signals (deployment_id);
CREATE INDEX outcome_signals_observed_at_idx ON outcome_signals (observed_at DESC);

CREATE OR REPLACE FUNCTION prevent_outcome_signal_mutation()
RETURNS TRIGGER AS $$
BEGIN
    RAISE EXCEPTION 'outcome_signals is append-only: UPDATE and DELETE are not permitted';
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER outcome_signals_no_update
    BEFORE UPDATE ON outcome_signals
    FOR EACH ROW EXECUTE FUNCTION prevent_outcome_signal_mutation();

CREATE TRIGGER outcome_signals_no_delete
    BEFORE DELETE ON outcome_signals
    FOR EACH ROW EXECUTE FUNCTION prevent_outcome_signal_mutation();

-- Change evaluations — one score per deployment (Phase 4)
CREATE TABLE change_evaluations (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    deployment_id UUID NOT NULL REFERENCES deployment_records(id) ON DELETE CASCADE,
    decision_id UUID REFERENCES decision_records(id) ON DELETE SET NULL,
    score SMALLINT NOT NULL CHECK (score BETWEEN 1 AND 5),
    rationale TEXT NOT NULL,
    evaluated_by UUID REFERENCES users(id),
    evaluated_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
    UNIQUE (deployment_id)
);

CREATE INDEX change_evaluations_deployment_id_idx ON change_evaluations (deployment_id);

-- Agent proposals — AI-generated outputs awaiting human review (Phase 5)
CREATE TABLE agent_proposals (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    proposal_type TEXT NOT NULL,
    -- proposal_type values: summary | requirement_draft | duplicate_flag |
    --                        policy_flag | impl_proposal
    source_widget_id    UUID REFERENCES widgets(id) ON DELETE SET NULL,
    source_candidate_id UUID REFERENCES requirement_candidates(id) ON DELETE SET NULL,
    source_thread_id    UUID REFERENCES annotation_threads(id) ON DELETE SET NULL,
    source_decision_id  UUID REFERENCES decision_records(id) ON DELETE SET NULL,
    content   TEXT NOT NULL,
    model_ref TEXT NOT NULL,
    confidence NUMERIC CHECK (confidence BETWEEN 0 AND 1),
    status TEXT NOT NULL DEFAULT 'pending',
    -- status values: pending | accepted | rejected | superseded
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE INDEX agent_proposals_proposal_type_idx ON agent_proposals (proposal_type);
CREATE INDEX agent_proposals_status_idx ON agent_proposals (status);
CREATE INDEX agent_proposals_source_widget_id_idx ON agent_proposals (source_widget_id);
CREATE INDEX agent_proposals_created_at_idx ON agent_proposals (created_at DESC);

-- One review record per proposal (human decision on AI output) (Phase 5)
CREATE TABLE agent_review_records (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    proposal_id UUID NOT NULL REFERENCES agent_proposals(id) ON DELETE CASCADE,
    reviewer_id UUID REFERENCES users(id),
    decision TEXT NOT NULL,  -- accepted | rejected | modified
    notes TEXT,
    reviewed_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
    UNIQUE (proposal_id)
);

CREATE INDEX agent_review_records_proposal_id_idx ON agent_review_records (proposal_id);

-- Confidence annotations — per-dimension breakdown of AI confidence (Phase 5)
CREATE TABLE confidence_annotations (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    proposal_id UUID NOT NULL REFERENCES agent_proposals(id) ON DELETE CASCADE,
    dimension TEXT NOT NULL,
    -- dimension values: accuracy | relevance | completeness | policy_alignment
    score NUMERIC NOT NULL CHECK (score BETWEEN 0 AND 1),
    explanation TEXT,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE INDEX confidence_annotations_proposal_id_idx ON confidence_annotations (proposal_id);
