"""Migrate legacy app.* settings to correct scoped keys

One-time data migration that:
1. Deletes deprecated settings (app.enable_fact_checking, app.output_dir)
2. Re-scopes app.* settings to their correct general.*/search.*/llm.* keys
   as defined in default_settings.json

This moves logic previously in the fix_corrupted_settings route handler
into a proper Alembic migration (per review on PR #2176).

Revision ID: 0004
Revises: 0003
Create Date: 2025-01-01

Key Corrections:
================
Three keys that the route handler mapped with simple prefix replacement
are corrected here to match default_settings.json:
- app.search_engine → search.engine.DEFAULT_SEARCH_ENGINE (not search.search_engine)
- app.openai_endpoint_url → llm.openai_endpoint.url (not llm.openai_endpoint_url)
- app.lmstudio_url → llm.lmstudio.url (not llm.lmstudio_url)

Downgrade Behavior:
==================
No-op. The old app.* keys were stale legacy artifacts with zero consumers.
Re-creating them would serve no purpose and risk confusing the settings
loader.
"""

import sqlalchemy as sa
from alembic import op
from sqlalchemy import inspect

# revision identifiers, used by Alembic.
revision = "0004"
down_revision = "0003"
branch_labels = None
depends_on = None

# Settings to delete outright (deprecated, no replacement)
DEPRECATED_KEYS = [
    "app.enable_fact_checking",
    "app.output_dir",
]

# Settings to re-scope: (old_key, new_key, new_type)
# new_type values are lowercase strings matching SettingType enum
RESCOPE_MAPPINGS = [
    # general.*
    ("app.knowledge_accumulation", "general.knowledge_accumulation", "app"),
    (
        "app.knowledge_accumulation_context_limit",
        "general.knowledge_accumulation_context_limit",
        "app",
    ),
    # search.*
    ("app.questions_per_iteration", "search.questions_per_iteration", "search"),
    (
        "app.search_engine",
        "search.engine.DEFAULT_SEARCH_ENGINE",
        "search",
    ),
    ("app.iterations", "search.iterations", "search"),
    ("app.max_results", "search.max_results", "search"),
    ("app.region", "search.region", "search"),
    ("app.safe_search", "search.safe_search", "search"),
    ("app.search_language", "search.search_language", "search"),
    ("app.snippets_only", "search.snippets_only", "search"),
    # llm.*
    ("app.model", "llm.model", "llm"),
    ("app.provider", "llm.provider", "llm"),
    ("app.temperature", "llm.temperature", "llm"),
    ("app.max_tokens", "llm.max_tokens", "llm"),
    ("app.openai_endpoint_url", "llm.openai_endpoint.url", "llm"),
    ("app.lmstudio_url", "llm.lmstudio.url", "llm"),
    ("app.llamacpp_model_path", "llm.llamacpp_model_path", "llm"),
]


def upgrade():
    """Delete deprecated settings and re-scope app.* keys."""
    conn = op.get_bind()

    inspector = inspect(conn)
    if not inspector.has_table("settings"):
        return

    # 1. Delete deprecated settings
    for key in DEPRECATED_KEYS:
        conn.execute(
            sa.text("DELETE FROM settings WHERE key = :key"),
            {"key": key},
        )

    # 2. Re-scope settings
    for old_key, new_key, new_type in RESCOPE_MAPPINGS:
        # Check if old key exists
        old_row = conn.execute(
            sa.text("SELECT COUNT(*) FROM settings WHERE key = :key"),
            {"key": old_key},
        ).scalar()

        if old_row:
            # Check if new key already exists (don't overwrite user's current setting)
            new_exists = conn.execute(
                sa.text("SELECT COUNT(*) FROM settings WHERE key = :key"),
                {"key": new_key},
            ).scalar()

            if not new_exists:
                # Copy to new key with corrected type
                conn.execute(
                    sa.text(
                        "INSERT INTO settings "
                        "(key, value, type, name, description, category, "
                        "ui_element, options, min_value, max_value, step, "
                        "visible, editable, env_var) "
                        "SELECT :new_key, value, :new_type, name, description, "
                        "category, ui_element, options, min_value, max_value, "
                        "step, visible, editable, env_var "
                        "FROM settings WHERE key = :old_key"
                    ),
                    {
                        "new_key": new_key,
                        "new_type": new_type,
                        "old_key": old_key,
                    },
                )

            # Always delete the old key
            conn.execute(
                sa.text("DELETE FROM settings WHERE key = :key"),
                {"key": old_key},
            )


def downgrade():
    """No-op: the old app.* keys were stale legacy artifacts with no consumers.

    Re-creating them would risk confusing the settings loader, which
    expects only the correctly-scoped keys from default_settings.json.
    """
