"""배포 확정 + 감사 로그 (화면 3 후반, 위험 동작 — 스펙 §5-2).

GET  /repos/{uuid}/deploy/confirm  → 확인 모달(미리보기 + type-to-confirm)
POST /repos/{uuid}/deploy          → publication 생성 → distribution 교체 → 감사 로그

안전장치:
- 검증 게이트: 검증 통과(pass) 저장소만 배포 가능 (서버측에서도 재확인)
- type-to-confirm: 저장소 이름을 정확히 입력해야 실행
- 2단계 실패 명확화: 어느 단계에서 실패하든 "운영망 변경 여부"를 분명히 안내
- 감사 로그: 누가/언제/어떤 repo/어느 버전→어느 버전 (이전 버전 기록 = 롤백 추적)
"""

from __future__ import annotations

import httpx
from fastapi import APIRouter, Depends, Form, Request
from fastapi.responses import HTMLResponse

from .. import audit, views
from ..deps import get_pulp_client, templates
from ..pulp_client import PulpClient
from .versions import _deployed_version_number, load_version_context

router = APIRouter()


def _distribution_for_repo(pulp: PulpClient, uuid: str) -> str | None:
    """이 저장소에 연결된 distribution href. repository 직접 참조 또는 현재 publication 으로 매핑."""
    needle = f"/repositories/rpm/rpm/{uuid}/"
    for dist in pulp.list_distributions():
        if needle in (dist.get("repository") or ""):
            return dist.get("pulp_href")
        pub_href = dist.get("publication")
        if pub_href:
            try:
                pub = pulp.get_publication(pub_href)
            except httpx.HTTPError:
                continue
            if needle in (pub.get("repository_version") or ""):
                return dist.get("pulp_href")
    return None


def _preview(pulp: PulpClient, uuid: str, version_href: str):
    """확인 모달용 컨텍스트(repo_name, gpg, preview, deployed)."""
    repo = pulp.get_repo(uuid)
    raw = pulp.list_versions(uuid)
    gpg = views.gpg_status(repo)
    deployed = _deployed_version_number(pulp, uuid)
    vv = sorted(
        (views.build_version_view(v, deployed, gpg) for v in raw),
        key=lambda v: v["number"] or 0,
        reverse=True,
    )
    target = views.version_number(version_href)
    return {
        "uuid": uuid,
        "repo_name": repo.get("name", uuid),
        "version_href": version_href,
        "gpg": gpg,
        "deployed": deployed,
        "preview": views.deploy_preview(vv, target, deployed),
        "target_number": target,
    }


def _modal(request: Request, ctx: dict, error: str | None = None, status: int = 200):
    return templates.TemplateResponse(
        request,
        "partials/deploy_modal.html",
        {**ctx, "error": error},
        status_code=status,
    )


@router.get("/repos/{uuid}/deploy/confirm", response_class=HTMLResponse)
def deploy_confirm(
    request: Request,
    uuid: str,
    version_href: str,
    pulp: PulpClient = Depends(get_pulp_client),
) -> HTMLResponse:
    try:
        ctx = _preview(pulp, uuid, version_href)
    except httpx.HTTPError as exc:
        return templates.TemplateResponse(
            request,
            "partials/deploy_modal.html",
            {
                "uuid": uuid,
                "repo_name": uuid,
                "version_href": version_href,
                "preview": None,
                "error": f"조회 실패: {exc}",
            },
            status_code=502,
        )
    return _modal(request, ctx)


@router.post("/repos/{uuid}/deploy", response_class=HTMLResponse)
def deploy(
    request: Request,
    uuid: str,
    version_href: str = Form(...),
    confirm_name: str = Form(""),
    pulp: PulpClient = Depends(get_pulp_client),
) -> HTMLResponse:
    try:
        ctx = _preview(pulp, uuid, version_href)
    except httpx.HTTPError as exc:
        return _modal(
            request,
            {
                "uuid": uuid,
                "repo_name": uuid,
                "version_href": version_href,
                "preview": None,
            },
            error=f"조회 실패: {exc}",
            status=502,
        )

    repo_name = ctx["repo_name"]

    # 1) 검증 게이트 (서버측 재확인 — 클라이언트만 믿지 않는다)
    if ctx["gpg"]["level"] != "pass":
        return _modal(
            request, ctx, "검증을 통과하지 못한 저장소는 배포할 수 없습니다.", 403
        )

    # 2) type-to-confirm
    if confirm_name != repo_name:
        return _modal(request, ctx, "저장소 이름이 일치하지 않습니다.", 400)

    # 3) 대상 distribution 확인
    dist_href = _distribution_for_repo(pulp, uuid)
    if not dist_href:
        return _modal(
            request, ctx, "이 저장소에 연결된 distribution 을 찾을 수 없습니다.", 404
        )

    # 4) publication 생성 (실패 시 운영망은 절대 변경되지 않음)
    try:
        pub_task = pulp.wait_for_task(pulp.create_publication(version_href))
    except httpx.HTTPError as exc:
        return _modal(
            request,
            ctx,
            f"배포 실패(publication 생성). 운영망은 변경되지 않았습니다: {exc}",
            502,
        )
    if pub_task.get("state") != "completed":
        return _modal(
            request,
            ctx,
            "배포 실패(publication 생성 단계). 운영망은 변경되지 않았습니다.",
            502,
        )
    pub_href = next(
        (h for h in pub_task.get("created_resources") or [] if "/publications/" in h),
        None,
    )
    if not pub_href:
        return _modal(
            request,
            ctx,
            "publication 결과를 찾을 수 없습니다. 운영망은 변경되지 않았습니다.",
            502,
        )

    # 5) distribution 교체 = 실제 운영 배포
    try:
        dist_task = pulp.wait_for_task(pulp.update_distribution(dist_href, pub_href))
    except httpx.HTTPError as exc:
        return _modal(
            request,
            ctx,
            f"배포 실패(distribution 교체). 운영망 변경에 실패했습니다: {exc}",
            502,
        )
    if dist_task.get("state") != "completed":
        return _modal(
            request,
            ctx,
            "배포 실패(distribution 교체 단계). 운영망 변경에 실패했습니다.",
            502,
        )

    # 6) 감사 로그 (이전 버전 기록 = 롤백 추적). DB 기록 실패해도 배포는 이미 완료됐으므로
    #    500 으로 죽이지 않고 audit_ok=False 로 UI 에 경고한다.
    #    TODO(운영): 감사 필수 정책이면 기록 실패 시 배포를 거부(hard-fail)하도록 강화.
    audit_ok = True
    try:
        audit.record_deploy(
            repo_uuid=uuid,
            repo_name=repo_name,
            from_version=ctx["deployed"],
            to_version=ctx["target_number"],
            publication_href=pub_href,
        )
    except Exception:  # noqa: BLE001 - DB/드라이버 오류 등 기록 실패는 배포 완료를 막지 않음
        audit_ok = False

    # 7) 성공: 버전 목록 OOB 갱신 + 모달 성공 표시
    try:
        new_ctx = load_version_context(pulp, uuid)
    except httpx.HTTPError:
        new_ctx = {"versions": [], "uuid": uuid}
    return templates.TemplateResponse(
        request,
        "partials/deploy_success.html",
        {
            **new_ctx,
            "uuid": uuid,
            "from_version": ctx["deployed"],
            "to_version": ctx["target_number"],
            "audit_ok": audit_ok,
        },
    )
