"""Initial migration — create 10 memory tables.

Revision ID: 001
Create Date: 2026-03-17

Uses CREATE TABLE IF NOT EXISTS for idempotency (safe to run on databases
that already have these tables, e.g. Personal Genius).
"""

from alembic import op

revision = "001"
down_revision = None
branch_labels = None
depends_on = None


def upgrade() -> None:
    # -- 1. memory_events --
    op.execute("""
        CREATE TABLE IF NOT EXISTS memory_events (
            id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
            user_id VARCHAR NOT NULL,
            occurred_at TIMESTAMPTZ NOT NULL,
            text TEXT NOT NULL,
            source VARCHAR NOT NULL DEFAULT 'api',
            importance FLOAT DEFAULT 0.5,
            embedding JSONB,
            embedding_vec vector(1536),
            trace_json JSONB,
            created_at TIMESTAMPTZ DEFAULT now(),
            emotion_primary VARCHAR(32),
            emotion_intensity FLOAT,
            energy_level VARCHAR(16)
        )
    """)
    op.execute("""
        CREATE INDEX IF NOT EXISTS ix_memory_events_user_id
        ON memory_events (user_id)
    """)

    # -- 2. memory_clusters --
    op.execute("""
        CREATE TABLE IF NOT EXISTS memory_clusters (
            id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
            user_id VARCHAR NOT NULL,
            label VARCHAR(128) NOT NULL,
            summary_text TEXT,
            cluster_type VARCHAR(32) NOT NULL DEFAULT 'auto',
            fact_count INTEGER NOT NULL DEFAULT 0,
            importance FLOAT DEFAULT 0.5,
            embedding_vec vector(1536),
            is_active BOOLEAN DEFAULT true,
            last_updated_at TIMESTAMPTZ DEFAULT now(),
            created_at TIMESTAMPTZ DEFAULT now()
        )
    """)
    op.execute("""
        CREATE INDEX IF NOT EXISTS ix_memory_clusters_user_id
        ON memory_clusters (user_id)
    """)

    # -- 3. memory_facts --
    op.execute("""
        CREATE TABLE IF NOT EXISTS memory_facts (
            id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
            user_id VARCHAR NOT NULL,
            entity_type VARCHAR NOT NULL,
            entity_key VARCHAR NOT NULL,
            entity_name VARCHAR,
            attribute_key VARCHAR,
            value_json JSONB,
            value_text VARCHAR,
            fact_text TEXT NOT NULL DEFAULT '',
            category VARCHAR(50),
            confidence FLOAT DEFAULT 0.8,
            importance FLOAT DEFAULT 0.5,
            is_sensitive BOOLEAN DEFAULT false,
            needs_confirmation BOOLEAN DEFAULT false,
            valid_from TIMESTAMPTZ NOT NULL,
            valid_to TIMESTAMPTZ,
            ttl_days INTEGER,
            source_event_id UUID REFERENCES memory_events(id),
            supersedes_fact_id UUID,
            embedding JSONB,
            embedding_vec vector(1536),
            last_confirmed_at TIMESTAMPTZ,
            cluster_id UUID REFERENCES memory_clusters(id) ON DELETE SET NULL,
            times_retrieved INTEGER NOT NULL DEFAULT 0,
            last_retrieved_at TIMESTAMPTZ,
            user_correction_count INTEGER NOT NULL DEFAULT 0,
            source_context VARCHAR(512),
            agent_annotation TEXT,
            search_vector tsvector,
            created_at TIMESTAMPTZ DEFAULT now(),
            ingested_at TIMESTAMPTZ DEFAULT now(),
            invalidated_at TIMESTAMPTZ,
            vitality_score FLOAT,
            is_stale BOOLEAN DEFAULT false
        )
    """)
    op.execute("""
        CREATE INDEX IF NOT EXISTS ix_memory_facts_user_id
        ON memory_facts (user_id)
    """)

    # -- 4. memory_meta_observations --
    op.execute("""
        CREATE TABLE IF NOT EXISTS memory_meta_observations (
            id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
            user_id VARCHAR NOT NULL,
            observation_type VARCHAR(32) NOT NULL,
            title VARCHAR(256) NOT NULL,
            text TEXT NOT NULL,
            supporting_event_ids JSONB DEFAULT '[]',
            supporting_fact_ids JSONB DEFAULT '[]',
            confidence FLOAT DEFAULT 0.7,
            importance FLOAT DEFAULT 0.5,
            first_detected_at TIMESTAMPTZ DEFAULT now(),
            last_reinforced_at TIMESTAMPTZ DEFAULT now(),
            times_reinforced INTEGER DEFAULT 1,
            is_active BOOLEAN DEFAULT true,
            embedding_vec vector(1536),
            created_at TIMESTAMPTZ DEFAULT now()
        )
    """)
    op.execute("""
        CREATE INDEX IF NOT EXISTS ix_memory_meta_observations_user_id
        ON memory_meta_observations (user_id)
    """)

    # -- 5. memory_attribute_registry --
    op.execute("""
        CREATE TABLE IF NOT EXISTS memory_attribute_registry (
            id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
            key VARCHAR(64) NOT NULL UNIQUE,
            status VARCHAR(20) NOT NULL DEFAULT 'proposed',
            value_type VARCHAR(20) NOT NULL DEFAULT 'string',
            conflict_policy VARCHAR(20) NOT NULL DEFAULT 'supersede',
            ttl_days INTEGER,
            first_seen_at TIMESTAMPTZ DEFAULT now(),
            last_seen_at TIMESTAMPTZ DEFAULT now(),
            seen_count INTEGER NOT NULL DEFAULT 1,
            proposed_by VARCHAR(20) NOT NULL DEFAULT 'llm',
            reason TEXT,
            example_raw_key VARCHAR(128),
            created_at TIMESTAMPTZ DEFAULT now(),
            updated_at TIMESTAMPTZ
        )
    """)

    # -- 6. memory_entities --
    op.execute("""
        CREATE TABLE IF NOT EXISTS memory_entities (
            id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
            user_id VARCHAR NOT NULL,
            canonical_key VARCHAR(128) NOT NULL,
            display_name VARCHAR(256),
            entity_type VARCHAR(32) NOT NULL,
            summary_text TEXT,
            embedding_vec vector(1536),
            first_seen_at TIMESTAMPTZ DEFAULT now(),
            last_seen_at TIMESTAMPTZ DEFAULT now(),
            fact_count INTEGER NOT NULL DEFAULT 0,
            is_active BOOLEAN DEFAULT true,
            importance_score FLOAT,
            summary_refreshed_at TIMESTAMPTZ,
            created_at TIMESTAMPTZ DEFAULT now(),
            CONSTRAINT uq_memory_entity_user_key UNIQUE (user_id, canonical_key)
        )
    """)
    op.execute("""
        CREATE INDEX IF NOT EXISTS ix_memory_entity_user_active
        ON memory_entities (user_id, is_active)
    """)

    # -- 7. memory_entity_aliases --
    op.execute("""
        CREATE TABLE IF NOT EXISTS memory_entity_aliases (
            id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
            user_id VARCHAR NOT NULL,
            alias VARCHAR NOT NULL,
            canonical_entity_key VARCHAR NOT NULL,
            canonical_entity_type VARCHAR NOT NULL,
            created_at TIMESTAMPTZ DEFAULT now(),
            CONSTRAINT uq_entity_alias_user_alias UNIQUE (user_id, alias)
        )
    """)
    op.execute("""
        CREATE INDEX IF NOT EXISTS ix_entity_alias_user_lookup
        ON memory_entity_aliases (user_id, alias)
    """)

    # -- 8. memory_entity_relationships --
    op.execute("""
        CREATE TABLE IF NOT EXISTS memory_entity_relationships (
            id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
            user_id VARCHAR NOT NULL,
            source_entity_key VARCHAR(128) NOT NULL,
            target_entity_key VARCHAR(128) NOT NULL,
            rel_type VARCHAR(64) NOT NULL,
            strength FLOAT DEFAULT 0.8,
            evidence_fact_id UUID REFERENCES memory_facts(id) ON DELETE SET NULL,
            provenance VARCHAR(16) DEFAULT 'rule',
            created_at TIMESTAMPTZ DEFAULT now(),
            updated_at TIMESTAMPTZ DEFAULT now(),
            last_used_at TIMESTAMPTZ,
            valid_from TIMESTAMPTZ DEFAULT now(),
            valid_to TIMESTAMPTZ,
            invalidated_at TIMESTAMPTZ,
            CONSTRAINT uq_entity_relationship UNIQUE (user_id, source_entity_key, target_entity_key, rel_type)
        )
    """)
    op.execute("""
        CREATE INDEX IF NOT EXISTS ix_entity_rel_source
        ON memory_entity_relationships (user_id, source_entity_key)
    """)
    op.execute("""
        CREATE INDEX IF NOT EXISTS ix_entity_rel_target
        ON memory_entity_relationships (user_id, target_entity_key)
    """)

    # -- 9. memory_intentions --
    op.execute("""
        CREATE TABLE IF NOT EXISTS memory_intentions (
            id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
            user_id VARCHAR NOT NULL,
            trigger_type VARCHAR(16) NOT NULL,
            trigger_condition TEXT NOT NULL,
            intended_action TEXT NOT NULL,
            due_date TIMESTAMPTZ,
            status VARCHAR(16) NOT NULL DEFAULT 'pending',
            trigger_embedding_vec vector(1536),
            source_context VARCHAR(32),
            outcome_note TEXT,
            created_at TIMESTAMPTZ DEFAULT now(),
            triggered_at TIMESTAMPTZ,
            fulfilled_at TIMESTAMPTZ
        )
    """)
    op.execute("""
        CREATE INDEX IF NOT EXISTS ix_memory_intentions_user_id
        ON memory_intentions (user_id)
    """)

    # -- 10. session_observations --
    op.execute("""
        CREATE TABLE IF NOT EXISTS session_observations (
            id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
            user_id VARCHAR NOT NULL,
            content TEXT NOT NULL,
            topic VARCHAR(64),
            entities_mentioned JSONB DEFAULT '[]',
            created_at TIMESTAMPTZ DEFAULT now(),
            referenced_at TIMESTAMPTZ,
            relative_offset VARCHAR(64),
            source_message_ids JSONB DEFAULT '[]',
            is_active BOOLEAN DEFAULT true,
            merged_into_id UUID,
            emotion_label VARCHAR(32),
            embedding_vec vector(1536)
        )
    """)
    op.execute("""
        CREATE INDEX IF NOT EXISTS idx_session_obs_user_active
        ON session_observations (user_id, is_active)
    """)


def downgrade() -> None:
    op.execute("DROP TABLE IF EXISTS session_observations CASCADE")
    op.execute("DROP TABLE IF EXISTS memory_intentions CASCADE")
    op.execute("DROP TABLE IF EXISTS memory_entity_relationships CASCADE")
    op.execute("DROP TABLE IF EXISTS memory_entity_aliases CASCADE")
    op.execute("DROP TABLE IF EXISTS memory_entities CASCADE")
    op.execute("DROP TABLE IF EXISTS memory_attribute_registry CASCADE")
    op.execute("DROP TABLE IF EXISTS memory_meta_observations CASCADE")
    op.execute("DROP TABLE IF EXISTS memory_facts CASCADE")
    op.execute("DROP TABLE IF EXISTS memory_clusters CASCADE")
    op.execute("DROP TABLE IF EXISTS memory_events CASCADE")
