from sqlalchemy import create_engine, text
from sqlalchemy.orm import sessionmaker

from app.config import settings


engine = create_engine(settings.database_url, pool_pre_ping=True)

SessionLocal = sessionmaker(
    autocommit=False,
    autoflush=False,
    bind=engine,
)


_CORE_SCHEMA_READY = False


def ensure_core_schema() -> None:
    """Cria o schema base do ClientFlow numa base PostgreSQL vazia.

    As versões anteriores assumiam que `tasks`, `messages`, `raw_events` e
    `action_runs` já existiam. Numa instalação limpa isso fazia o arranque
    falhar quando a camada de oportunidades tentava executar:

        ALTER TABLE tasks ADD COLUMN ...

    Esta função é aditiva e segura para bases existentes: cria tabelas e
    índices apenas se não existirem e adiciona colunas opcionais em falta.
    """
    global _CORE_SCHEMA_READY
    if _CORE_SCHEMA_READY:
        return

    with engine.begin() as conn:
        # Necessário para DEFAULT gen_random_uuid(). Em PostgreSQL moderno,
        # pgcrypto é a forma mais simples de gerar UUIDs no próprio servidor.
        conn.execute(text("CREATE EXTENSION IF NOT EXISTS pgcrypto"))

        conn.execute(text("""
            CREATE TABLE IF NOT EXISTS schema_migrations (
                version TEXT PRIMARY KEY,
                name TEXT NOT NULL,
                applied_at TIMESTAMPTZ NOT NULL DEFAULT now()
            )
        """))

        conn.execute(text("""
            CREATE TABLE IF NOT EXISTS raw_events (
                id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
                source_system TEXT NOT NULL DEFAULT 'clientflow',
                event_type TEXT,
                source_event_id TEXT,
                conversation_id TEXT,
                contact_id TEXT,
                payload JSONB NOT NULL DEFAULT '{}'::jsonb,
                processed BOOLEAN NOT NULL DEFAULT FALSE,
                ignored BOOLEAN NOT NULL DEFAULT FALSE,
                processing_error TEXT,
                message_id UUID,
                action_run_id UUID,
                created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
                processed_at TIMESTAMPTZ
            )
        """))

        conn.execute(text("""
            CREATE UNIQUE INDEX IF NOT EXISTS ux_raw_events_source_event
            ON raw_events(source_system, source_event_id)
            WHERE source_event_id IS NOT NULL
        """))

        conn.execute(text("""
            CREATE TABLE IF NOT EXISTS messages (
                id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
                raw_event_id UUID,
                source_system TEXT NOT NULL DEFAULT 'clientflow',
                source_event_id TEXT,
                conversation_id TEXT,
                contact_id TEXT,
                direction TEXT NOT NULL DEFAULT 'inbound',
                raw_body TEXT,
                clean_body TEXT,
                previous_context TEXT,
                metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
                created_at TIMESTAMPTZ NOT NULL DEFAULT now()
            )
        """))

        conn.execute(text("""
            CREATE TABLE IF NOT EXISTS action_runs (
                id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
                message_id UUID,
                raw_event_id UUID,
                conversation_id TEXT,
                contact_id TEXT,
                source_system TEXT NOT NULL DEFAULT 'clientflow',
                model TEXT,
                provider TEXT,
                openrouter_generation_id TEXT,
                decision_source TEXT,
                action_decision JSONB NOT NULL DEFAULT '{}'::jsonb,
                action_result JSONB NOT NULL DEFAULT '{}'::jsonb,
                prompt_tokens INTEGER,
                completion_tokens INTEGER,
                total_tokens INTEGER,
                cost NUMERIC(12,6),
                usage JSONB NOT NULL DEFAULT '{}'::jsonb,
                needs_review BOOLEAN NOT NULL DEFAULT FALSE,
                created_at TIMESTAMPTZ NOT NULL DEFAULT now()
            )
        """))

        conn.execute(text("""
            CREATE TABLE IF NOT EXISTS tasks (
                id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
                action_run_id UUID,
                message_id UUID,
                raw_event_id UUID,
                opportunity_id UUID,
                conversation_id TEXT,
                contact_id TEXT,
                customer_id TEXT,
                action_code TEXT NOT NULL DEFAULT 'REVIEW_MANUALLY',
                route TEXT NOT NULL DEFAULT 'rever',
                action TEXT NOT NULL DEFAULT 'Rever manualmente',
                note TEXT,
                action_required BOOLEAN NOT NULL DEFAULT FALSE,
                safe_to_post BOOLEAN NOT NULL DEFAULT FALSE,
                status TEXT NOT NULL DEFAULT 'pending',
                source_system TEXT NOT NULL DEFAULT 'clientflow',
                source_event_id TEXT,
                idempotency_key TEXT,
                due_at TIMESTAMPTZ,
                created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
                updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
                done_at TIMESTAMPTZ,
                done_by TEXT,
                metadata JSONB NOT NULL DEFAULT '{}'::jsonb
            )
        """))

        # Colunas adicionadas em upgrades anteriores. Mantidas aqui para
        # compatibilidade quando a tabela já existe numa base antiga.
        for statement in [
            "ALTER TABLE tasks ADD COLUMN IF NOT EXISTS opportunity_id UUID",
            "ALTER TABLE tasks ADD COLUMN IF NOT EXISTS due_at TIMESTAMPTZ",
            "ALTER TABLE tasks ADD COLUMN IF NOT EXISTS done_at TIMESTAMPTZ",
            "ALTER TABLE tasks ADD COLUMN IF NOT EXISTS done_by TEXT",
            "ALTER TABLE tasks ADD COLUMN IF NOT EXISTS metadata JSONB NOT NULL DEFAULT '{}'::jsonb",
            "ALTER TABLE tasks ADD COLUMN IF NOT EXISTS idempotency_key TEXT",
            "ALTER TABLE tasks ADD COLUMN IF NOT EXISTS priority TEXT NOT NULL DEFAULT 'normal'",
            "ALTER TABLE tasks ADD COLUMN IF NOT EXISTS assigned_to TEXT",
        ]:
            conn.execute(text(statement))

        conn.execute(text("""
            CREATE UNIQUE INDEX IF NOT EXISTS ux_tasks_idempotency_key
            ON tasks(idempotency_key)
            WHERE idempotency_key IS NOT NULL
        """))
        conn.execute(text("CREATE UNIQUE INDEX IF NOT EXISTS idx_tasks_idempotency_key ON tasks(idempotency_key)"))

        conn.execute(text("""
            CREATE TABLE IF NOT EXISTS task_preparations (
                id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
                task_id UUID NOT NULL,
                conversation_id TEXT,
                contact_id TEXT,
                prep_type TEXT NOT NULL DEFAULT 'generic',
                status TEXT NOT NULL DEFAULT 'draft',
                extracted_data JSONB NOT NULL DEFAULT '{}'::jsonb,
                missing_fields JSONB NOT NULL DEFAULT '[]'::jsonb,
                suggested_reply TEXT,
                confidence NUMERIC(4,3),
                model TEXT,
                provider TEXT,
                total_tokens INTEGER NOT NULL DEFAULT 0,
                cost NUMERIC(12,6) NOT NULL DEFAULT 0,
                raw_response JSONB NOT NULL DEFAULT '{}'::jsonb,
                created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
                updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
            )
        """))

        conn.execute(text("CREATE INDEX IF NOT EXISTS idx_task_preparations_task ON task_preparations(task_id, created_at DESC)"))
        conn.execute(text("CREATE INDEX IF NOT EXISTS idx_task_preparations_conversation ON task_preparations(conversation_id)"))

        conn.execute(text("""
            CREATE TABLE IF NOT EXISTS task_events (
                id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
                task_id UUID NOT NULL,
                event_type TEXT NOT NULL,
                payload JSONB NOT NULL DEFAULT '{}'::jsonb,
                created_by TEXT NOT NULL DEFAULT 'system',
                created_at TIMESTAMPTZ NOT NULL DEFAULT now()
            )
        """))

        conn.execute(text("""
            CREATE TABLE IF NOT EXISTS business_events (
                id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
                event_type TEXT NOT NULL,
                task_id UUID,
                action_run_id UUID,
                message_id UUID,
                raw_event_id UUID,
                customer_id TEXT,
                conversation_id TEXT,
                contact_id TEXT,
                payload JSONB NOT NULL DEFAULT '{}'::jsonb,
                idempotency_key TEXT,
                created_by TEXT NOT NULL DEFAULT 'system',
                created_at TIMESTAMPTZ NOT NULL DEFAULT now()
            )
        """))

        conn.execute(text("""
            CREATE UNIQUE INDEX IF NOT EXISTS ux_business_events_idempotency_key
            ON business_events(idempotency_key)
            WHERE idempotency_key IS NOT NULL
        """))
        conn.execute(text("CREATE UNIQUE INDEX IF NOT EXISTS idx_business_events_idempotency_key ON business_events(idempotency_key)"))

        conn.execute(text("""
            CREATE TABLE IF NOT EXISTS integration_outbox (
                id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
                business_event_id UUID,
                target_system TEXT NOT NULL,
                action_type TEXT NOT NULL,
                payload JSONB NOT NULL DEFAULT '{}'::jsonb,
                status TEXT NOT NULL DEFAULT 'pending',
                retry_count INTEGER NOT NULL DEFAULT 0,
                idempotency_key TEXT,
                last_error TEXT,
                created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
                updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
                sent_at TIMESTAMPTZ
            )
        """))

        # Older ClientFlow installs may already have integration_outbox created
        # without newer operational columns.  The outbox worker selects/updates
        # these columns directly, so guarantee them on every schema init.
        for statement in [
            "ALTER TABLE integration_outbox ADD COLUMN IF NOT EXISTS business_event_id UUID",
            "ALTER TABLE integration_outbox ADD COLUMN IF NOT EXISTS retry_count INTEGER NOT NULL DEFAULT 0",
            "ALTER TABLE integration_outbox ADD COLUMN IF NOT EXISTS idempotency_key TEXT",
            "ALTER TABLE integration_outbox ADD COLUMN IF NOT EXISTS last_error TEXT",
            "ALTER TABLE integration_outbox ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT now()",
            "ALTER TABLE integration_outbox ADD COLUMN IF NOT EXISTS sent_at TIMESTAMPTZ",
            "ALTER TABLE integration_outbox ADD COLUMN IF NOT EXISTS locked_at TIMESTAMPTZ",
            "ALTER TABLE integration_outbox ADD COLUMN IF NOT EXISTS lock_owner TEXT",
            "ALTER TABLE integration_outbox ADD COLUMN IF NOT EXISTS ignored_at TIMESTAMPTZ",
        ]:
            conn.execute(text(statement))

        conn.execute(text("""
            CREATE UNIQUE INDEX IF NOT EXISTS ux_integration_outbox_idempotency_key
            ON integration_outbox(idempotency_key)
            WHERE idempotency_key IS NOT NULL
        """))
        conn.execute(text("CREATE UNIQUE INDEX IF NOT EXISTS idx_integration_outbox_idempotency_key ON integration_outbox(idempotency_key)"))
        conn.execute(text("""
            CREATE INDEX IF NOT EXISTS idx_integration_outbox_pending_dispatch
            ON integration_outbox(target_system, created_at)
            WHERE status = 'pending'
        """))

        conn.execute(text("""
            CREATE TABLE IF NOT EXISTS external_mappings (
                id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
                local_system TEXT NOT NULL DEFAULT 'clientflow',
                local_entity_type TEXT NOT NULL,
                local_entity_id TEXT NOT NULL,
                external_system TEXT NOT NULL,
                external_entity_type TEXT NOT NULL,
                external_entity_id TEXT,
                external_url TEXT,
                match_key TEXT,
                match_value TEXT,
                confidence NUMERIC(4,3),
                metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
                created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
                updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
            )
        """))

        # Índices de consulta mais usados no admin e webhooks.
        conn.execute(text("CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status)"))
        conn.execute(text("CREATE INDEX IF NOT EXISTS idx_tasks_route ON tasks(route)"))
        conn.execute(text("CREATE INDEX IF NOT EXISTS idx_tasks_conversation ON tasks(conversation_id)"))
        conn.execute(text("CREATE INDEX IF NOT EXISTS idx_tasks_contact ON tasks(contact_id)"))
        conn.execute(text("CREATE INDEX IF NOT EXISTS idx_tasks_created_at ON tasks(created_at DESC)"))
        conn.execute(text("CREATE INDEX IF NOT EXISTS idx_raw_events_created_at ON raw_events(created_at DESC)"))
        conn.execute(text("CREATE INDEX IF NOT EXISTS idx_raw_events_conversation ON raw_events(conversation_id)"))
        conn.execute(text("CREATE INDEX IF NOT EXISTS idx_messages_conversation ON messages(conversation_id)"))
        conn.execute(text("CREATE INDEX IF NOT EXISTS idx_action_runs_created_at ON action_runs(created_at DESC)"))
        conn.execute(text("CREATE INDEX IF NOT EXISTS idx_task_events_task ON task_events(task_id, created_at DESC)"))
        conn.execute(text("CREATE INDEX IF NOT EXISTS idx_business_events_created_at ON business_events(created_at DESC)"))
        conn.execute(text("CREATE INDEX IF NOT EXISTS idx_outbox_status ON integration_outbox(status)"))
        conn.execute(text("CREATE INDEX IF NOT EXISTS idx_outbox_target ON integration_outbox(target_system)"))
        conn.execute(text("CREATE INDEX IF NOT EXISTS idx_external_mappings_local ON external_mappings(local_system, local_entity_type, local_entity_id)"))

    _CORE_SCHEMA_READY = True


def init_db() -> None:
    """Inicialização do schema ClientFlow.

    Ordem importante para bases novas:
    1. core: raw_events/messages/action_runs/tasks/outbox;
    2. oportunidades: depende de tasks;
    3. produtos: depende de opportunities.
    """
    try:
        ensure_core_schema()

        from app.opportunity_service import ensure_opportunity_schema
        from app.product_service import ensure_product_schema
        from app.operation_service import ensure_operation_schema
        from app.commercial_service import ensure_commercial_schema
        from app.communication_service import ensure_communication_schema
        from app.reply_assistant_service import ensure_reply_assistant_schema
        from app.reconciliation_service import ensure_reconciliation_schema
        from app.revenue_forecast_service import ensure_revenue_forecast_schema

        ensure_opportunity_schema()
        ensure_product_schema()
        ensure_operation_schema()
        ensure_commercial_schema()
        ensure_communication_schema()
        ensure_reply_assistant_schema()
        ensure_reconciliation_schema()
        ensure_revenue_forecast_schema()
    except Exception as exc:
        print(f"ClientFlow schema init failed: {exc}", flush=True)
        raise
    return None
