"""Rename project IDs from 'repo-name' to 'owner/repo' format.

Derives the new ID from each project's github_url. Projects without a
github_url are left unchanged (manual intervention required).

Revision ID: 0007
Revises: 0006
"""

from typing import Sequence, Union

import sqlalchemy as sa
from alembic import op

revision: str = "0007"
down_revision: Union[str, None] = "0006"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None

def _extract_owner_repo(github_url: str) -> str | None:
    """Extract 'owner/repo' from a GitHub URL.

    Handles:
      - https://github.com/owner/repo
      - https://github.com/owner/repo.git
      - git@github.com:owner/repo.git
    """
    url = github_url.strip().rstrip("/")
    if url.endswith(".git"):
        url = url[:-4]

    # SSH format
    if ":" in url and url.startswith("git@"):
        path = url.split(":")[-1]
        parts = path.split("/")
        if len(parts) >= 2:
            return f"{parts[-2]}/{parts[-1]}"

    # HTTPS format
    parts = url.split("/")
    if len(parts) >= 2:
        return f"{parts[-2]}/{parts[-1]}"

    return None


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

    # Discover ALL FK constraints that reference projects(id) using pg_constraint.
    # This is more reliable than matching constraint names by pattern.
    fk_rows = conn.execute(
        sa.text("""
            SELECT
                con.conname AS constraint_name,
                src.relname AS table_name,
                att.attname AS column_name
            FROM pg_constraint con
            JOIN pg_class src ON con.conrelid = src.oid
            JOIN pg_class ref ON con.confrelid = ref.oid
            JOIN pg_attribute att ON att.attrelid = con.conrelid
                AND att.attnum = ANY(con.conkey)
            WHERE con.contype = 'f'
              AND ref.relname = 'projects'
        """)
    ).fetchall()

    # Drop all FK constraints referencing projects
    for constraint_name, table_name, _col in fk_rows:
        conn.execute(
            sa.text(f"ALTER TABLE {table_name} DROP CONSTRAINT {constraint_name}")
        )

    # Fetch all projects and derive new IDs
    projects = conn.execute(
        sa.text("SELECT id, github_url FROM projects")
    ).fetchall()

    for old_id, github_url in projects:
        if not github_url:
            continue
        new_id = _extract_owner_repo(github_url)
        if not new_id or new_id == old_id:
            continue

        # Update parent first (no FKs to block us now)
        conn.execute(
            sa.text("UPDATE projects SET id = :new WHERE id = :old"),
            {"new": new_id, "old": old_id},
        )

        # Update all child tables (derived from the FK query, not hardcoded)
        for _constraint, table_name, column_name in fk_rows:
            conn.execute(
                sa.text(
                    f"UPDATE {table_name} SET {column_name} = :new "
                    f"WHERE {column_name} = :old"
                ),
                {"new": new_id, "old": old_id},
            )

    # Re-create all FK constraints
    for constraint_name, table_name, column_name in fk_rows:
        conn.execute(
            sa.text(
                f"ALTER TABLE {table_name} ADD CONSTRAINT {constraint_name} "
                f"FOREIGN KEY ({column_name}) REFERENCES projects(id)"
            )
        )


def downgrade() -> None:
    # Not reversible — would need the original IDs stored somewhere.
    pass
