-- Migration 027: Foundation Ontology Update
-- Purpose: Implement DAARION.city ontology (Agent → MicroDAO → Node → District)
-- Based on: docs/foundation/microdao_Data_Model_UPDATE_v1.md
-- Date: 2025-11-29
-- Status: Non-Breaking Update

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

-- Agent role enum
DO $$ BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'agent_role') THEN
        CREATE TYPE agent_role AS ENUM ('regular', 'orchestrator');
    END IF;
END $$;

-- Agent service scope enum
DO $$ BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'service_scope') THEN
        CREATE TYPE service_scope AS ENUM ('microdao', 'district', 'city');
    END IF;
END $$;

-- MicroDAO type enum
DO $$ BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'microdao_type') THEN
        CREATE TYPE microdao_type AS ENUM ('root', 'standard', 'district');
    END IF;
END $$;

-- Node kind enum
DO $$ BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'node_kind') THEN
        CREATE TYPE node_kind AS ENUM ('smartphone', 'laptop', 'edge', 'datacenter', 'iot', 'gpu-cluster');
    END IF;
END $$;

-- Node status enum
DO $$ BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'node_status') THEN
        CREATE TYPE node_status AS ENUM ('provisioning', 'active', 'draining', 'retired');
    END IF;
END $$;

-- Assignment scope enum (same as service_scope but for assignments)
DO $$ BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'assignment_scope') THEN
        CREATE TYPE assignment_scope AS ENUM ('microdao', 'district', 'city');
    END IF;
END $$;

-- DAIS trust level enum
DO $$ BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'dais_trust_level') THEN
        CREATE TYPE dais_trust_level AS ENUM ('guest', 'agent', 'verified', 'orchestrator', 'operator');
    END IF;
END $$;

-- ============================================================================
-- 1. DAIS IDENTITY TABLES (DAIS = DAARION Autonomous Identity System)
-- ============================================================================

-- Main DAIS identity table
CREATE TABLE IF NOT EXISTS dais_identities (
    id TEXT PRIMARY KEY,
    did TEXT NOT NULL UNIQUE,  -- format: did:daarion:<uuid>
    default_email TEXT,
    default_wallet TEXT,
    matrix_handle TEXT,  -- format: @<agent_id>:matrix.daarion.city
    trust_level dais_trust_level NOT NULL DEFAULT 'agent',
    metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_dais_identities_did ON dais_identities(did);
CREATE INDEX IF NOT EXISTS idx_dais_identities_trust ON dais_identities(trust_level);

-- DAIS email identities
CREATE TABLE IF NOT EXISTS dais_emails (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    dais_id TEXT NOT NULL REFERENCES dais_identities(id) ON DELETE CASCADE,
    email TEXT NOT NULL,
    verified BOOLEAN NOT NULL DEFAULT false,
    verified_at TIMESTAMPTZ,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    UNIQUE(dais_id, email)
);

CREATE INDEX IF NOT EXISTS idx_dais_emails_dais ON dais_emails(dais_id);
CREATE INDEX IF NOT EXISTS idx_dais_emails_email ON dais_emails(email);

-- DAIS wallet identities
CREATE TABLE IF NOT EXISTS dais_wallets (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    dais_id TEXT NOT NULL REFERENCES dais_identities(id) ON DELETE CASCADE,
    wallet_address TEXT NOT NULL,
    network TEXT NOT NULL DEFAULT 'evm',  -- evm, ton, solana
    verified BOOLEAN NOT NULL DEFAULT false,
    verified_at TIMESTAMPTZ,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    UNIQUE(dais_id, wallet_address)
);

CREATE INDEX IF NOT EXISTS idx_dais_wallets_dais ON dais_wallets(dais_id);
CREATE INDEX IF NOT EXISTS idx_dais_wallets_address ON dais_wallets(wallet_address);

-- DAIS public keys
CREATE TABLE IF NOT EXISTS dais_keys (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    dais_id TEXT NOT NULL REFERENCES dais_identities(id) ON DELETE CASCADE,
    key_type TEXT NOT NULL,  -- ed25519, x25519, secp256k1
    public_key TEXT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    revoked_at TIMESTAMPTZ,
    UNIQUE(dais_id, key_type, public_key)
);

CREATE INDEX IF NOT EXISTS idx_dais_keys_dais ON dais_keys(dais_id);

-- ============================================================================
-- 2. AGENTS TABLE UPDATE
-- ============================================================================

-- Add new ontology fields to agents
ALTER TABLE agents ADD COLUMN IF NOT EXISTS dais_identity_id TEXT REFERENCES dais_identities(id);
ALTER TABLE agents ADD COLUMN IF NOT EXISTS agent_role agent_role DEFAULT 'regular';
ALTER TABLE agents ADD COLUMN IF NOT EXISTS agent_service_scope service_scope;
ALTER TABLE agents ADD COLUMN IF NOT EXISTS home_microdao_id TEXT;
ALTER TABLE agents ADD COLUMN IF NOT EXISTS home_node_id TEXT;

-- Create indexes
CREATE INDEX IF NOT EXISTS idx_agents_dais ON agents(dais_identity_id);
CREATE INDEX IF NOT EXISTS idx_agents_role ON agents(agent_role);
CREATE INDEX IF NOT EXISTS idx_agents_service_scope ON agents(agent_service_scope);
CREATE INDEX IF NOT EXISTS idx_agents_home_microdao ON agents(home_microdao_id);
CREATE INDEX IF NOT EXISTS idx_agents_home_node ON agents(home_node_id);

-- Migrate existing is_orchestrator to agent_role
UPDATE agents 
SET agent_role = 'orchestrator'::agent_role 
WHERE is_orchestrator = true AND agent_role IS NULL;

UPDATE agents 
SET agent_role = 'regular'::agent_role 
WHERE is_orchestrator = false AND agent_role IS NULL;

-- Set default home_microdao_id for existing agents
UPDATE agents 
SET home_microdao_id = primary_microdao_id 
WHERE home_microdao_id IS NULL AND primary_microdao_id IS NOT NULL;

-- ============================================================================
-- 3. MICRODAOS TABLE UPDATE
-- ============================================================================

-- Add new ontology fields to microdaos
ALTER TABLE microdaos ADD COLUMN IF NOT EXISTS dao_type microdao_type DEFAULT 'standard';
ALTER TABLE microdaos ADD COLUMN IF NOT EXISTS primary_orchestrator_agent_id TEXT;
ALTER TABLE microdaos ADD COLUMN IF NOT EXISTS wallet_address TEXT;

-- Create indexes
CREATE INDEX IF NOT EXISTS idx_microdaos_type ON microdaos(dao_type);
CREATE INDEX IF NOT EXISTS idx_microdaos_primary_orchestrator ON microdaos(primary_orchestrator_agent_id);

-- Migrate existing is_platform to dao_type
UPDATE microdaos 
SET dao_type = 'district'::microdao_type 
WHERE is_platform = true AND dao_type IS NULL;

UPDATE microdaos 
SET dao_type = 'standard'::microdao_type 
WHERE is_platform = false AND dao_type IS NULL;

-- Set DAARION as root
UPDATE microdaos 
SET dao_type = 'root'::microdao_type 
WHERE slug = 'daarion';

-- Copy orchestrator_agent_id to primary_orchestrator_agent_id
UPDATE microdaos 
SET primary_orchestrator_agent_id = orchestrator_agent_id 
WHERE primary_orchestrator_agent_id IS NULL AND orchestrator_agent_id IS NOT NULL;

-- ============================================================================
-- 4. NODES TABLE UPDATE
-- ============================================================================

-- Add new ontology fields to nodes
ALTER TABLE nodes ADD COLUMN IF NOT EXISTS kind node_kind DEFAULT 'datacenter';
ALTER TABLE nodes ADD COLUMN IF NOT EXISTS node_status node_status DEFAULT 'active';
ALTER TABLE nodes ADD COLUMN IF NOT EXISTS capabilities JSONB NOT NULL DEFAULT '{}'::jsonb;
ALTER TABLE nodes ADD COLUMN IF NOT EXISTS microdao_id TEXT;
ALTER TABLE nodes ADD COLUMN IF NOT EXISTS last_heartbeat TIMESTAMPTZ;

-- Create indexes
CREATE INDEX IF NOT EXISTS idx_nodes_kind ON nodes(kind);
CREATE INDEX IF NOT EXISTS idx_nodes_status ON nodes(node_status);
CREATE INDEX IF NOT EXISTS idx_nodes_microdao ON nodes(microdao_id);

-- Migrate existing nodes
UPDATE nodes 
SET kind = 'datacenter'::node_kind,
    node_status = 'active'::node_status
WHERE kind IS NULL;

-- Build capabilities from existing gpu/modules columns
UPDATE nodes 
SET capabilities = jsonb_build_object(
    'gpu', COALESCE(gpu, '{}'::jsonb),
    'modules', COALESCE(modules, '[]'::jsonb),
    'roles', COALESCE(array_to_json(roles), '[]'::json)
)
WHERE capabilities = '{}'::jsonb;

-- Set DAARION as owner for existing nodes
UPDATE nodes 
SET microdao_id = (SELECT id FROM microdaos WHERE slug = 'daarion' LIMIT 1)
WHERE microdao_id IS NULL;

-- ============================================================================
-- 5. AGENT ASSIGNMENTS TABLE (NEW)
-- ============================================================================

CREATE TABLE IF NOT EXISTS agent_assignments (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
    target_microdao_id TEXT NOT NULL,
    scope assignment_scope NOT NULL DEFAULT 'microdao',
    role TEXT NOT NULL,  -- advisor, security, mentor, ops, core-team
    start_ts TIMESTAMPTZ NOT NULL DEFAULT now(),
    end_ts TIMESTAMPTZ,
    metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    UNIQUE(agent_id, target_microdao_id, role)
);

CREATE INDEX IF NOT EXISTS idx_assignments_agent ON agent_assignments(agent_id);
CREATE INDEX IF NOT EXISTS idx_assignments_target ON agent_assignments(target_microdao_id);
CREATE INDEX IF NOT EXISTS idx_assignments_scope ON agent_assignments(scope);
CREATE INDEX IF NOT EXISTS idx_assignments_active ON agent_assignments(agent_id) WHERE end_ts IS NULL;

-- Migrate existing microdao_agents to assignments
INSERT INTO agent_assignments (agent_id, target_microdao_id, scope, role)
SELECT 
    agent_id, 
    microdao_id, 
    'microdao'::assignment_scope,
    COALESCE(role, 'member')
FROM microdao_agents
ON CONFLICT (agent_id, target_microdao_id, role) DO NOTHING;

-- ============================================================================
-- 6. ROOMS TABLE (City/MicroDAO/District rooms)
-- ============================================================================

-- Room type enum
DO $$ BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'room_type') THEN
        CREATE TYPE room_type AS ENUM ('city-room', 'dao-room', 'front-room', 'agent-room', 'event-room', 'district-room');
    END IF;
END $$;

-- Room visibility enum
DO $$ BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'room_visibility') THEN
        CREATE TYPE room_visibility AS ENUM ('private', 'members', 'public-city', 'public-global');
    END IF;
END $$;

-- Space scope enum
DO $$ BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'space_scope') THEN
        CREATE TYPE space_scope AS ENUM ('city', 'microdao', 'district');
    END IF;
END $$;

-- Rooms table (foundation for Rooms Layer)
CREATE TABLE IF NOT EXISTS rooms (
    id TEXT PRIMARY KEY,
    owner_type TEXT NOT NULL,  -- city, microdao, district, agent
    owner_id TEXT NOT NULL,
    type room_type NOT NULL DEFAULT 'dao-room',
    space_scope space_scope NOT NULL DEFAULT 'microdao',
    visibility room_visibility NOT NULL DEFAULT 'members',
    name TEXT NOT NULL,
    description TEXT,
    matrix_room_id TEXT,
    is_portal BOOLEAN NOT NULL DEFAULT false,
    portal_target_microdao_id TEXT,
    map_x INTEGER,
    map_y INTEGER,
    zone TEXT,
    mesh_id TEXT,  -- for 3D
    primary_agent_id TEXT,
    team_agent_ids TEXT[],
    metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_rooms_owner ON rooms(owner_type, owner_id);
CREATE INDEX IF NOT EXISTS idx_rooms_type ON rooms(type);
CREATE INDEX IF NOT EXISTS idx_rooms_scope ON rooms(space_scope);
CREATE INDEX IF NOT EXISTS idx_rooms_visibility ON rooms(visibility);
CREATE INDEX IF NOT EXISTS idx_rooms_portal ON rooms(is_portal) WHERE is_portal = true;

-- ============================================================================
-- 7. EVENT OUTBOX (for NATS events)
-- ============================================================================

CREATE TABLE IF NOT EXISTS event_outbox (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    event_type TEXT NOT NULL,  -- dagion.agent.promoted_to_orchestrator, etc.
    subject TEXT NOT NULL,     -- NATS subject
    payload JSONB NOT NULL,
    version TEXT NOT NULL DEFAULT '1.0',
    status TEXT NOT NULL DEFAULT 'pending',  -- pending, published, failed
    retry_count INTEGER NOT NULL DEFAULT 0,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    published_at TIMESTAMPTZ,
    error_message TEXT
);

CREATE INDEX IF NOT EXISTS idx_outbox_status ON event_outbox(status);
CREATE INDEX IF NOT EXISTS idx_outbox_type ON event_outbox(event_type);
CREATE INDEX IF NOT EXISTS idx_outbox_pending ON event_outbox(created_at) WHERE status = 'pending';

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

COMMENT ON TABLE dais_identities IS 'DAIS (DAARION Autonomous Identity System) - unified digital identity';
COMMENT ON TABLE dais_emails IS 'Email identities linked to DAIS';
COMMENT ON TABLE dais_wallets IS 'Wallet identities (EVM/TON) linked to DAIS';
COMMENT ON TABLE dais_keys IS 'Public keys for signing/encryption (Ed25519, X25519, secp256k1)';
COMMENT ON TABLE agent_assignments IS 'Agent work assignments to other MicroDAO/District/City';
COMMENT ON TABLE rooms IS 'Rooms Layer - city/microdao/district rooms and portals';
COMMENT ON TABLE event_outbox IS 'Outbox pattern for transactional NATS event delivery';

COMMENT ON COLUMN agents.agent_role IS 'Agent role: regular or orchestrator';
COMMENT ON COLUMN agents.agent_service_scope IS 'Service scope: microdao, district, or city';
COMMENT ON COLUMN agents.home_microdao_id IS 'Primary MicroDAO affiliation (ontology invariant)';
COMMENT ON COLUMN agents.home_node_id IS 'Primary node for agent execution';

COMMENT ON COLUMN microdaos.dao_type IS 'MicroDAO type: root (DAARION), standard, or district';
COMMENT ON COLUMN microdaos.primary_orchestrator_agent_id IS 'Main orchestrator agent for this MicroDAO';

COMMENT ON COLUMN nodes.kind IS 'Node hardware type: smartphone, laptop, edge, datacenter, iot, gpu-cluster';
COMMENT ON COLUMN nodes.node_status IS 'Lifecycle status: provisioning, active, draining, retired';
COMMENT ON COLUMN nodes.capabilities IS 'Node capabilities: CPU, GPU, RAM, network, sensors';

-- ============================================================================
-- 9. SEED DATA - City Rooms
-- ============================================================================

-- Create default city rooms
INSERT INTO rooms (id, owner_type, owner_id, type, space_scope, visibility, name, description, primary_agent_id)
VALUES 
    ('city-lobby', 'city', 'daarion', 'city-room', 'city', 'public-city', 'City Lobby', 'Main entrance to DAARION.city', 'dario'),
    ('city-square', 'city', 'daarion', 'city-room', 'city', 'public-city', 'City Square', 'Central public space', 'dario'),
    ('city-news', 'city', 'daarion', 'city-room', 'city', 'public-city', 'City News', 'Latest news and announcements', 'daria'),
    ('city-help', 'city', 'daarion', 'city-room', 'city', 'public-city', 'City Help', 'Support and assistance', 'daria'),
    ('city-events', 'city', 'daarion', 'city-room', 'city', 'public-city', 'City Events', 'Events and activities', 'daarwizz')
ON CONFLICT (id) DO NOTHING;

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

SELECT 'Migration 027 completed: Foundation Ontology Update' as result;

