from dataclasses import asdict

from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession

from api.database import get_session
from api.config import settings
from api.schemas.identifier_migration import (
    IdentifierMigrationApplySubmit,
    SealedProjectionRepairReceiptRead,
    SealedProjectionRepairSubmit,
)
from api.services.work_record_identifier_migration import (
    IdentifierMigrationError,
    apply_repository_identifier_migration,
    repair_absent_prederivation_projection,
    reverse_repository_identifier_migration,
)

router = APIRouter(prefix="/identifier-migrations", tags=["identifier-migrations"])


@router.post("/repositories/{repo_slug}/apply")
async def apply_identifier_migration(
    repo_slug: str,
    body: IdentifierMigrationApplySubmit,
    session: AsyncSession = Depends(get_session),
) -> dict:
    """Apply one sealed repository-atomic identifier convergence transaction."""
    if settings.state_hub_instance_role != "primary":
        raise HTTPException(
            status_code=409,
            detail={
                "message": "identifier migration writes require the primary State Hub",
                "instance_role": settings.state_hub_instance_role,
                "instance_label": settings.state_hub_instance_label,
            },
        )
    if not body.primary_confirmed:
        raise HTTPException(status_code=409, detail="explicit primary confirmation is required")
    if body.plan.get("plan_sha256") != body.expected_plan_sha256:
        raise HTTPException(status_code=409, detail="explicit plan SHA-256 does not match sealed plan")
    try:
        result = await apply_repository_identifier_migration(session, body.plan, repo_slug)
    except IdentifierMigrationError as exc:
        raise HTTPException(status_code=409, detail=str(exc)) from exc
    from api.routers.workstreams import _invalidate_workplan_index_cache

    _invalidate_workplan_index_cache()
    return {
        "schema": "state-hub.identifier-migration-apply.v1",
        "instance_role": settings.state_hub_instance_role,
        "instance_label": settings.state_hub_instance_label,
        "result": asdict(result),
    }


@router.post("/repositories/{repo_slug}/reverse")
async def reverse_identifier_migration(
    repo_slug: str,
    body: IdentifierMigrationApplySubmit,
    session: AsyncSession = Depends(get_session),
) -> dict:
    """Reverse one sealed repository migration after a failed file phase."""
    if settings.state_hub_instance_role != "primary":
        raise HTTPException(
            status_code=409,
            detail={
                "message": "identifier migration writes require the primary State Hub",
                "instance_role": settings.state_hub_instance_role,
                "instance_label": settings.state_hub_instance_label,
            },
        )
    if not body.primary_confirmed:
        raise HTTPException(status_code=409, detail="explicit primary confirmation is required")
    if body.plan.get("plan_sha256") != body.expected_plan_sha256:
        raise HTTPException(status_code=409, detail="explicit plan SHA-256 does not match sealed plan")
    try:
        result = await reverse_repository_identifier_migration(session, body.plan, repo_slug)
    except IdentifierMigrationError as exc:
        raise HTTPException(status_code=409, detail=str(exc)) from exc
    from api.routers.workstreams import _invalidate_workplan_index_cache

    _invalidate_workplan_index_cache()
    return {
        "schema": "state-hub.identifier-migration-reverse.v1",
        "instance_role": settings.state_hub_instance_role,
        "instance_label": settings.state_hub_instance_label,
        "result": asdict(result),
    }


@router.post(
    "/sealed-projection-repairs",
    response_model=SealedProjectionRepairReceiptRead,
)
async def repair_sealed_projection(
    body: SealedProjectionRepairSubmit,
    session: AsyncSession = Depends(get_session),
) -> dict:
    """Restore one exact pre-derivation workplan unit into an absent projection."""
    try:
        receipt = await repair_absent_prederivation_projection(
            session,
            body.plan,
            body.repo_slug,
            body.unit,
            expected_plan_sha256=body.expected_plan_sha256,
            source_revision=body.source_revision,
            source_fingerprint=body.source_fingerprint,
            source_clean=body.source_clean,
            source_synchronized=body.source_synchronized,
            primary_confirmed=body.primary_confirmed,
            projection_identity=body.projection_identity,
        )
    except IdentifierMigrationError as exc:
        raise HTTPException(status_code=409, detail=str(exc)) from exc
    return asdict(receipt)
