"""Tests for migration 040_inference_registry.sql.

Validates:
- SQL is syntactically valid (parseable)
- Required tables are created (inference_endpoints, model_deployments, agent_stage_bindings)
- Required constraints exist (protocol CHECK, UNIQUE composites)
- Lineage columns added to agent_performance_log
- Migration is idempotent (uses IF NOT EXISTS / IF NOT EXISTS patterns)
"""

import re
from pathlib import Path

import pytest

MIGRATION_PATH = Path(__file__).resolve().parent.parent / "infra" / "migrations" / "040_inference_registry.sql"


@pytest.fixture
def migration_sql() -> str:
    """Load the migration SQL content."""
    assert MIGRATION_PATH.exists(), f"Migration file not found: {MIGRATION_PATH}"
    return MIGRATION_PATH.read_text()


class TestMigrationFileExists:
    def test_file_exists(self):
        assert MIGRATION_PATH.exists()

    def test_file_not_empty(self):
        content = MIGRATION_PATH.read_text()
        assert len(content.strip()) > 100


class TestSQLSyntax:
    """Basic syntax validation via regex pattern checks."""

    def test_no_unclosed_parentheses(self, migration_sql: str):
        """Every CREATE TABLE block has balanced parentheses."""
        # Remove comments
        sql = re.sub(r"--[^\n]*", "", migration_sql)
        # Remove string literals
        sql = re.sub(r"'[^']*'", "''", sql)
        open_count = sql.count("(")
        close_count = sql.count(")")
        assert open_count == close_count, (
            f"Unbalanced parentheses: {open_count} open vs {close_count} close"
        )

    def test_no_trailing_commas_before_close_paren(self, migration_sql: str):
        """No trailing comma before closing paren in CREATE TABLE."""
        # Pattern: comma followed by optional whitespace/newline then )
        # This is a common SQL syntax error
        sql = re.sub(r"--[^\n]*", "", migration_sql)
        matches = re.findall(r",\s*\)", sql)
        assert len(matches) == 0, f"Trailing commas before ')': {matches}"

    def test_all_statements_terminated(self, migration_sql: str):
        """Every SQL statement ends with a semicolon."""
        # Remove comments and empty lines
        sql = re.sub(r"--[^\n]*", "", migration_sql)
        # Remove function bodies (between $$ markers)
        sql = re.sub(r"\$\$.*?\$\$", "$$BODY$$", sql, flags=re.DOTALL)
        # Find significant lines that look like statements but don't end with ;
        lines = [ln.strip() for ln in sql.split("\n") if ln.strip()]
        # We just check that the overall content has properly terminated statements
        # by checking that we have multiple semicolons
        semicolons = migration_sql.count(";")
        assert semicolons >= 10, f"Expected at least 10 semicolons, got {semicolons}"


class TestTableCreation:
    """Verify all required tables are defined."""

    def test_inference_endpoints_table(self, migration_sql: str):
        assert "CREATE TABLE IF NOT EXISTS inference_endpoints" in migration_sql

    def test_model_deployments_table(self, migration_sql: str):
        assert "CREATE TABLE IF NOT EXISTS model_deployments" in migration_sql

    def test_agent_stage_bindings_table(self, migration_sql: str):
        assert "CREATE TABLE IF NOT EXISTS agent_stage_bindings" in migration_sql


class TestInferenceEndpointsColumns:
    """Verify inference_endpoints has required columns."""

    def test_has_id_primary_key(self, migration_sql: str):
        block = _extract_create_block(migration_sql, "inference_endpoints")
        assert "id UUID PRIMARY KEY" in block

    def test_has_name_unique(self, migration_sql: str):
        block = _extract_create_block(migration_sql, "inference_endpoints")
        assert "name TEXT NOT NULL UNIQUE" in block

    def test_has_protocol_check(self, migration_sql: str):
        block = _extract_create_block(migration_sql, "inference_endpoints")
        assert "CHECK" in block
        assert "ollama_native" in block
        assert "openai_chat" in block
        assert "specialist_http" in block

    def test_has_base_url(self, migration_sql: str):
        block = _extract_create_block(migration_sql, "inference_endpoints")
        assert "base_url TEXT NOT NULL" in block

    def test_has_auth_secret_ref(self, migration_sql: str):
        block = _extract_create_block(migration_sql, "inference_endpoints")
        assert "auth_secret_ref TEXT" in block

    def test_has_auth_scheme_default(self, migration_sql: str):
        block = _extract_create_block(migration_sql, "inference_endpoints")
        assert "auth_scheme" in block
        assert "'bearer'" in block

    def test_has_enabled_default_true(self, migration_sql: str):
        block = _extract_create_block(migration_sql, "inference_endpoints")
        assert "enabled BOOLEAN NOT NULL DEFAULT TRUE" in block

    def test_has_revision(self, migration_sql: str):
        block = _extract_create_block(migration_sql, "inference_endpoints")
        assert "revision INTEGER NOT NULL DEFAULT 1" in block

    def test_has_timestamps(self, migration_sql: str):
        block = _extract_create_block(migration_sql, "inference_endpoints")
        assert "created_at TIMESTAMPTZ" in block
        assert "updated_at TIMESTAMPTZ" in block


class TestModelDeploymentsColumns:
    """Verify model_deployments has required columns and FK."""

    def test_has_endpoint_fk(self, migration_sql: str):
        block = _extract_create_block(migration_sql, "model_deployments")
        assert "REFERENCES inference_endpoints(id)" in block

    def test_has_served_model_name(self, migration_sql: str):
        block = _extract_create_block(migration_sql, "model_deployments")
        assert "served_model_name TEXT NOT NULL" in block

    def test_has_capabilities_jsonb(self, migration_sql: str):
        block = _extract_create_block(migration_sql, "model_deployments")
        assert "capabilities JSONB NOT NULL" in block

    def test_has_context_window(self, migration_sql: str):
        block = _extract_create_block(migration_sql, "model_deployments")
        assert "context_window INTEGER" in block

    def test_has_unique_endpoint_model(self, migration_sql: str):
        block = _extract_create_block(migration_sql, "model_deployments")
        assert "UNIQUE(endpoint_id, served_model_name)" in block

    def test_has_timestamps(self, migration_sql: str):
        block = _extract_create_block(migration_sql, "model_deployments")
        assert "created_at TIMESTAMPTZ" in block
        assert "updated_at TIMESTAMPTZ" in block


class TestAgentStageBindingsColumns:
    """Verify agent_stage_bindings has required columns and FKs."""

    def test_has_agent_fk(self, migration_sql: str):
        block = _extract_create_block(migration_sql, "agent_stage_bindings")
        assert "REFERENCES ai_agents(id)" in block

    def test_has_model_deployment_fk(self, migration_sql: str):
        block = _extract_create_block(migration_sql, "agent_stage_bindings")
        assert "REFERENCES model_deployments(id)" in block

    def test_has_stage_column(self, migration_sql: str):
        block = _extract_create_block(migration_sql, "agent_stage_bindings")
        assert "stage TEXT NOT NULL" in block

    def test_has_route_order(self, migration_sql: str):
        block = _extract_create_block(migration_sql, "agent_stage_bindings")
        assert "route_order INTEGER NOT NULL DEFAULT 0" in block

    def test_has_unique_agent_stage_order(self, migration_sql: str):
        block = _extract_create_block(migration_sql, "agent_stage_bindings")
        assert "UNIQUE(agent_id, stage, route_order)" in block

    def test_has_is_active(self, migration_sql: str):
        block = _extract_create_block(migration_sql, "agent_stage_bindings")
        assert "is_active BOOLEAN NOT NULL DEFAULT TRUE" in block

    def test_has_timestamps(self, migration_sql: str):
        block = _extract_create_block(migration_sql, "agent_stage_bindings")
        assert "created_at TIMESTAMPTZ" in block
        assert "updated_at TIMESTAMPTZ" in block


class TestIndexes:
    """Verify required indexes are created."""

    def test_endpoint_protocol_index(self, migration_sql: str):
        assert "idx_inference_endpoints_protocol" in migration_sql
        assert "ON inference_endpoints(protocol)" in migration_sql

    def test_deployment_endpoint_index(self, migration_sql: str):
        assert "idx_model_deployments_endpoint" in migration_sql
        assert "ON model_deployments(endpoint_id)" in migration_sql

    def test_binding_agent_index(self, migration_sql: str):
        assert "idx_agent_stage_bindings_agent" in migration_sql
        assert "ON agent_stage_bindings(agent_id)" in migration_sql

    def test_binding_deployment_index(self, migration_sql: str):
        assert "idx_agent_stage_bindings_deployment" in migration_sql
        assert "ON agent_stage_bindings(model_deployment_id)" in migration_sql


class TestUpdatedAtTrigger:
    """Verify the updated_at trigger function and triggers exist."""

    def test_trigger_function_defined(self, migration_sql: str):
        assert "CREATE OR REPLACE FUNCTION update_updated_at_column()" in migration_sql

    def test_trigger_on_inference_endpoints(self, migration_sql: str):
        assert "trg_inference_endpoints_updated_at" in migration_sql

    def test_trigger_on_model_deployments(self, migration_sql: str):
        assert "trg_model_deployments_updated_at" in migration_sql

    def test_trigger_on_agent_stage_bindings(self, migration_sql: str):
        assert "trg_agent_stage_bindings_updated_at" in migration_sql


class TestLineageColumns:
    """Verify additive lineage columns on agent_performance_log."""

    def test_endpoint_id_column(self, migration_sql: str):
        assert "ADD COLUMN IF NOT EXISTS endpoint_id UUID" in migration_sql
        assert "REFERENCES inference_endpoints(id)" in migration_sql

    def test_deployment_id_column(self, migration_sql: str):
        assert "ADD COLUMN IF NOT EXISTS deployment_id UUID" in migration_sql
        assert "REFERENCES model_deployments(id)" in migration_sql

    def test_binding_revision_column(self, migration_sql: str):
        assert "ADD COLUMN IF NOT EXISTS binding_revision INTEGER" in migration_sql

    def test_structured_mode_column(self, migration_sql: str):
        assert "ADD COLUMN IF NOT EXISTS structured_mode TEXT" in migration_sql


class TestIdempotency:
    """Verify the migration uses idempotent patterns."""

    def test_create_table_if_not_exists(self, migration_sql: str):
        creates = re.findall(r"CREATE TABLE\b", migration_sql)
        creates_idempotent = re.findall(r"CREATE TABLE IF NOT EXISTS", migration_sql)
        assert len(creates) == len(creates_idempotent), (
            "All CREATE TABLE should use IF NOT EXISTS"
        )

    def test_create_index_if_not_exists(self, migration_sql: str):
        indexes = re.findall(r"CREATE INDEX\b", migration_sql)
        indexes_idempotent = re.findall(r"CREATE INDEX IF NOT EXISTS", migration_sql)
        assert len(indexes) == len(indexes_idempotent), (
            "All CREATE INDEX should use IF NOT EXISTS"
        )

    def test_alter_table_if_not_exists(self, migration_sql: str):
        alters = re.findall(r"ADD COLUMN\b", migration_sql)
        alters_idempotent = re.findall(r"ADD COLUMN IF NOT EXISTS", migration_sql)
        assert len(alters) == len(alters_idempotent), (
            "All ADD COLUMN should use IF NOT EXISTS"
        )

    def test_trigger_uses_drop_if_exists(self, migration_sql: str):
        """Triggers use DROP IF EXISTS before CREATE for idempotency."""
        drops = re.findall(r"DROP TRIGGER IF EXISTS", migration_sql)
        creates = re.findall(r"CREATE TRIGGER", migration_sql)
        assert len(drops) == len(creates), (
            "Each CREATE TRIGGER should be preceded by DROP TRIGGER IF EXISTS"
        )


# ─── Helpers ───────────────────────────────────────────────────────────────────

def _extract_create_block(sql: str, table_name: str) -> str:
    """Extract the CREATE TABLE block for a given table name."""
    pattern = rf"CREATE TABLE IF NOT EXISTS {table_name}\s*\((.*?)\);"
    match = re.search(pattern, sql, re.DOTALL)
    assert match is not None, f"Could not find CREATE TABLE block for {table_name}"
    return match.group(1)
