"""Switch default search.fetch.mode from 'full' to 'summary_focus_query'.

Background
==========
PR #3680 (v1.6.3) introduced the ``search.fetch.mode`` setting with a
default of ``"full"``. ``full`` returns every page's complete extracted
text to the LangGraph agent. With small-to-mid local models (e.g.
qwen3.6:27b at 20480 ctx) this floods the context with boilerplate and
metadata, simultaneously slowing each example down (~60% in our SimpleQA
runs) and dropping accuracy (95.7% → ~80% on a 25-example sample).

The new default ``summary_focus_query`` asks the model to extract only
spans relevant to the agent's per-fetch focus question AND the original
research query, keeping the context lean.

What this migration does
========================
Updates rows where ``key = 'search.fetch.mode'`` AND the stored value is
exactly ``"full"``. Users who explicitly chose ``summary_focus``,
``summary_focus_query``, or ``disabled`` are left untouched. Users who
deliberately picked ``full`` will find their preference flipped — they
can re-select ``Full Page Text`` from the settings UI; this is the
intentional cost of fixing a poor default that almost everyone inherited
without choosing it.

Storage note: the ``value`` column uses SQLAlchemy's ``JSON`` type, which
serializes Python strings via ``json.dumps`` and stores them as TEXT.
The on-disk representation of the string ``full`` is the 6-character
literal ``"full"`` (with the surrounding quotes), so the WHERE clause
matches on that exact form.

Revision ID: 0009
Revises: 0008
Create Date: 2026-05-02
"""

from __future__ import annotations

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

revision = "0009"
down_revision = "0008"
branch_labels = None
depends_on = None

SETTING_KEY = "search.fetch.mode"
OLD_VALUE_JSON = '"full"'
NEW_VALUE_JSON = '"summary_focus_query"'


def upgrade() -> None:
    conn = op.get_bind()

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

    result = conn.execute(
        sa.text(
            "UPDATE settings SET value = :new_value "
            "WHERE key = :key AND value = :old_value"
        ),
        {
            "new_value": NEW_VALUE_JSON,
            "key": SETTING_KEY,
            "old_value": OLD_VALUE_JSON,
        },
    )

    if result.rowcount:
        logger.info(
            "Migrated {} setting(s) {!r} from 'full' to 'summary_focus_query'.",
            result.rowcount,
            SETTING_KEY,
        )


def downgrade() -> None:
    """Revert previously-migrated rows back to 'full'.

    Only rows whose value is currently ``"summary_focus_query"`` are
    reverted — leaves any user choice of ``summary_focus`` or
    ``disabled`` alone. Rows that already held ``summary_focus_query``
    before the upgrade are indistinguishable from migrated rows; the
    downgrade conservatively reverts them as well, since the
    pre-upgrade state stored ``"full"`` as the default.
    """
    conn = op.get_bind()

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

    conn.execute(
        sa.text(
            "UPDATE settings SET value = :old_value "
            "WHERE key = :key AND value = :new_value"
        ),
        {
            "old_value": OLD_VALUE_JSON,
            "key": SETTING_KEY,
            "new_value": NEW_VALUE_JSON,
        },
    )
