"""
Integration tests for Shell schema layer.

Tests use real SQLite databases (tmpdir), no mocking.
Per R8 (Real Wiring): Integration tests use real implementations at boundaries.
"""

import sqlite3
from pathlib import Path
import tempfile

import pytest
from returns.result import Success, Failure

from lattice.shell.schema import (
    create_store,
    add_message_id_column,
)


class TestAddMessageIdColumn:
    """Test migration for adding message_id column."""

    def test_migration_adds_column_to_new_database(self):
        """Test that migration works on a fresh database."""
        with tempfile.TemporaryDirectory() as tmpdir:
            db_path = Path(tmpdir) / "store.db"

            # Create store (which should already include message_id in schema)
            result = create_store(db_path)
            assert isinstance(result, Success)
            conn = result.unwrap()

            # Verify column exists
            cursor = conn.execute(
                "SELECT name FROM pragma_table_info('events') WHERE name = ?",
                ("message_id",),
            )
            assert cursor.fetchone() is not None, "message_id column should exist"

            # Run migration again (idempotent)
            result = add_message_id_column(conn)
            assert isinstance(result, Success)

            # Verify still exists
            cursor = conn.execute(
                "SELECT name FROM pragma_table_info('events') WHERE name = ?",
                ("message_id",),
            )
            assert cursor.fetchone() is not None

            conn.close()

    def test_migration_preserves_existing_data(self):
        """Test that migration on populated database preserves all data."""
        with tempfile.TemporaryDirectory() as tmpdir:
            db_path = Path(tmpdir) / "store.db"

            # Create database WITHOUT message_id column (old schema simulation)
            conn = sqlite3.connect(str(db_path))
            conn.executescript("""
                CREATE TABLE events (
                    id INTEGER PRIMARY KEY,
                    external_id TEXT UNIQUE NOT NULL,
                    session_id TEXT NOT NULL,
                    timestamp TEXT NOT NULL,
                    type TEXT NOT NULL,
                    content TEXT NOT NULL,
                    tool_input TEXT,
                    tool_status TEXT,
                    tool_error TEXT
                );
                CREATE INDEX idx_events_session ON events(session_id);
                CREATE INDEX idx_events_type ON events(type);
            """)

            # Insert test data (simulating existing data before migration)
            conn.execute(
                """INSERT INTO events 
                   (external_id, session_id, timestamp, type, content)
                   VALUES (?, ?, ?, ?, ?)""",
                ("ext-1", "session-old", "2024-01-01T00:00:00Z", "user", "Hello"),
            )
            conn.execute(
                """INSERT INTO events 
                   (external_id, session_id, timestamp, type, content)
                   VALUES (?, ?, ?, ?, ?)""",
                (
                    "ext-2",
                    "session-old",
                    "2024-01-01T00:00:01Z",
                    "assistant",
                    "Hi there",
                ),
            )
            conn.execute(
                """INSERT INTO events 
                   (external_id, session_id, timestamp, type, content, tool_input, tool_status)
                   VALUES (?, ?, ?, ?, ?, ?, ?)""",
                (
                    "ext-3",
                    "session-old",
                    "2024-01-01T00:00:02Z",
                    "tool",
                    "read",
                    "file.py",
                    "success",
                ),
            )
            conn.commit()

            # Count before migration
            cursor = conn.execute("SELECT COUNT(*) FROM events")
            count_before = cursor.fetchone()[0]
            assert count_before == 3, "Should have 3 events before migration"

            # Run migration
            result = add_message_id_column(conn)
            assert isinstance(result, Success), f"Migration failed: {result}"

            # Verify column was added
            cursor = conn.execute(
                "SELECT name FROM pragma_table_info('events') WHERE name = ?",
                ("message_id",),
            )
            assert cursor.fetchone() is not None, "message_id column should be added"

            # Verify index was created
            cursor = conn.execute(
                "SELECT name FROM sqlite_master WHERE type='index' AND name='idx_events_message_id'"
            )
            assert cursor.fetchone() is not None, (
                "idx_events_message_id should be created"
            )

            # Verify data preserved
            cursor = conn.execute("SELECT COUNT(*) FROM events")
            count_after = cursor.fetchone()[0]
            assert count_after == 3, "All events should be preserved"

            # Verify content preserved
            cursor = conn.execute(
                "SELECT external_id, type, content FROM events ORDER BY id"
            )
            rows = cursor.fetchall()
            assert rows[0] == ("ext-1", "user", "Hello")
            assert rows[1] == ("ext-2", "assistant", "Hi there")
            assert rows[2] == ("ext-3", "tool", "read")

            # Verify message_id is NULL for existing rows
            cursor = conn.execute(
                "SELECT message_id FROM events WHERE external_id = ?", ("ext-1",)
            )
            assert cursor.fetchone()[0] is None, (
                "message_id should be NULL for old rows"
            )

            conn.close()

    def test_migration_is_idempotent(self):
        """Test that running migration multiple times is safe."""
        with tempfile.TemporaryDirectory() as tmpdir:
            db_path = Path(tmpdir) / "store.db"
            conn = sqlite3.connect(str(db_path))

            # Create table without message_id
            conn.execute("""
                CREATE TABLE events (
                    id INTEGER PRIMARY KEY,
                    external_id TEXT UNIQUE NOT NULL,
                    session_id TEXT NOT NULL,
                    timestamp TEXT NOT NULL,
                    type TEXT NOT NULL,
                    content TEXT NOT NULL
                )
            """)
            conn.commit()

            # Run migration first time
            result1 = add_message_id_column(conn)
            assert isinstance(result1, Success)

            # Run migration second time
            result2 = add_message_id_column(conn)
            assert isinstance(result2, Success), "Second migration should succeed"

            # Run migration third time
            result3 = add_message_id_column(conn)
            assert isinstance(result3, Success), "Third migration should succeed"

            # Verify column exists exactly once
            cursor = conn.execute(
                "SELECT COUNT(*) FROM pragma_table_info('events') WHERE name = ?",
                ("message_id",),
            )
            assert cursor.fetchone()[0] == 1, "Column should exist exactly once"

            conn.close()

    def test_migration_with_fts_and_vec_tables(self):
        """Test migration works with FTS and vector tables present."""
        with tempfile.TemporaryDirectory() as tmpdir:
            # Use create_store to get full schema including FTS and vec
            db_path = Path(tmpdir) / "store.db"
            result = create_store(db_path)
            assert isinstance(result, Success)
            conn = result.unwrap()

            # Insert some data
            conn.execute(
                """INSERT INTO events 
                   (external_id, session_id, timestamp, type, content, message_id)
                   VALUES (?, ?, ?, ?, ?, ?)""",
                ("ext-1", "session-1", "2024-01-01T00:00:00Z", "user", "test", "msg-1"),
            )
            conn.commit()

            # Run migration (should be idempotent)
            result = add_message_id_column(conn)
            assert isinstance(result, Success)

            # Verify data still exists
            cursor = conn.execute("SELECT COUNT(*) FROM events")
            assert cursor.fetchone()[0] == 1

            conn.close()
