"""Tests for database initialize module functions."""

import tempfile
from pathlib import Path
from unittest.mock import Mock, patch

from sqlalchemy import create_engine, Integer, String, Column
from sqlalchemy.orm import Session

from local_deep_research.database.models import Base


class TestCheckDatabaseSchema:
    """Tests for check_database_schema function."""

    def test_returns_dict_with_tables_key(self):
        """check_database_schema returns dict with 'tables' key."""
        from local_deep_research.database.initialize import (
            check_database_schema,
        )

        with tempfile.TemporaryDirectory() as temp_dir:
            db_path = Path(temp_dir) / "test.db"
            engine = create_engine(f"sqlite:///{db_path}")

            # Create tables
            Base.metadata.create_all(engine)

            result = check_database_schema(engine)

            assert isinstance(result, dict)
            assert "tables" in result

    def test_lists_existing_tables(self):
        """check_database_schema lists existing tables."""
        from local_deep_research.database.initialize import (
            check_database_schema,
        )

        with tempfile.TemporaryDirectory() as temp_dir:
            db_path = Path(temp_dir) / "test.db"
            engine = create_engine(f"sqlite:///{db_path}")

            # Create tables
            Base.metadata.create_all(engine)

            result = check_database_schema(engine)

            # Should have tables dict
            assert isinstance(result["tables"], dict)

    def test_lists_missing_tables(self):
        """check_database_schema identifies missing tables."""
        from local_deep_research.database.initialize import (
            check_database_schema,
        )

        with tempfile.TemporaryDirectory() as temp_dir:
            db_path = Path(temp_dir) / "test.db"
            engine = create_engine(f"sqlite:///{db_path}")

            # Don't create any tables
            result = check_database_schema(engine)

            assert "missing_tables" in result
            assert isinstance(result["missing_tables"], list)

    def test_detects_news_tables(self):
        """check_database_schema detects news tables presence."""
        from local_deep_research.database.initialize import (
            check_database_schema,
        )

        with tempfile.TemporaryDirectory() as temp_dir:
            db_path = Path(temp_dir) / "test.db"
            engine = create_engine(f"sqlite:///{db_path}")

            # Create tables
            Base.metadata.create_all(engine)

            result = check_database_schema(engine)

            assert "has_news_tables" in result
            assert isinstance(result["has_news_tables"], bool)

    def test_returns_columns_for_each_table(self):
        """check_database_schema returns column names for existing tables."""
        from local_deep_research.database.initialize import (
            check_database_schema,
        )

        with tempfile.TemporaryDirectory() as temp_dir:
            db_path = Path(temp_dir) / "test.db"
            engine = create_engine(f"sqlite:///{db_path}")

            # Create tables
            Base.metadata.create_all(engine)

            result = check_database_schema(engine)

            # Each table in tables dict should have a list of columns
            for table_name, columns in result["tables"].items():
                assert isinstance(columns, list)


class TestAddColumnIfNotExists:
    """Tests for _add_column_if_not_exists function."""

    def test_adds_column_when_missing(self):
        """_add_column_if_not_exists adds column when it doesn't exist."""
        from local_deep_research.database.initialize import (
            _add_column_if_not_exists,
        )
        from sqlalchemy import Table, MetaData, inspect

        with tempfile.TemporaryDirectory() as temp_dir:
            db_path = Path(temp_dir) / "test.db"
            engine = create_engine(f"sqlite:///{db_path}")

            # Create a simple table
            metadata = MetaData()
            _ = Table(
                "test_table",
                metadata,
                Column("id", Integer, primary_key=True),
                Column("name", String),
            )
            metadata.create_all(engine)

            # Add a new column
            result = _add_column_if_not_exists(
                engine, "test_table", "new_column", "TEXT"
            )

            assert result is True

            # Verify column was added
            inspector = inspect(engine)
            columns = [c["name"] for c in inspector.get_columns("test_table")]
            assert "new_column" in columns

    def test_returns_false_when_column_exists(self):
        """_add_column_if_not_exists returns False when column exists."""
        from local_deep_research.database.initialize import (
            _add_column_if_not_exists,
        )
        from sqlalchemy import Table, MetaData

        with tempfile.TemporaryDirectory() as temp_dir:
            db_path = Path(temp_dir) / "test.db"
            engine = create_engine(f"sqlite:///{db_path}")

            # Create a table with the column already
            metadata = MetaData()
            _ = Table(
                "test_table",
                metadata,
                Column("id", Integer, primary_key=True),
                Column("existing_column", String),
            )
            metadata.create_all(engine)

            # Try to add existing column
            result = _add_column_if_not_exists(
                engine, "test_table", "existing_column", "TEXT"
            )

            assert result is False

    def test_handles_integer_type(self):
        """_add_column_if_not_exists handles INTEGER type."""
        from local_deep_research.database.initialize import (
            _add_column_if_not_exists,
        )
        from sqlalchemy import Table, MetaData, inspect

        with tempfile.TemporaryDirectory() as temp_dir:
            db_path = Path(temp_dir) / "test.db"
            engine = create_engine(f"sqlite:///{db_path}")

            # Create a simple table
            metadata = MetaData()
            _ = Table(
                "test_table",
                metadata,
                Column("id", Integer, primary_key=True),
            )
            metadata.create_all(engine)

            # Add an integer column
            result = _add_column_if_not_exists(
                engine, "test_table", "count", "INTEGER"
            )

            assert result is True

            # Verify column was added
            inspector = inspect(engine)
            columns = [c["name"] for c in inspector.get_columns("test_table")]
            assert "count" in columns

    def test_handles_text_type(self):
        """_add_column_if_not_exists handles TEXT type."""
        from local_deep_research.database.initialize import (
            _add_column_if_not_exists,
        )
        from sqlalchemy import Table, MetaData, inspect

        with tempfile.TemporaryDirectory() as temp_dir:
            db_path = Path(temp_dir) / "test.db"
            engine = create_engine(f"sqlite:///{db_path}")

            # Create a simple table
            metadata = MetaData()
            _ = Table(
                "test_table",
                metadata,
                Column("id", Integer, primary_key=True),
            )
            metadata.create_all(engine)

            # Add a text column
            result = _add_column_if_not_exists(
                engine, "test_table", "description", "TEXT"
            )

            assert result is True

            # Verify column was added
            inspector = inspect(engine)
            columns = [c["name"] for c in inspector.get_columns("test_table")]
            assert "description" in columns

    def test_adds_default_value(self):
        """_add_column_if_not_exists adds column with default value."""
        from local_deep_research.database.initialize import (
            _add_column_if_not_exists,
        )
        from sqlalchemy import Table, MetaData, text

        with tempfile.TemporaryDirectory() as temp_dir:
            db_path = Path(temp_dir) / "test.db"
            engine = create_engine(f"sqlite:///{db_path}")

            # Create a simple table with some data
            metadata = MetaData()
            _ = Table(
                "test_table",
                metadata,
                Column("id", Integer, primary_key=True),
            )
            metadata.create_all(engine)

            # Insert a row
            with engine.connect() as conn:
                conn.execute(text("INSERT INTO test_table (id) VALUES (1)"))
                conn.commit()

            # Add a column with default
            result = _add_column_if_not_exists(
                engine, "test_table", "status", "INTEGER", default="0"
            )

            assert result is True


class TestRunMigrations:
    """Tests for _run_migrations function."""

    def test_adds_progress_columns_to_task_metadata(self):
        """_run_migrations adds progress columns to task_metadata table."""
        from local_deep_research.database.initialize import _run_migrations
        from sqlalchemy import Table, MetaData, Column, Integer, String, inspect

        with tempfile.TemporaryDirectory() as temp_dir:
            db_path = Path(temp_dir) / "test.db"
            engine = create_engine(f"sqlite:///{db_path}")

            # Create task_metadata table without progress columns
            metadata = MetaData()
            _ = Table(
                "task_metadata",
                metadata,
                Column("id", Integer, primary_key=True),
                Column("task_id", String),
            )
            metadata.create_all(engine)

            # Run migrations
            _run_migrations(engine)

            # Verify progress columns were added
            inspector = inspect(engine)
            columns = [
                c["name"] for c in inspector.get_columns("task_metadata")
            ]
            assert "progress_current" in columns
            assert "progress_total" in columns
            assert "progress_message" in columns
            assert "metadata_json" in columns

    def test_skips_when_columns_exist(self):
        """_run_migrations skips columns that already exist."""
        from local_deep_research.database.initialize import _run_migrations
        from sqlalchemy import (
            Table,
            MetaData,
            Column,
            Integer,
            String,
            Text,
            inspect,
        )

        with tempfile.TemporaryDirectory() as temp_dir:
            db_path = Path(temp_dir) / "test.db"
            engine = create_engine(f"sqlite:///{db_path}")

            # Create task_metadata table WITH progress columns
            metadata = MetaData()
            _ = Table(
                "task_metadata",
                metadata,
                Column("id", Integer, primary_key=True),
                Column("task_id", String),
                Column("progress_current", Integer),
                Column("progress_total", Integer),
                Column("progress_message", Text),
                Column("metadata_json", Text),
            )
            metadata.create_all(engine)

            # Run migrations - should not fail
            _run_migrations(engine)

            # Verify columns still exist (no duplicates or errors)
            inspector = inspect(engine)
            columns = [
                c["name"] for c in inspector.get_columns("task_metadata")
            ]
            assert columns.count("progress_current") == 1
            assert columns.count("progress_total") == 1

    def test_skips_when_table_does_not_exist(self):
        """_run_migrations skips migration when table doesn't exist."""
        from local_deep_research.database.initialize import _run_migrations

        with tempfile.TemporaryDirectory() as temp_dir:
            db_path = Path(temp_dir) / "test.db"
            engine = create_engine(f"sqlite:///{db_path}")

            # Don't create any tables

            # Run migrations - should not fail
            _run_migrations(engine)

            # Should complete without error


class TestInitializeDefaultSettings:
    """Tests for _initialize_default_settings function."""

    def test_calls_settings_manager(self):
        """_initialize_default_settings calls SettingsManager methods."""
        from local_deep_research.database.initialize import (
            _initialize_default_settings,
        )

        mock_session = Mock(spec=Session)

        with patch(
            "local_deep_research.settings.manager.SettingsManager"
        ) as MockSettingsManager:
            mock_settings_mgr = Mock()
            mock_settings_mgr.db_version_matches_package.return_value = False
            MockSettingsManager.return_value = mock_settings_mgr

            _initialize_default_settings(mock_session)

            MockSettingsManager.assert_called_once_with(mock_session)
            mock_settings_mgr.db_version_matches_package.assert_called_once()
            mock_settings_mgr.load_from_defaults_file.assert_called_once()
            mock_settings_mgr.update_db_version.assert_called_once()

    def test_skips_when_version_matches(self):
        """_initialize_default_settings skips update when version matches."""
        from local_deep_research.database.initialize import (
            _initialize_default_settings,
        )

        mock_session = Mock(spec=Session)

        with patch(
            "local_deep_research.settings.manager.SettingsManager"
        ) as MockSettingsManager:
            mock_settings_mgr = Mock()
            mock_settings_mgr.db_version_matches_package.return_value = True
            MockSettingsManager.return_value = mock_settings_mgr

            _initialize_default_settings(mock_session)

            # Should not call load_from_defaults_file
            mock_settings_mgr.load_from_defaults_file.assert_not_called()

    def test_handles_errors_gracefully(self):
        """_initialize_default_settings handles errors without raising."""
        from local_deep_research.database.initialize import (
            _initialize_default_settings,
        )

        mock_session = Mock(spec=Session)

        with patch(
            "local_deep_research.settings.manager.SettingsManager"
        ) as MockSettingsManager:
            MockSettingsManager.side_effect = Exception("Settings error")

            # Should not raise
            _initialize_default_settings(mock_session)


class TestInitializeDatabase:
    """Tests for initialize_database function."""

    def test_creates_all_tables(self):
        """initialize_database creates all tables from Base.metadata."""
        from local_deep_research.database.initialize import initialize_database

        with tempfile.TemporaryDirectory() as temp_dir:
            db_path = Path(temp_dir) / "test.db"
            engine = create_engine(f"sqlite:///{db_path}")

            initialize_database(engine)

            # Verify tables were created
            from sqlalchemy import inspect

            inspector = inspect(engine)
            tables = inspector.get_table_names()

            # Should have at least some tables
            assert len(tables) > 0

    def test_calls_run_migrations(self):
        """initialize_database calls _run_migrations."""
        from local_deep_research.database.initialize import initialize_database

        with tempfile.TemporaryDirectory() as temp_dir:
            db_path = Path(temp_dir) / "test.db"
            engine = create_engine(f"sqlite:///{db_path}")

            with patch(
                "local_deep_research.database.initialize._run_migrations"
            ) as mock_migrations:
                initialize_database(engine)

                mock_migrations.assert_called_once_with(engine)

    def test_initializes_settings_when_session_provided(self):
        """initialize_database initializes settings when session provided."""
        from local_deep_research.database.initialize import initialize_database

        with tempfile.TemporaryDirectory() as temp_dir:
            db_path = Path(temp_dir) / "test.db"
            engine = create_engine(f"sqlite:///{db_path}")
            mock_session = Mock(spec=Session)

            with patch(
                "local_deep_research.database.initialize._initialize_default_settings"
            ) as mock_init_settings:
                initialize_database(engine, db_session=mock_session)

                mock_init_settings.assert_called_once_with(mock_session)

    def test_skips_settings_when_no_session(self):
        """initialize_database skips settings init when no session provided."""
        from local_deep_research.database.initialize import initialize_database

        with tempfile.TemporaryDirectory() as temp_dir:
            db_path = Path(temp_dir) / "test.db"
            engine = create_engine(f"sqlite:///{db_path}")

            with patch(
                "local_deep_research.database.initialize._initialize_default_settings"
            ) as mock_init_settings:
                initialize_database(engine)

                mock_init_settings.assert_not_called()

    def test_handles_checkfirst_for_existing_tables(self):
        """initialize_database uses checkfirst=True for existing tables."""
        from local_deep_research.database.initialize import initialize_database

        with tempfile.TemporaryDirectory() as temp_dir:
            db_path = Path(temp_dir) / "test.db"
            engine = create_engine(f"sqlite:///{db_path}")

            # Create tables first
            Base.metadata.create_all(engine)

            # Run initialize again - should not fail
            initialize_database(engine)

            # Verify tables still exist
            from sqlalchemy import inspect

            inspector = inspect(engine)
            tables = inspector.get_table_names()
            assert len(tables) > 0
