"""
Tests for Hippo SQLite schema creation and migrations.

Issue: #801
"""

from __future__ import annotations

import pytest
import aiosqlite

from cortex.hippo.db.connection import HippoDatabase
from cortex.hippo.db.schema import HIPPO_SCHEMA_VERSION
from cortex.hippo.config import HippoConfig


@pytest.mark.asyncio
async def test_schema_creation():
    """All Hippo tables are created in an in-memory DB."""
    config = HippoConfig(db_path=":memory:")
    db = HippoDatabase(config)
    await db.initialize()

    conn = db.conn

    # Check all expected tables exist
    expected_tables = [
        "hippo_conversations",
        "hippo_messages",
        "hippo_summaries",
        "hippo_summary_parents",
        "hippo_summary_messages",
        "hippo_context_items",
        "hippo_message_parts",
        "hippo_large_files",
        "hippo_signals",
        "hippo_config",
    ]

    async with conn.execute(
        "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
    ) as cur:
        rows = await cur.fetchall()
        table_names = [r[0] for r in rows]

    for expected in expected_tables:
        assert expected in table_names, f"Table {expected} not found. Got: {table_names}"

    await db.close()


@pytest.mark.asyncio
async def test_schema_version_stored():
    """Schema version is stored in hippo_config after creation."""
    config = HippoConfig(db_path=":memory:")
    db = HippoDatabase(config)
    await db.initialize()

    version = await db.get_schema_version()
    assert version == HIPPO_SCHEMA_VERSION

    await db.close()


@pytest.mark.asyncio
async def test_schema_idempotent():
    """Running initialize() twice doesn't fail."""
    config = HippoConfig(db_path=":memory:")
    db = HippoDatabase(config)
    await db.initialize()

    # Second init on same connection — should be idempotent
    await db._create_schema()

    version = await db.get_schema_version()
    assert version == HIPPO_SCHEMA_VERSION

    await db.close()


@pytest.mark.asyncio
async def test_foreign_keys_enabled():
    """Foreign key enforcement is on."""
    config = HippoConfig(db_path=":memory:")
    db = HippoDatabase(config)
    await db.initialize()

    async with db.conn.execute("PRAGMA foreign_keys") as cur:
        row = await cur.fetchone()
        assert row[0] == 1

    await db.close()


@pytest.mark.asyncio
async def test_owner_id_column_exists():
    """All tenant-isolated tables have owner_id column."""
    config = HippoConfig(db_path=":memory:")
    db = HippoDatabase(config)
    await db.initialize()

    tables_with_owner = [
        "hippo_conversations",
        "hippo_messages",
        "hippo_summaries",
        "hippo_signals",
    ]

    for table in tables_with_owner:
        async with db.conn.execute(f"PRAGMA table_info({table})") as cur:
            rows = await cur.fetchall()
            col_names = [r[1] for r in rows]
            assert "owner_id" in col_names, f"owner_id missing from {table}"

    await db.close()


@pytest.mark.asyncio
async def test_messages_role_check_constraint():
    """Messages table enforces role CHECK constraint."""
    config = HippoConfig(db_path=":memory:")
    db = HippoDatabase(config)
    await db.initialize()

    # First create a conversation
    await db.conn.execute(
        "INSERT INTO hippo_conversations (owner_id, session_id) VALUES ('test', 'sess1')"
    )
    await db.conn.commit()

    # Valid role should work
    await db.conn.execute(
        """INSERT INTO hippo_messages (conversation_id, owner_id, seq, role, content, token_count)
           VALUES (1, 'test', 1, 'user', 'hello', 2)"""
    )
    await db.conn.commit()

    # Invalid role should fail
    with pytest.raises(Exception):
        await db.conn.execute(
            """INSERT INTO hippo_messages (conversation_id, owner_id, seq, role, content, token_count)
               VALUES (1, 'test', 2, 'invalid_role', 'bad', 1)"""
        )

    await db.close()


@pytest.mark.asyncio
async def test_indexes_created():
    """Key indexes are created."""
    config = HippoConfig(db_path=":memory:")
    db = HippoDatabase(config)
    await db.initialize()

    async with db.conn.execute(
        "SELECT name FROM sqlite_master WHERE type='index' AND name LIKE 'idx_hippo_%'"
    ) as cur:
        rows = await cur.fetchall()
        index_names = [r[0] for r in rows]

    expected_indexes = [
        "idx_hippo_conversations_owner_session",
        "idx_hippo_conversations_owner",
        "idx_hippo_messages_conversation_seq",
        "idx_hippo_messages_owner",
        "idx_hippo_summaries_conversation_depth",
        "idx_hippo_summaries_owner",
    ]

    for expected in expected_indexes:
        assert expected in index_names, f"Index {expected} not found. Got: {index_names}"

    await db.close()
