-- 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);

-- ============================================================
-- Phase 6 — Cross-Framework UI Adaptation Layer
-- ============================================================

-- Formalises the rules for widget envelope emission: which data-* attributes
-- are required, their format, and the contract version.
CREATE TABLE envelope_emission_contracts (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    contract_version TEXT NOT NULL UNIQUE,   -- e.g. "1.0", "1.1"
    required_attributes JSONB NOT NULL,
    -- e.g. ["data-widget-id", "data-view-context", "data-hub-id"]
    optional_attributes JSONB NOT NULL DEFAULT '[]',
    validation_rules JSONB NOT NULL DEFAULT '{}',
    -- machine-readable rules: format checks, presence guards
    description TEXT,
    status TEXT NOT NULL DEFAULT 'active',
    -- status values: draft | active | superseded
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE INDEX envelope_emission_contracts_status_idx ON envelope_emission_contracts (status);

-- Standardised REST interface contract for external event and annotation
-- submission — used by non-IHP adapters.
CREATE TABLE interaction_reporting_contracts (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    contract_version TEXT NOT NULL UNIQUE,   -- e.g. "1.0"
    endpoint_path TEXT NOT NULL,             -- e.g. "/api/v1/interaction-events"
    accepted_event_types JSONB NOT NULL,     -- e.g. ["clicked","viewed","submitted"]
    required_fields JSONB NOT NULL,
    -- minimum payload: widget_id, hub_id, event_type, occurred_at
    auth_scheme TEXT NOT NULL DEFAULT 'bearer',
    description TEXT,
    status TEXT NOT NULL DEFAULT 'active',
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE INDEX interaction_reporting_contracts_status_idx ON interaction_reporting_contracts (status);

-- Describes how a specific UI technology maps to IHF widget protocol obligations.
CREATE TABLE widget_adapter_specs (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    name TEXT NOT NULL UNIQUE,         -- e.g. "react-18", "vue-3", "web-component"
    framework TEXT NOT NULL,           -- e.g. "react", "vue", "vanilla"
    version TEXT NOT NULL,             -- adapter spec version, e.g. "1.0"
    envelope_contract_id UUID REFERENCES envelope_emission_contracts(id),
    reporting_contract_id UUID REFERENCES interaction_reporting_contracts(id),
    status TEXT NOT NULL DEFAULT 'draft',
    -- status values: draft | active | deprecated
    notes TEXT,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE INDEX widget_adapter_specs_framework_idx ON widget_adapter_specs (framework);
CREATE INDEX widget_adapter_specs_status_idx ON widget_adapter_specs (status);

-- Link widgets to their adapter spec (null = native IHP widget).
ALTER TABLE widgets
    ADD COLUMN adapter_spec_id UUID REFERENCES widget_adapter_specs(id);

CREATE INDEX widgets_adapter_spec_id_idx ON widgets (adapter_spec_id);

-- Per-hub API key for bearer-token auth on the interaction reporting endpoint.
ALTER TABLE hubs
    ADD COLUMN api_key TEXT;

-- Phase 7: Advanced Observability and Operational Integration

-- Aggregated pain score per widget, recomputed on demand or scheduled.
CREATE TABLE friction_scores (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    widget_id UUID NOT NULL REFERENCES widgets(id),
    score INTEGER NOT NULL DEFAULT 0,
    annotation_count INTEGER NOT NULL DEFAULT 0,
    error_event_count INTEGER NOT NULL DEFAULT 0,
    regression_flag BOOLEAN NOT NULL DEFAULT FALSE,
    stale_candidate_count INTEGER NOT NULL DEFAULT 0,
    last_computed_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
    UNIQUE (widget_id)
);

CREATE INDEX friction_scores_widget_id_idx ON friction_scores (widget_id);
CREATE INDEX friction_scores_score_idx ON friction_scores (score DESC);

-- Detected stalls at specific pipeline stages.
CREATE TABLE bottleneck_records (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    hub_id UUID NOT NULL REFERENCES hubs(id),
    stage TEXT NOT NULL,
    subject_type TEXT NOT NULL,
    subject_id UUID NOT NULL,
    stalled_since TIMESTAMP WITH TIME ZONE NOT NULL,
    severity TEXT NOT NULL DEFAULT 'medium',
    resolved_at TIMESTAMP WITH TIME ZONE,
    notes TEXT,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE INDEX bottleneck_records_hub_id_idx ON bottleneck_records (hub_id);
CREATE INDEX bottleneck_records_stage_idx ON bottleneck_records (stage);
CREATE INDEX bottleneck_records_resolved_idx ON bottleneck_records (resolved_at)
    WHERE resolved_at IS NULL;

-- Periodic health snapshots for trend tracking.
CREATE TABLE hub_health_snapshots (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    hub_id UUID NOT NULL REFERENCES hubs(id),
    health_score INTEGER NOT NULL,
    open_candidates INTEGER NOT NULL DEFAULT 0,
    regressed_widgets INTEGER NOT NULL DEFAULT 0,
    stale_decisions INTEGER NOT NULL DEFAULT 0,
    active_bottlenecks INTEGER NOT NULL DEFAULT 0,
    computed_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE INDEX hub_health_snapshots_hub_id_idx ON hub_health_snapshots (hub_id);
CREATE INDEX hub_health_snapshots_computed_at_idx
    ON hub_health_snapshots (hub_id, computed_at DESC);

-- Patterns detected across multiple hubs.
CREATE TABLE cross_hub_propagations (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    pattern_type TEXT NOT NULL,
    source_hub_id UUID REFERENCES hubs(id),
    affected_hub_ids JSONB NOT NULL DEFAULT '[]',
    summary TEXT NOT NULL,
    detected_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
    status TEXT NOT NULL DEFAULT 'open',
    notes TEXT
);

CREATE INDEX cross_hub_propagations_status_idx ON cross_hub_propagations (status);
CREATE INDEX cross_hub_propagations_pattern_idx ON cross_hub_propagations (pattern_type);

-- Phase 8: Federated Hub Maturity

-- Explicit ownership record for a widget.
CREATE TABLE widget_ownerships (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    widget_id UUID NOT NULL REFERENCES widgets(id),
    owner_hub_id UUID NOT NULL REFERENCES hubs(id),
    steward_hub_id UUID REFERENCES hubs(id),
    ownership_type TEXT NOT NULL DEFAULT 'local',
    -- 'local' | 'delegated' | 'global'
    effective_from TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
    effective_until TIMESTAMP WITH TIME ZONE,
    notes TEXT,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE INDEX widget_ownerships_widget_id_idx ON widget_ownerships (widget_id);
CREATE INDEX widget_ownerships_owner_hub_idx ON widget_ownerships (owner_hub_id);
CREATE INDEX widget_ownerships_steward_hub_idx ON widget_ownerships (steward_hub_id);

-- Routing rule: automatically routes a RequirementCandidate to another hub.
CREATE TABLE hub_routing_rules (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    source_hub_id UUID NOT NULL REFERENCES hubs(id),
    target_hub_id UUID NOT NULL REFERENCES hubs(id),
    match_category TEXT,
    match_widget_type TEXT,
    priority INTEGER NOT NULL DEFAULT 0,
    status TEXT NOT NULL DEFAULT 'inactive',
    -- 'active' | 'inactive'
    notes TEXT,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE INDEX hub_routing_rules_source_idx ON hub_routing_rules (source_hub_id);
CREATE INDEX hub_routing_rules_status_idx ON hub_routing_rules (status);

-- Routing destination on requirement candidates.
ALTER TABLE requirement_candidates
    ADD COLUMN routed_to_hub_id UUID REFERENCES hubs(id);

CREATE INDEX requirement_candidates_routed_hub_idx
    ON requirement_candidates (routed_to_hub_id)
    WHERE routed_to_hub_id IS NOT NULL;

-- Org-wide policy overlay applied across selected hubs.
CREATE TABLE federated_policy_overlays (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    title TEXT NOT NULL,
    policy_text TEXT NOT NULL,
    applies_to_hubs JSONB NOT NULL DEFAULT '[]',
    enforced_from TIMESTAMP WITH TIME ZONE,
    status TEXT NOT NULL DEFAULT 'draft',
    -- 'draft' | 'active' | 'retired'
    notes TEXT,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE INDEX federated_policy_overlays_status_idx ON federated_policy_overlays (status);

-- Named governance role assigned to a hub.
CREATE TABLE stewardship_roles (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    hub_id UUID NOT NULL REFERENCES hubs(id),
    role_name TEXT NOT NULL,
    assigned_to TEXT NOT NULL,
    granted_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
    revoked_at TIMESTAMP WITH TIME ZONE,
    notes TEXT
);

CREATE INDEX stewardship_roles_hub_id_idx ON stewardship_roles (hub_id);
CREATE INDEX stewardship_roles_active_idx ON stewardship_roles (revoked_at)
    WHERE revoked_at IS NULL;

-- Long-term archival entry for any IHF artifact.
CREATE TABLE archive_records (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    subject_type TEXT NOT NULL,
    subject_id UUID NOT NULL,
    archived_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
    reason TEXT NOT NULL,
    archived_by TEXT NOT NULL,
    lineage_ref TEXT
);

CREATE INDEX archive_records_subject_type_idx ON archive_records (subject_type);
CREATE INDEX archive_records_subject_id_idx ON archive_records (subject_id);

-- Soft-archive flag on widgets.
ALTER TABLE widgets
    ADD COLUMN is_archived BOOLEAN NOT NULL DEFAULT FALSE;

CREATE INDEX widgets_is_archived_idx ON widgets (is_archived)
    WHERE is_archived = TRUE;
