-- Migration 032: Governance Engine
-- Purpose: Implement Governance Layer for DAARION.city
-- Based on: docs/foundation/Agent_Governance_Protocol_v1.md
-- Date: 2025-11-29
-- Status: MVP Feature

-- ============================================================================
-- 0. ENUM TYPES
-- ============================================================================

-- Agent governance level (0-7 hierarchy)
DO $$ BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'agent_gov_level') THEN
        CREATE TYPE agent_gov_level AS ENUM (
            'guest',           -- Level 0
            'personal',        -- Level 1
            'member',          -- Level 2
            'worker',          -- Level 3
            'core_team',       -- Level 4
            'orchestrator',    -- Level 5
            'district_lead',   -- Level 6
            'city_governance'  -- Level 7
        );
    END IF;
END $$;

-- Agent status
DO $$ BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'agent_status') THEN
        CREATE TYPE agent_status AS ENUM ('active', 'suspended', 'revoked');
    END IF;
END $$;

-- Revocation type
DO $$ BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'revocation_type') THEN
        CREATE TYPE revocation_type AS ENUM ('soft', 'hard', 'shadow');
    END IF;
END $$;

-- Incident status
DO $$ BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'incident_status') THEN
        CREATE TYPE incident_status AS ENUM ('open', 'in_progress', 'resolved', 'closed');
    END IF;
END $$;

-- Incident priority
DO $$ BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'incident_priority') THEN
        CREATE TYPE incident_priority AS ENUM ('low', 'medium', 'high', 'critical');
    END IF;
END $$;

-- Incident escalation level
DO $$ BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'escalation_level') THEN
        CREATE TYPE escalation_level AS ENUM ('microdao', 'district', 'city');
    END IF;
END $$;

-- Target scope type for incidents
DO $$ BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'target_scope_type') THEN
        CREATE TYPE target_scope_type AS ENUM ('city', 'district', 'microdao', 'room', 'node', 'agent');
    END IF;
END $$;

-- Permission action
DO $$ BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'permission_action') THEN
        CREATE TYPE permission_action AS ENUM ('read', 'write', 'moderate', 'admin', 'superadmin');
    END IF;
END $$;

-- ============================================================================
-- 1. AGENTS TABLE UPDATE - Governance Fields
-- ============================================================================

-- Add governance fields to agents
ALTER TABLE agents ADD COLUMN IF NOT EXISTS gov_level agent_gov_level DEFAULT 'personal';
ALTER TABLE agents ADD COLUMN IF NOT EXISTS status agent_status DEFAULT 'active';
ALTER TABLE agents ADD COLUMN IF NOT EXISTS revoked_at TIMESTAMPTZ;
ALTER TABLE agents ADD COLUMN IF NOT EXISTS revoked_by TEXT;
ALTER TABLE agents ADD COLUMN IF NOT EXISTS revocation_reason TEXT;
ALTER TABLE agents ADD COLUMN IF NOT EXISTS revocation_type revocation_type;

-- Create indexes
CREATE INDEX IF NOT EXISTS idx_agents_gov_level ON agents(gov_level);
CREATE INDEX IF NOT EXISTS idx_agents_status ON agents(status);
CREATE INDEX IF NOT EXISTS idx_agents_revoked ON agents(revoked_at) WHERE revoked_at IS NOT NULL;

-- Migrate existing agent_role to gov_level
UPDATE agents 
SET gov_level = 'orchestrator'::agent_gov_level 
WHERE agent_role = 'orchestrator' AND gov_level = 'personal';

-- Set city governance agents
UPDATE agents 
SET gov_level = 'city_governance'::agent_gov_level
WHERE id IN ('daarwizz', 'dario', 'daria');

-- ============================================================================
-- 2. INCIDENTS TABLE
-- ============================================================================

CREATE TABLE IF NOT EXISTS incidents (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    created_by_dais_id TEXT NOT NULL,
    target_scope_type target_scope_type NOT NULL,
    target_scope_id TEXT NOT NULL,
    status incident_status NOT NULL DEFAULT 'open',
    priority incident_priority NOT NULL DEFAULT 'medium',
    assigned_to_dais_id TEXT,
    escalation_level escalation_level NOT NULL DEFAULT 'microdao',
    title TEXT NOT NULL,
    description TEXT,
    resolution TEXT,
    metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    resolved_at TIMESTAMPTZ,
    closed_at TIMESTAMPTZ
);

CREATE INDEX IF NOT EXISTS idx_incidents_status ON incidents(status);
CREATE INDEX IF NOT EXISTS idx_incidents_priority ON incidents(priority);
CREATE INDEX IF NOT EXISTS idx_incidents_escalation ON incidents(escalation_level);
CREATE INDEX IF NOT EXISTS idx_incidents_created_by ON incidents(created_by_dais_id);
CREATE INDEX IF NOT EXISTS idx_incidents_assigned ON incidents(assigned_to_dais_id);
CREATE INDEX IF NOT EXISTS idx_incidents_target ON incidents(target_scope_type, target_scope_id);
CREATE INDEX IF NOT EXISTS idx_incidents_open ON incidents(status, priority) WHERE status IN ('open', 'in_progress');

-- ============================================================================
-- 3. INCIDENT HISTORY TABLE
-- ============================================================================

CREATE TABLE IF NOT EXISTS incident_history (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    incident_id UUID NOT NULL REFERENCES incidents(id) ON DELETE CASCADE,
    action TEXT NOT NULL,  -- created, assigned, escalated, resolved, closed, comment
    actor_dais_id TEXT NOT NULL,
    old_value JSONB,
    new_value JSONB,
    comment TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_incident_history_incident ON incident_history(incident_id);
CREATE INDEX IF NOT EXISTS idx_incident_history_actor ON incident_history(actor_dais_id);

-- ============================================================================
-- 4. PERMISSIONS TABLE
-- ============================================================================

CREATE TABLE IF NOT EXISTS permissions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    dais_id TEXT NOT NULL,
    target_type TEXT NOT NULL,  -- room, microdao, node, district, city
    target_id TEXT NOT NULL,
    action permission_action NOT NULL,
    granted_by TEXT NOT NULL,
    expires_at TIMESTAMPTZ,
    metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    UNIQUE(dais_id, target_type, target_id, action)
);

CREATE INDEX IF NOT EXISTS idx_permissions_dais ON permissions(dais_id);
CREATE INDEX IF NOT EXISTS idx_permissions_target ON permissions(target_type, target_id);
CREATE INDEX IF NOT EXISTS idx_permissions_valid ON permissions(dais_id) 
    WHERE expires_at IS NULL OR expires_at > now();

-- ============================================================================
-- 5. REVOCATIONS TABLE (Audit trail for revocations)
-- ============================================================================

CREATE TABLE IF NOT EXISTS agent_revocations (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    agent_id TEXT NOT NULL REFERENCES agents(id),
    dais_id TEXT,
    revoked_by TEXT NOT NULL,
    revocation_type revocation_type NOT NULL,
    reason TEXT NOT NULL,
    scope TEXT NOT NULL,  -- city, district:<id>, microdao:<id>
    keys_invalidated BOOLEAN NOT NULL DEFAULT true,
    wallet_disabled BOOLEAN NOT NULL DEFAULT true,
    room_access_revoked BOOLEAN NOT NULL DEFAULT true,
    node_privileges_removed BOOLEAN NOT NULL DEFAULT true,
    assignments_terminated BOOLEAN NOT NULL DEFAULT true,
    reversible BOOLEAN NOT NULL DEFAULT true,
    reversed_at TIMESTAMPTZ,
    reversed_by TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_revocations_agent ON agent_revocations(agent_id);
CREATE INDEX IF NOT EXISTS idx_revocations_dais ON agent_revocations(dais_id);
CREATE INDEX IF NOT EXISTS idx_revocations_type ON agent_revocations(revocation_type);

-- ============================================================================
-- 6. UPDATE EVENT_OUTBOX WITH NEW GOVERNANCE EVENTS
-- ============================================================================

-- Add actor_id column to event_outbox for audit
ALTER TABLE event_outbox ADD COLUMN IF NOT EXISTS actor_id TEXT;
ALTER TABLE event_outbox ADD COLUMN IF NOT EXISTS target_id TEXT;
ALTER TABLE event_outbox ADD COLUMN IF NOT EXISTS scope TEXT;

CREATE INDEX IF NOT EXISTS idx_outbox_actor ON event_outbox(actor_id);
CREATE INDEX IF NOT EXISTS idx_outbox_target ON event_outbox(target_id);
CREATE INDEX IF NOT EXISTS idx_outbox_scope ON event_outbox(scope);

-- ============================================================================
-- 7. DAIS KEYS - Add revoked flag
-- ============================================================================

ALTER TABLE dais_keys ADD COLUMN IF NOT EXISTS revoked BOOLEAN DEFAULT false;
ALTER TABLE dais_keys ADD COLUMN IF NOT EXISTS revoked_reason TEXT;
ALTER TABLE dais_keys ADD COLUMN IF NOT EXISTS revoked_by TEXT;

-- Migrate existing revoked_at to revoked
UPDATE dais_keys SET revoked = true WHERE revoked_at IS NOT NULL AND revoked = false;

-- ============================================================================
-- 8. COMMENTS
-- ============================================================================

COMMENT ON TABLE incidents IS 'Incident tracking for governance escalation';
COMMENT ON TABLE incident_history IS 'Audit trail for incident changes';
COMMENT ON TABLE permissions IS 'Explicit permissions for DAIS identities';
COMMENT ON TABLE agent_revocations IS 'Audit trail for agent revocations';

COMMENT ON COLUMN agents.gov_level IS 'Governance level: 0=guest to 7=city_governance';
COMMENT ON COLUMN agents.status IS 'Agent status: active, suspended, or revoked';
COMMENT ON COLUMN agents.revoked_at IS 'Timestamp when agent was revoked';
COMMENT ON COLUMN agents.revocation_type IS 'Type of revocation: soft, hard, or shadow';

COMMENT ON COLUMN incidents.escalation_level IS 'Current escalation: microdao → district → city';
COMMENT ON COLUMN incidents.target_scope_type IS 'What the incident is about: city, district, microdao, room, node, agent';

-- ============================================================================
-- 9. SEED DATA - City Governance Agents
-- ============================================================================

-- Ensure city governance agents have correct level
UPDATE agents 
SET gov_level = 'city_governance'::agent_gov_level,
    status = 'active'::agent_status
WHERE id IN ('daarwizz', 'dario', 'daria');

-- ============================================================================
-- DONE
-- ============================================================================

SELECT 'Migration 032 completed: Governance Engine' as result;

