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

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

from sqlalchemy import create_engine
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}")
            try:
                # Create tables
                Base.metadata.create_all(engine)

                result = check_database_schema(engine)

                assert isinstance(result, dict)
                assert "tables" in result
            finally:
                engine.dispose()

    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}")
            try:
                # Create tables
                Base.metadata.create_all(engine)

                result = check_database_schema(engine)

                # Should have tables dict
                assert isinstance(result["tables"], dict)
            finally:
                engine.dispose()

    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}")
            try:
                # Don't create any tables
                result = check_database_schema(engine)

                assert "missing_tables" in result
                assert isinstance(result["missing_tables"], list)
            finally:
                engine.dispose()

    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}")
            try:
                # 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)
            finally:
                engine.dispose()

    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}")
            try:
                # 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)
            finally:
                engine.dispose()


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 swallows SettingsManager errors.

        Background: PR #2235 originally tried to make DB errors propagate
        through this code path. PR #2118 (Feb 22 2026, commit 76524cc4de)
        walked that change back because masking failures here was causing
        runtime bugs / CI failure masking — startup must be resilient
        even when the user's settings DB is corrupt or missing. The
        current contract is: SettingsManager errors during initial
        defaults seeding are logged-and-swallowed, not raised. This test
        pins that contract.

        PUNCHLIST historically flagged this as H5_SWALLOWS_ERROR. That
        flag is a false positive against the current SUT — see
        settings/manager.py:780-786 for the catch-and-log site.
        """
        # audit: PUNCHLIST reviewed 2026-05 — issue resolved by prior PR (recommendation: FIX).
        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")

            # Must not raise — startup-resilience contract per PR #2118.
            _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}")
            try:
                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
            finally:
                engine.dispose()

    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}")
            try:
                with patch(
                    "local_deep_research.database.initialize.run_migrations"
                ) as mock_migrations:
                    initialize_database(engine)

                    mock_migrations.assert_called_once_with(engine)
            finally:
                engine.dispose()

    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}")
            try:
                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)
            finally:
                engine.dispose()

    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}")
            try:
                with patch(
                    "local_deep_research.database.initialize._initialize_default_settings"
                ) as mock_init_settings:
                    initialize_database(engine)

                    mock_init_settings.assert_not_called()
            finally:
                engine.dispose()

    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}")
            try:
                # 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
            finally:
                engine.dispose()
