"""Test pipeline for running alembic migrations and corresponding test hooks in order."""

from __future__ import annotations

import pytest
from alembic import command
from alembic.config import Config
from alembic.script import ScriptDirectory
from sqlalchemy import Engine

from tests.alembic.conftest import ALEMBIC_CONFIG_PATH
from tests.alembic.registry import get_registered_hooks
from tests.alembic.verifier import MigrationVerifier


def _load_revision_sequence() -> tuple[str, ...]:
    """Read the Alembic script directory to produce the linear revision order."""

    script = ScriptDirectory.from_config(Config(str(ALEMBIC_CONFIG_PATH)))
    revisions = list(script.walk_revisions())  # newest -> oldest
    revisions.reverse()
    return tuple(revision.revision for revision in revisions)


REVISION_SEQUENCE: tuple[str, ...] = _load_revision_sequence()
REVISION_PARAMS = [
    pytest.param(revision, id=f"{index:02d}_{revision}")
    for index, revision in enumerate(REVISION_SEQUENCE, start=1)
]


def _test_single_revision(
    revision: str,
    alembic_cfg: Config,
    alembic_engine: Engine,
) -> None:
    """
    Test a single migration revision upgrade.

    For each revision:
    - Migrate to the previous revision
    - Run the before_upgrade hook to seed and validate the state of the DB before the revision
    - Migrate to the current revision
    - Run the after_upgrade hook to validate the state of the DB after the revision
    """
    hooks_map = get_registered_hooks()
    revision_order = list(REVISION_SEQUENCE)

    # Find the previous revision in the chain
    revision_index = revision_order.index(revision)
    previous_revision = (
        revision_order[revision_index - 1] if revision_index > 0 else "base"
    )

    # Migrate up to the previous revision using a shared connection
    with alembic_engine.begin() as conn:
        alembic_cfg.attributes["connection"] = conn
        command.upgrade(alembic_cfg, previous_revision)

    # Run before_upgrade hook if it exists
    hooks = hooks_map.get(revision)
    if hooks and hooks.before_upgrade:
        with alembic_engine.begin() as conn:
            verifier = MigrationVerifier(conn, revision)
            hooks.before_upgrade(verifier)

    # Migrate to the current revision using the same pattern
    with alembic_engine.begin() as conn:
        alembic_cfg.attributes["connection"] = conn
        command.upgrade(alembic_cfg, revision)

    # Run after_upgrade hook if it exists
    if hooks and hooks.after_upgrade:
        with alembic_engine.begin() as conn:
            verifier = MigrationVerifier(conn, revision)
            hooks.after_upgrade(verifier)


@pytest.mark.parametrize("revision", REVISION_PARAMS)
def test_migration_revision(
    revision: str,
    alembic_cfg: Config,
    alembic_engine: Engine,
) -> None:
    _test_single_revision(revision, alembic_cfg, alembic_engine)
