from __future__ import annotations

from typing import Optional
from uuid import UUID

from fastapi import APIRouter, Depends, Path, Query
from sqlalchemy.ext.asyncio import AsyncSession

from src.core.database import get_db
from src.core.exceptions import NotFoundError, ValidationError
from src.domain.admin.router import require_admin
from src.domain.auth.models import User
from src.domain.cloud.dependencies import require_cloud_internal_token
from src.domain.l3_registry.repository import L3CandidateRepository
from src.domain.l3_registry.schemas import L3CandidateCreate, L3CandidateResponse, L3ReviewAction

router = APIRouter(prefix="/api/admin/l3-candidates", tags=["admin-l3-registry"])
internal_router = APIRouter(prefix="/api/internal/codegen/l3-candidates", tags=["internal-l3-registry"], dependencies=[Depends(require_cloud_internal_token)])


@internal_router.post("", response_model=L3CandidateResponse)
async def register_l3_candidate(body: L3CandidateCreate, db: AsyncSession = Depends(get_db)):
    repo = L3CandidateRepository(db)
    external_id = body.candidate_id or f"l3.{body.semantic}@project:{body.project_id}"
    existing = await repo.get_by_external_id(external_id)
    if existing:
        return existing
    row = await repo.create(**body.model_dump(exclude={"candidate_id"}), candidate_id=external_id)
    await db.commit()
    return row


@router.get("", response_model=list[L3CandidateResponse])
async def list_l3_candidates(status: Optional[str] = Query(None), semantic: Optional[str] = Query(None), _: User = Depends(require_admin), db: AsyncSession = Depends(get_db)):
    return await L3CandidateRepository(db).list(status=status, semantic=semantic)


@router.get("/{candidate}", response_model=L3CandidateResponse)
async def get_l3_candidate(candidate: UUID = Path(...), _: User = Depends(require_admin), db: AsyncSession = Depends(get_db)):
    row = await L3CandidateRepository(db).get(candidate)
    if not row:
        raise NotFoundError("L3 candidate not found")
    return row


@router.post("/{candidate}/review", response_model=L3CandidateResponse)
async def review_l3_candidate(body: L3ReviewAction, candidate: UUID = Path(...), admin: User = Depends(require_admin), db: AsyncSession = Depends(get_db)):
    row = await L3CandidateRepository(db).get(candidate)
    if not row:
        raise NotFoundError("L3 candidate not found")
    if body.action == "relate" and not body.related_candidate_ids:
        raise ValidationError("Relate requires at least one related candidate")
    status_by_action = {
        "approve_for_architecture_review": "approved_for_architecture_review",
        "request_refinement": "needs_refinement",
        "keep_project_specific": "retained_l3",
        "relate": row.status,
        "reject": "rejected",
        "promote": "promoted",
        "supersede": "superseded",
    }
    result = await L3CandidateRepository(db).review(row, status=status_by_action[body.action], reviewer_id=admin.id, notes=body.notes, related=body.related_candidate_ids)
    await db.commit()
    return result
