"""Tests for database initialisation."""
import sqlite3
import pytest
from bigmind.db import get_db_path, get_connection, init_db, _migrate_v1_to_v2, _migrate_v2_to_v3, _migrate_v3_to_v4, _migrate_v4_to_v5


class TestDbInit:
    def test_db_file_created(self, temp_db):
        assert temp_db.exists()

    def test_schema_version_is_8(self, temp_db):
        conn = get_connection()
        row = conn.execute("SELECT version FROM schema_version").fetchone()
        conn.close()
        assert row is not None
        assert row["version"] == 8

    def test_all_tables_exist(self, temp_db):
        expected = {
            "users", "identity_profile", "sessions",
            "session_summaries", "conversation_chunks", "facts",
            "global_knowledge", "hypotheses", "upgrade_requests",
            "gallery_images",
        }
        conn = get_connection()
        rows = conn.execute(
            "SELECT name FROM sqlite_master WHERE type='table'"
        ).fetchall()
        conn.close()
        found = {r["name"] for r in rows}
        assert expected.issubset(found)

    def test_facts_fts_table_exists(self, temp_db):
        conn = get_connection()
        rows = conn.execute(
            "SELECT name FROM sqlite_master WHERE type='table' AND name='facts_fts'"
        ).fetchall()
        conn.close()
        assert len(rows) == 1

    def test_facts_has_deprecated_columns(self, temp_db):
        conn = get_connection()
        rows = conn.execute("PRAGMA table_info(facts)").fetchall()
        conn.close()
        col_names = {r["name"] for r in rows}
        assert "deprecated" in col_names
        assert "deprecation_reason" in col_names

    def test_hypotheses_has_correct_columns(self, temp_db):
        conn = get_connection()
        rows = conn.execute("PRAGMA table_info(hypotheses)").fetchall()
        conn.close()
        col_names = {r["name"] for r in rows}
        for col in ("id", "session_id", "user_id", "hypothesis",
                    "confidence", "status", "resolution",
                    "created_at", "resolved_at"):
            assert col in col_names, f"Missing column: {col}"

    def test_init_is_idempotent(self, temp_db):
        """Calling init_db() twice must not raise."""
        init_db()
        init_db()

    def test_db_path_override_via_env(self, temp_db):
        assert str(get_db_path()) == str(temp_db)


class TestMigrationV1ToV2:
    def test_migration_adds_columns_to_existing_table(self, temp_db):
        """Simulate a v1 DB: facts table without deprecated columns."""
        conn = get_connection()
        conn.execute("DROP TABLE IF EXISTS facts")
        conn.execute("""CREATE TABLE facts (
            id             INTEGER PRIMARY KEY AUTOINCREMENT,
            user_id        TEXT NOT NULL,
            category       TEXT NOT NULL,
            fact           TEXT NOT NULL,
            source_session TEXT,
            confidence     REAL DEFAULT 1.0,
            created_at     DATETIME DEFAULT CURRENT_TIMESTAMP,
            updated_at     DATETIME DEFAULT CURRENT_TIMESTAMP
        )""")
        conn.commit()
        rows = conn.execute("PRAGMA table_info(facts)").fetchall()
        assert "deprecated" not in {r["name"] for r in rows}
        _migrate_v1_to_v2(conn)
        conn.commit()
        rows = conn.execute("PRAGMA table_info(facts)").fetchall()
        conn.close()
        col_names = {r["name"] for r in rows}
        assert "deprecated" in col_names
        assert "deprecation_reason" in col_names

    def test_migration_is_idempotent(self, temp_db):
        """Running v1→v2 twice must not raise."""
        conn = get_connection()
        _migrate_v1_to_v2(conn)
        conn.commit()
        conn.close()


class TestMigrationV4ToV5:
    def test_migration_creates_facts_fts(self, temp_db):
        conn = get_connection()
        conn.execute("DROP TABLE IF EXISTS facts_fts")
        conn.commit()
        _migrate_v4_to_v5(conn)
        conn.commit()
        rows = conn.execute(
            "SELECT name FROM sqlite_master WHERE type='table' AND name='facts_fts'"
        ).fetchall()
        conn.close()
        assert len(rows) == 1

    def test_migration_backfills_existing_facts(self, temp_db):
        from bigmind import memory_store
        user = memory_store.get_or_create_user("backfill_user")
        # Insert a fact directly (bypassing FTS sync) to simulate pre-v5 data
        conn = get_connection()
        conn.execute(
            "INSERT INTO facts (user_id, category, fact) VALUES (?,?,?)",
            (user["id"], "codebase", "pre-migration fact about SQLite")
        )
        conn.execute("DROP TABLE IF EXISTS facts_fts")
        conn.commit()
        _migrate_v4_to_v5(conn)
        conn.commit()
        conn.close()
        results = memory_store.search_facts(user["id"], "SQLite")
        assert len(results) == 1

    def test_migration_is_idempotent(self, temp_db):
        conn = get_connection()
        _migrate_v4_to_v5(conn)
        conn.commit()
        conn.close()


class TestMigrationV3ToV4:
    def test_migration_creates_upgrade_requests_table(self, temp_db):
        conn = get_connection()
        conn.execute("DROP TABLE IF EXISTS upgrade_requests")
        conn.commit()
        rows = conn.execute(
            "SELECT name FROM sqlite_master WHERE type='table' AND name='upgrade_requests'"
        ).fetchall()
        assert len(rows) == 0
        _migrate_v3_to_v4(conn)
        conn.commit()
        rows = conn.execute(
            "SELECT name FROM sqlite_master WHERE type='table' AND name='upgrade_requests'"
        ).fetchall()
        conn.close()
        assert len(rows) == 1

    def test_migration_creates_index(self, temp_db):
        conn = get_connection()
        conn.execute("DROP INDEX IF EXISTS idx_upgrade_requests_user_status")
        conn.execute("DROP TABLE IF EXISTS upgrade_requests")
        conn.commit()
        _migrate_v3_to_v4(conn)
        conn.commit()
        rows = conn.execute(
            "SELECT name FROM sqlite_master WHERE type='index' "
            "AND name='idx_upgrade_requests_user_status'"
        ).fetchall()
        conn.close()
        assert len(rows) == 1

    def test_migration_is_idempotent(self, temp_db):
        conn = get_connection()
        _migrate_v3_to_v4(conn)
        conn.commit()
        conn.close()


    def test_migration_creates_hypotheses_table(self, temp_db):
        """Drop hypotheses and re-run migration — table must reappear."""
        conn = get_connection()
        conn.execute("DROP TABLE IF EXISTS hypotheses")
        conn.commit()
        rows = conn.execute(
            "SELECT name FROM sqlite_master WHERE type='table' AND name='hypotheses'"
        ).fetchall()
        assert len(rows) == 0
        _migrate_v2_to_v3(conn)
        conn.commit()
        rows = conn.execute(
            "SELECT name FROM sqlite_master WHERE type='table' AND name='hypotheses'"
        ).fetchall()
        conn.close()
        assert len(rows) == 1

    def test_migration_creates_index(self, temp_db):
        conn = get_connection()
        conn.execute("DROP INDEX IF EXISTS idx_hypotheses_user_status")
        conn.execute("DROP TABLE IF EXISTS hypotheses")
        conn.commit()
        _migrate_v2_to_v3(conn)
        conn.commit()
        rows = conn.execute(
            "SELECT name FROM sqlite_master WHERE type='index' AND name='idx_hypotheses_user_status'"
        ).fetchall()
        conn.close()
        assert len(rows) == 1

    def test_migration_is_idempotent(self, temp_db):
        """Running v2→v3 twice must not raise."""
        conn = get_connection()
        _migrate_v2_to_v3(conn)
        conn.commit()
        conn.close()

    def test_status_check_constraint_enforced(self, temp_db):
        """Inserting a bad status value must raise an integrity error."""
        conn = get_connection()
        user_row = conn.execute("SELECT id FROM users LIMIT 1").fetchone()
        if not user_row:
            conn.execute("INSERT INTO users (id, username) VALUES ('u1', 'testuser')")
        conn.commit()
        with pytest.raises(sqlite3.IntegrityError):
            conn.execute(
                """INSERT INTO hypotheses (user_id, hypothesis, status)
                   VALUES ('u1', 'bad status test', 'invalid_status')"""
            )
            conn.commit()
        conn.close()

