"""
Approval (HITL) Endpoints
Phase 2.2: Dry-Run 預演 API
Phase 2.3: Multi-Sig 多重簽核 API
"""

from datetime import datetime, timedelta
from typing import Literal
from uuid import UUID, uuid4

from fastapi import APIRouter, HTTPException
from pydantic import BaseModel

from src.services.approval import (
    RISK_MATRIX,
    ApprovalAlreadyDecidedError,
    ApprovalNotFoundError,
    DuplicateSignatureError,
    InsufficientPermissionError,
    TOCTOUConflictError,
    multi_sig_engine,
)
from src.services.dry_run import dry_run_engine

router = APIRouter()


class PendingAction(BaseModel):
    plugin_id: str
    operation: str
    parameters: dict
    risk_level: Literal["low", "medium", "high", "critical"]
    dry_run_result: dict | None = None


class Approval(BaseModel):
    id: UUID
    type: str
    status: Literal["pending", "approved", "rejected", "expired"]
    action: PendingAction
    requested_at: datetime
    expires_at: datetime
    decided_at: datetime | None = None
    decided_by: str | None = None
    reason: str | None = None


class ApprovalDecision(BaseModel):
    reason: str | None = None
    modified_parameters: dict | None = None


class ApprovalList(BaseModel):
    items: list[Approval]
    next_page_token: str | None = None


# ==================== Dry-Run Models ====================


class DryRunCheckResponse(BaseModel):
    """單項檢查結果"""
    name: str
    passed: bool
    message: str | None = None


class BlastRadiusResponse(BaseModel):
    """爆炸半徑"""
    affected_pods: int
    estimated_downtime: str
    related_services: list[str]
    data_impact: Literal["NONE", "READ_ONLY", "WRITE", "DESTRUCTIVE"]


class DryRunResponse(BaseModel):
    """Dry-Run 完整結果 (對應前端 ApprovalCard)"""
    checks: list[DryRunCheckResponse]
    blast_radius: BlastRadiusResponse
    overall_passed: bool
    risk_level: Literal["low", "medium", "high", "critical"]


# ==================== Multi-Sig Models (Phase 2.3) ====================


class SignatureRequest(BaseModel):
    """簽章請求"""
    user_id: str
    user_role: str  # "admin", "devops", "cto", "ciso"
    comment: str | None = None


class SignerInfo(BaseModel):
    """簽章者資訊"""
    user_id: str
    role: str
    signed_at: datetime


class SignatureStatusResponse(BaseModel):
    """簽章狀態回應"""
    approval_id: str
    risk_level: str
    status: str
    current_signatures: int
    required_signatures: int
    has_required_role: bool
    required_roles: list[str]
    signers: list[SignerInfo]


class MultiSigApproveResponse(BaseModel):
    """Multi-Sig 簽核回應"""
    approval_id: str
    status: str
    message: str
    current_signatures: int
    required_signatures: int
    needs_more: bool
    signers: list[SignerInfo]


class TOCTOUErrorResponse(BaseModel):
    """TOCTOU 衝突回應"""
    error: str
    reason: str
    failed_checks: list[str]
    signatures_cleared: bool


# In-memory storage
_approvals: dict[UUID, Approval] = {}


@router.get("", response_model=ApprovalList)
async def list_approvals(
    status: Literal["pending", "approved", "rejected", "expired"] | None = None,
) -> ApprovalList:
    """列出待授權項目"""
    items = list(_approvals.values())
    if status:
        items = [a for a in items if a.status == status]
    return ApprovalList(items=items)


@router.get("/{approval_id}", response_model=Approval)
async def get_approval(approval_id: UUID) -> Approval:
    """取得授權項目詳情"""
    if approval_id not in _approvals:
        raise HTTPException(status_code=404, detail="Approval not found")
    return _approvals[approval_id]


@router.post("/{approval_id}/approve", response_model=MultiSigApproveResponse)
async def approve_approval(
    approval_id: UUID,
    request: SignatureRequest,
) -> MultiSigApproveResponse:
    """
    Multi-Sig 簽核 (Phase 2.3)

    提交簽章到指定的審批項目。
    根據風險等級，可能需要多個簽章才能完成審批。

    風險矩陣:
    - low: 自動執行
    - medium: 需要 1 位 admin/devops
    - high: 需要 2 位管理員
    - critical: 需要 2 人，含 CTO 或 CISO

    ⚠️ TOCTOU 防護:
    當簽章達到閾值時，會自動重新執行 Dry-Run。
    如果資源狀態已改變，將回傳 409 Conflict 並清空所有簽章。
    """
    # 確保 Approval 存在於舊系統
    if approval_id not in _approvals:
        raise HTTPException(status_code=404, detail="Approval not found")

    approval = _approvals[approval_id]

    # 同步到 Multi-Sig 引擎 (如果還沒有)
    try:
        multi_sig_engine.get_approval(approval_id)
    except ApprovalNotFoundError:
        multi_sig_engine.create_approval(
            approval_id=approval_id,
            operation=approval.action.operation,
            parameters=approval.action.parameters,
            risk_level=approval.action.risk_level,
        )

    # 執行簽核
    try:
        state = multi_sig_engine.approve_request(
            approval_id=approval_id,
            user_id=request.user_id,
            user_role=request.user_role,
            comment=request.comment,
        )

        # 同步狀態回舊系統
        if state.status.value == "approved":
            approval.status = "approved"
            approval.decided_at = state.executed_at

        requirement = RISK_MATRIX[state.risk_level]

        return MultiSigApproveResponse(
            approval_id=str(approval_id),
            status=state.status.value,
            message=(
                "Approval complete - executing action"
                if state.status.value == "approved"
                else f"Signature recorded ({len(state.signatures)}/{requirement.min_signatures})"
            ),
            current_signatures=len(state.signatures),
            required_signatures=requirement.min_signatures,
            needs_more=len(state.signatures) < requirement.min_signatures,
            signers=[
                SignerInfo(
                    user_id=sig.user_id,
                    role=sig.user_role.value,
                    signed_at=sig.signed_at,
                )
                for sig in state.signatures
            ],
        )

    except InsufficientPermissionError as e:
        raise HTTPException(
            status_code=403,
            detail={
                "error": "Insufficient permission",
                "role": e.role,
                "required_roles": e.required_roles,
            },
        ) from e

    except DuplicateSignatureError as e:
        raise HTTPException(
            status_code=409,
            detail={
                "error": "Duplicate signature",
                "user_id": e.user_id,
            },
        ) from e

    except ApprovalAlreadyDecidedError as e:
        raise HTTPException(
            status_code=400,
            detail={"error": str(e)},
        ) from e

    except TOCTOUConflictError as e:
        # ⚠️ TOCTOU 衝突 - 資源狀態已改變
        raise HTTPException(
            status_code=409,
            detail={
                "error": "TOCTOU Conflict",
                "reason": e.reason,
                "failed_checks": e.failed_checks,
                "signatures_cleared": True,
            },
        ) from e


@router.post("/{approval_id}/reject", response_model=Approval)
async def reject_approval(approval_id: UUID, decision: ApprovalDecision) -> Approval:
    """拒絕授權"""
    if approval_id not in _approvals:
        raise HTTPException(status_code=404, detail="Approval not found")

    approval = _approvals[approval_id]
    approval.status = "rejected"
    approval.decided_at = datetime.utcnow()
    approval.reason = decision.reason

    # 同步到 Multi-Sig 引擎
    try:
        multi_sig_engine.reject_request(
            approval_id=approval_id,
            user_id="system",
            user_role="admin",
            reason=decision.reason,
        )
    except (ApprovalNotFoundError, ApprovalAlreadyDecidedError):
        pass  # 忽略，舊系統已處理

    return approval


@router.get("/{approval_id}/signatures", response_model=SignatureStatusResponse)
async def get_signature_status(approval_id: UUID) -> SignatureStatusResponse:
    """
    取得簽章狀態 (Phase 2.3)

    回傳目前有多少簽章、還需要多少、已簽核者列表等資訊
    """
    if approval_id not in _approvals:
        raise HTTPException(status_code=404, detail="Approval not found")

    approval = _approvals[approval_id]

    # 確保同步到 Multi-Sig 引擎
    try:
        multi_sig_engine.get_approval(approval_id)
    except ApprovalNotFoundError:
        multi_sig_engine.create_approval(
            approval_id=approval_id,
            operation=approval.action.operation,
            parameters=approval.action.parameters,
            risk_level=approval.action.risk_level,
        )

    status = multi_sig_engine.get_signature_status(approval_id)

    return SignatureStatusResponse(
        approval_id=status["approval_id"],
        risk_level=status["risk_level"],
        status=status["status"],
        current_signatures=status["current_signatures"],
        required_signatures=status["required_signatures"],
        has_required_role=status["has_required_role"],
        required_roles=status["required_roles"],
        signers=[
            SignerInfo(
                user_id=s["user_id"],
                role=s["role"],
                signed_at=datetime.fromisoformat(s["signed_at"]),
            )
            for s in status["signers"]
        ],
    )


@router.get("/{approval_id}/dry-run", response_model=DryRunResponse)
async def run_dry_run(approval_id: UUID) -> DryRunResponse:
    """
    執行 Dry-Run 預演檢查

    Phase 2.2: 回傳 ApprovalCard 所需的 dryRunChecks 格式
    - RBAC 權限檢查
    - 語法正確性
    - 資源存在性
    - 爆炸半徑評估
    """
    if approval_id not in _approvals:
        raise HTTPException(status_code=404, detail="Approval not found")

    approval = _approvals[approval_id]
    action = approval.action

    # 執行 Dry-Run 引擎
    result = dry_run_engine.evaluate(
        operation=action.operation,
        parameters=action.parameters,
        user_role="cluster-admin",  # TODO: 從 JWT 取得真實角色
    )

    # 轉換為 API Response 格式
    return DryRunResponse(
        checks=[
            DryRunCheckResponse(
                name=c.name,
                passed=c.passed,
                message=c.message,
            )
            for c in result.checks
        ],
        blast_radius=BlastRadiusResponse(
            affected_pods=result.blast_radius.affected_pods,
            estimated_downtime=result.blast_radius.estimated_downtime,
            related_services=result.blast_radius.related_services,
            data_impact=result.blast_radius.data_impact,
        ),
        overall_passed=result.overall_passed,
        risk_level=result.risk_level,
    )


@router.post("/dry-run/preview", response_model=DryRunResponse)
async def preview_dry_run(
    operation: str,
    parameters: dict,
    user_role: str = "cluster-admin",
) -> DryRunResponse:
    """
    預覽 Dry-Run (不需要先建立 Approval)

    用於前端即時預覽操作風險
    """
    result = dry_run_engine.evaluate(
        operation=operation,
        parameters=parameters,
        user_role=user_role,
    )

    return DryRunResponse(
        checks=[
            DryRunCheckResponse(
                name=c.name,
                passed=c.passed,
                message=c.message,
            )
            for c in result.checks
        ],
        blast_radius=BlastRadiusResponse(
            affected_pods=result.blast_radius.affected_pods,
            estimated_downtime=result.blast_radius.estimated_downtime,
            related_services=result.blast_radius.related_services,
            data_impact=result.blast_radius.data_impact,
        ),
        overall_passed=result.overall_passed,
        risk_level=result.risk_level,
    )


# ==================== Test Helpers ====================


def create_test_approval(
    operation: str = "delete_pod",
    parameters: dict | None = None,
    risk_level: Literal["low", "medium", "high", "critical"] = "high",
) -> Approval:
    """Create a test approval for development"""
    approval_id = uuid4()
    now = datetime.utcnow()

    if parameters is None:
        if operation == "delete_pod":
            parameters = {"pod_name": "nginx-frontend-7d4b8c9f5-xk2m3"}
        elif operation == "drop_table":
            parameters = {"table_name": "user_sessions"}
        else:
            parameters = {}

    approval = Approval(
        id=approval_id,
        type="action_execution",
        status="pending",
        action=PendingAction(
            plugin_id="lewooogo-action-k8s",
            operation=operation,
            parameters=parameters,
            risk_level=risk_level,
        ),
        requested_at=now,
        expires_at=now + timedelta(hours=1),
    )
    _approvals[approval_id] = approval
    return approval


def create_test_approvals() -> list[Approval]:
    """建立多個測試 Approval (對應前端 Mock Data)"""
    return [
        # HIGH RISK: 刪除 Pod
        create_test_approval(
            operation="delete_pod",
            parameters={"pod_name": "nginx-frontend-7d4b8c9f5-xk2m3"},
            risk_level="high",
        ),
        # CRITICAL: DROP TABLE (DESTRUCTIVE)
        create_test_approval(
            operation="drop_table",
            parameters={"table_name": "user_sessions"},
            risk_level="critical",
        ),
        # MEDIUM: Scale Deployment
        create_test_approval(
            operation="scale_deployment",
            parameters={"deployment": "api-server", "replicas": 5},
            risk_level="medium",
        ),
    ]
