#!/usr/bin/env python3
"""
Test that database schema (table names) never changes unexpectedly.

Renaming or removing tables will cause data loss for existing users.
These tests catch accidental schema changes before they're deployed.
"""

import pytest


# Expected table names - DO NOT REMOVE ANY
# Adding new tables is fine, but removing/renaming breaks existing databases
EXPECTED_TABLES = {
    # Auth (in auth.db, not user dbs)
    "users",
    # Settings
    "api_keys",
    "settings",
    "user_settings",
    # Cache
    "cache",
    "search_cache",
    # Queue
    "queue_status",
    "task_metadata",
    # Research
    "research",
    "research_history",
    "research_resources",
    "research_strategies",
    "research_tasks",
    "search_queries",
    "search_results",
    "queued_researches",
    "user_active_researches",
    # Reports
    "reports",
    "report_sections",
    # Library
    "collections",
    "collection_folders",
    "collection_folder_files",
    "documents",
    "document_blobs",
    "document_chunks",
    "document_collections",
    "download_queue",
    "library_statistics",
    "rag_document_status",
    "rag_indices",
    "source_types",
    "upload_batches",
    # Download tracking
    "download_tracker",
    "download_duplicates",
    "download_attempts",
    # Metrics
    "token_usage",
    "model_usage",
    "research_ratings",
    "search_calls",
    # News
    "news_cards",
    "news_interests",
    "news_subscriptions",
    "news_user_preferences",
    "news_user_ratings",
    "subscription_folders",
    "user_news_search_history",
    # File integrity
    "file_integrity_records",
    "file_verification_failures",
    # Providers
    "provider_models",
    # Rate limiting
    "rate_limit_attempts",
    "rate_limit_estimates",
    # Domain classification
    "domain_classifications",
    # Logs
    "app_logs",
    "journals",
    # Benchmark
    "benchmark_configs",
    "benchmark_progress",
    "benchmark_results",
    "benchmark_runs",
}


class TestSchemaStability:
    """
    Verify that database table names haven't changed.

    Renaming or removing tables will cause existing user databases
    to lose data or fail to open properly.
    """

    def test_no_tables_removed(self):
        """
        Ensure no expected tables have been removed.

        Removing a table definition will cause data loss when users
        upgrade, as SQLAlchemy won't know how to access that data.
        """
        from local_deep_research.database.models import Base

        # Get all actual table names from the models
        actual_tables = set(Base.metadata.tables.keys())

        # Check that all expected tables still exist
        missing_tables = EXPECTED_TABLES - actual_tables

        assert not missing_tables, (
            f"CRITICAL: Database tables have been removed!\n"
            f"Missing tables: {missing_tables}\n\n"
            "Removing tables will cause data loss for existing users.\n"
            "If you intentionally removed these tables, you need a migration plan.\n"
            "Otherwise, REVERT THIS CHANGE."
        )

    def test_no_tables_renamed(self):
        """
        Detect if tables might have been renamed.

        If new tables appear and expected tables are missing,
        it's likely a rename which will cause data loss.
        """
        from local_deep_research.database.models import Base

        actual_tables = set(Base.metadata.tables.keys())
        missing_tables = EXPECTED_TABLES - actual_tables

        if missing_tables:
            # Check if there are new tables that might be renames
            new_tables = actual_tables - EXPECTED_TABLES

            if new_tables:
                pytest.fail(
                    f"Possible table rename detected!\n"
                    f"Missing: {missing_tables}\n"
                    f"New: {new_tables}\n\n"
                    "If you renamed tables, existing data will be lost.\n"
                    "You need a migration to copy data from old to new tables."
                )

    def test_new_tables_are_documented(self):
        """
        Ensure any new tables are added to EXPECTED_TABLES.

        This is a reminder to update this test when adding new tables.
        New tables should be added to EXPECTED_TABLES to track them.
        """
        from local_deep_research.database.models import Base

        actual_tables = set(Base.metadata.tables.keys())
        new_tables = actual_tables - EXPECTED_TABLES

        # These are okay - just a reminder to update the test
        if new_tables:
            pytest.fail(
                f"New tables detected that aren't in EXPECTED_TABLES:\n"
                f"{new_tables}\n\n"
                "Please add these to EXPECTED_TABLES in this test file.\n"
                "This ensures they'll be protected from accidental removal."
            )


class TestCriticalColumns:
    """
    Verify that critical columns in key tables haven't been removed.

    These are columns that store important user data.
    """

    def test_user_settings_has_required_columns(self):
        """Verify UserSettings table has all required columns."""
        from local_deep_research.database.models import UserSettings

        required_columns = {"id", "key", "value", "category"}
        actual_columns = set(UserSettings.__table__.columns.keys())

        missing = required_columns - actual_columns
        assert not missing, (
            f"UserSettings is missing required columns: {missing}\n"
            "This will break user settings storage."
        )

    def test_research_has_required_columns(self):
        """Verify Research table has all required columns."""
        from local_deep_research.database.models.research import Research

        required_columns = {"id", "query", "status", "mode", "created_at"}
        actual_columns = set(Research.__table__.columns.keys())

        missing = required_columns - actual_columns
        assert not missing, (
            f"Research is missing required columns: {missing}\n"
            "This will break research history."
        )

    def test_api_keys_has_required_columns(self):
        """Verify APIKey table has all required columns."""
        from local_deep_research.database.models import APIKey

        required_columns = {"id", "provider", "key", "is_active"}
        actual_columns = set(APIKey.__table__.columns.keys())

        missing = required_columns - actual_columns
        assert not missing, (
            f"APIKey is missing required columns: {missing}\n"
            "This will break API key storage."
        )


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
