"""Add performance indexes to research_tasks and research_history

This migration adds indexes to improve query performance on commonly
filtered columns. These indexes were originally added in
PR #2015 (fix/database-indexes-performance) but are maintained
only by this migration, not by model-level declarations.

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

Indexes Added:
=============
research_tasks:
- ix_research_tasks_status (status)
- ix_research_tasks_created_at (created_at)
- idx_research_task_status_created (status, created_at)
- idx_research_task_priority_status (priority, status)

research_history:
- ix_research_history_mode (mode)
- ix_research_history_status (status)
- ix_research_history_created_at (created_at)
- idx_research_history_status_created (status, created_at)
- idx_research_history_mode_status (mode, status)

Migration Notes:
===============
- Index creation/drop are native SQLite operations (no batch mode needed)
- Each index is guarded with existence checks for idempotency
- if_not_exists=True provides additional safety for databases where
  0001's create_all() already created indexes from model declarations
- Tables that don't exist are silently skipped

Downgrade Behavior:
==================
The downgrade() function removes all 9 indexes added by this migration.
Table data is completely preserved — only index structures are removed.
"""

from alembic import op
from sqlalchemy import inspect

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

# Index definitions: (index_name, table_name, columns)
INDEXES = [
    # research_tasks single-column indexes
    ("ix_research_tasks_status", "research_tasks", ["status"]),
    ("ix_research_tasks_created_at", "research_tasks", ["created_at"]),
    # research_tasks composite indexes
    (
        "idx_research_task_status_created",
        "research_tasks",
        ["status", "created_at"],
    ),
    (
        "idx_research_task_priority_status",
        "research_tasks",
        ["priority", "status"],
    ),
    # research_history single-column indexes
    ("ix_research_history_mode", "research_history", ["mode"]),
    ("ix_research_history_status", "research_history", ["status"]),
    ("ix_research_history_created_at", "research_history", ["created_at"]),
    # research_history composite indexes
    (
        "idx_research_history_status_created",
        "research_history",
        ["status", "created_at"],
    ),
    (
        "idx_research_history_mode_status",
        "research_history",
        ["mode", "status"],
    ),
]


def _index_exists(index_name: str, table_name: str) -> bool:
    """Check if an index exists on a table.

    Creates a fresh inspector each call because inspector caches
    per-instance — reusing after DDL returns stale data.
    """
    bind = op.get_bind()
    inspector = inspect(bind)

    if not inspector.has_table(table_name):
        return False

    existing_indexes = inspector.get_indexes(table_name)
    return any(idx["name"] == index_name for idx in existing_indexes)


def upgrade():
    """Add performance indexes to research_tasks and research_history."""
    bind = op.get_bind()
    inspector = inspect(bind)

    for index_name, table_name, columns in INDEXES:
        if not inspector.has_table(table_name):
            continue

        if not _index_exists(index_name, table_name):
            op.create_index(
                index_name,
                table_name,
                columns,
                if_not_exists=True,
            )


def downgrade():
    """Remove performance indexes from research_tasks and research_history."""
    bind = op.get_bind()
    inspector = inspect(bind)

    for index_name, table_name, columns in INDEXES:
        if not inspector.has_table(table_name):
            continue

        if _index_exists(index_name, table_name):
            op.drop_index(index_name, table_name=table_name)
