"""
Proposals API - Phase 6.4h Decision Proposal REST API
======================================================

完整的 Decision Proposal CRUD 端點:
- POST   /api/v1/proposals           - 建立新提案
- GET    /api/v1/proposals           - 查詢提案清單
- GET    /api/v1/proposals/{id}      - 查詢單一提案
- PATCH  /api/v1/proposals/{id}/approve - 批准提案

整合:
- ProposalService (真實 LLM 決策)
- ApprovalService (持久化與狀態管理)
- TrustEngine (風險評估)

統帥鐵律:
- 禁止跳過 TrustEngine 評估
- 所有提案必須 require_dry_run: true
- 所有決策必須可稽核

Version: 6.4h
Date: 2026-03-23
"""

from datetime import datetime
from typing import Annotated
from uuid import UUID

from fastapi import APIRouter, HTTPException, Query, status
from pydantic import BaseModel, Field

from src.core.logging import get_logger
from src.models.approval import (
    ApprovalRequest,
    ApprovalStatus,
    RiskLevel,
)
from src.services.approval_db import get_approval_service
from src.services.proposal_service import get_proposal_service

router = APIRouter(prefix="/proposals", tags=["Proposals"])
logger = get_logger("awoooi.proposals")


# =============================================================================
# Request/Response Models
# =============================================================================

class ProposalCreateRequest(BaseModel):
    """建立提案請求"""
    incident_id: str = Field(..., description="關聯的事件 ID")
    require_dry_run: bool = Field(
        default=True,
        description="強制要求演練模式 (Guardrails)",
    )
    skill_id: str | None = Field(
        default=None,
        description="指定使用的 Skill ID (e.g., '04-awoooi-devops-commander')",
    )


class ProposalResponse(BaseModel):
    """提案回應 (向下相容 ApprovalRequest)"""
    proposal_id: str = Field(..., description="提案 ID")
    incident_id: str | None = Field(None, description="關聯的事件 ID")
    action: str = Field(..., description="執行動作")
    description: str = Field(..., description="詳細說明")
    status: str = Field(..., description="狀態")
    risk_level: str = Field(..., description="風險等級")
    tier: int = Field(..., description="授權級別 (1: 自主, 2: 授權, 3: 親核)")
    required_signatures: int = Field(..., description="所需簽核數")
    current_signatures: int = Field(..., description="目前簽核數")
    guardrails_passed: bool = Field(default=True, description="是否通過安全護欄")
    llm_provider: str | None = Field(None, description="LLM 提供者")
    llm_confidence: float | None = Field(None, description="LLM 信心度")
    kubectl_command: str | None = Field(None, description="生成的 kubectl 指令")
    created_at: datetime = Field(..., description="建立時間")
    updated_at: datetime = Field(..., description="更新時間")

    @classmethod
    def from_approval(cls, approval: ApprovalRequest) -> "ProposalResponse":
        """從 ApprovalRequest 轉換"""
        metadata = approval.metadata or {}
        incident_id = metadata.get("incident_id")

        # 計算 tier 基於 risk_level
        tier_map = {
            RiskLevel.LOW: 1,       # 自主 (AI 可直接執行)
            RiskLevel.MEDIUM: 2,    # 授權 (需 1 人簽核)
            RiskLevel.CRITICAL: 3,  # 親核 (需 2 人簽核)
        }
        tier = tier_map.get(approval.risk_level, 2)

        return cls(
            proposal_id=str(approval.id),
            incident_id=incident_id,
            action=approval.action,
            description=approval.description,
            status=approval.status.value,
            risk_level=approval.risk_level.value,
            tier=tier,
            required_signatures=approval.required_signatures,
            current_signatures=approval.current_signatures,
            guardrails_passed=True,
            llm_provider=metadata.get("llm_provider"),
            llm_confidence=metadata.get("llm_confidence"),
            kubectl_command=metadata.get("kubectl_command"),
            created_at=approval.created_at,
            updated_at=approval.updated_at,
        )


class ProposalListResponse(BaseModel):
    """提案清單回應"""
    count: int = Field(..., description="總數")
    proposals: list[ProposalResponse] = Field(..., description="提案清單")


class ProposalApproveRequest(BaseModel):
    """批准提案請求"""
    signer_id: str = Field(..., description="簽核者 ID")
    signer_name: str = Field(..., description="簽核者名稱")
    comment: str | None = Field(None, description="簽核備註")
    source: str = Field(
        default="api",
        description="簽核來源 (web/telegram/api)",
    )


class ProposalApproveResponse(BaseModel):
    """批准提案回應"""
    success: bool = Field(..., description="是否成功")
    message: str = Field(..., description="訊息")
    proposal: ProposalResponse = Field(..., description="更新後的提案")
    fully_approved: bool = Field(..., description="是否已完全批准")
    execution_triggered: bool = Field(
        default=False,
        description="是否觸發執行",
    )


# =============================================================================
# POST /api/v1/proposals - 建立新提案
# =============================================================================

@router.post(
    "",
    response_model=ProposalResponse,
    status_code=status.HTTP_201_CREATED,
    summary="建立決策提案 (Phase 6.4h)",
    description="""
    從 Incident 生成 Decision Proposal。

    流程:
    1. Guardrails 前置檢查 (require_dry_run 必須為 True)
    2. 從 Redis/PostgreSQL 載入 Incident
    3. 呼叫 OpenClaw LLM 生成提案 (Ollama → Gemini → Claude fallback)
    4. TrustEngine 風險評估與 Tier 判定
    5. 建立 ApprovalRequest
    6. 返回 ProposalResponse
    """,
)
async def create_proposal(
    request: ProposalCreateRequest,
) -> ProposalResponse:
    """
    建立新的決策提案

    Args:
        request: 提案建立請求

    Returns:
        ProposalResponse: 建立的提案

    Raises:
        HTTPException: 422 Guardrails 違規, 400 無法生成, 404 Incident 不存在
    """
    try:
        # 1. Guardrails 檢查: require_dry_run 必須為 True
        if not request.require_dry_run:
            logger.warning(
                "guardrails_rejected",
                incident_id=request.incident_id,
                reason="require_dry_run must be True",
            )
            raise HTTPException(
                status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
                detail="Guardrail triggered: require_dry_run must be True",
            )

        logger.info(
            "proposal_create_start",
            incident_id=request.incident_id,
            skill_id=request.skill_id,
        )

        # 2. 呼叫 ProposalService 生成提案
        service = get_proposal_service()
        approval, message = await service.generate_proposal(request.incident_id)

        if approval is None:
            if "not found" in message.lower():
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail=message,
                )
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=message,
            )

        logger.info(
            "proposal_created",
            proposal_id=str(approval.id),
            incident_id=request.incident_id,
            risk_level=approval.risk_level.value,
        )

        return ProposalResponse.from_approval(approval)

    except HTTPException:
        raise
    except Exception as e:
        logger.exception(
            "proposal_create_error",
            incident_id=request.incident_id,
            error=str(e),
        )
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Internal Error: {str(e)}",
        ) from e


# =============================================================================
# GET /api/v1/proposals - 查詢提案清單
# =============================================================================

@router.get(
    "",
    response_model=ProposalListResponse,
    summary="查詢提案清單",
    description="取得所有提案，可依狀態篩選。",
)
async def list_proposals(
    status_filter: Annotated[
        ApprovalStatus | None,
        Query(
            alias="status",
            description="篩選狀態 (pending/approved/rejected/expired)",
        ),
    ] = None,
    incident_id: Annotated[
        str | None,
        Query(description="篩選特定 Incident 的提案"),
    ] = None,
    limit: Annotated[int, Query(ge=1, le=200, description="每頁數量")] = 50,
    offset: Annotated[int, Query(ge=0, description="偏移量")] = 0,
) -> ProposalListResponse:
    """
    查詢提案清單

    Args:
        status_filter: 狀態篩選
        incident_id: Incident ID 篩選
        limit: 每頁數量
        offset: 偏移量

    Returns:
        ProposalListResponse: 提案清單
    """
    try:
        approval_service = get_approval_service()

        # 取得所有提案 (根據狀態篩選)
        if status_filter == ApprovalStatus.PENDING:
            approvals = await approval_service.get_pending_approvals()
        else:
            # 取得所有狀態的提案
            approvals = await approval_service.get_all_approvals(
                status=status_filter,
                incident_id=incident_id,
                limit=limit,
                offset=offset,
            )

        # 轉換為 ProposalResponse
        proposals = [ProposalResponse.from_approval(a) for a in approvals]

        # 如果指定了 incident_id，進一步過濾
        if incident_id:
            proposals = [p for p in proposals if p.incident_id == incident_id]

        logger.info(
            "proposals_listed",
            count=len(proposals),
            status_filter=status_filter.value if status_filter else None,
            incident_id=incident_id,
        )

        return ProposalListResponse(
            count=len(proposals),
            proposals=proposals,
        )

    except Exception as e:
        logger.exception(
            "proposals_list_error",
            error=str(e),
        )
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to list proposals: {str(e)}",
        ) from e


# =============================================================================
# GET /api/v1/proposals/{proposal_id} - 查詢單一提案
# =============================================================================

@router.get(
    "/{proposal_id}",
    response_model=ProposalResponse,
    summary="查詢單一提案",
    description="取得特定提案的詳細資訊。",
)
async def get_proposal(
    proposal_id: str,
) -> ProposalResponse:
    """
    查詢單一提案

    Args:
        proposal_id: 提案 ID

    Returns:
        ProposalResponse: 提案詳細資訊

    Raises:
        HTTPException: 404 提案不存在
    """
    try:
        approval_service = get_approval_service()

        # 驗證 UUID 格式
        try:
            uuid = UUID(proposal_id)
        except ValueError as e:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"Invalid proposal ID format: {proposal_id}",
            ) from e

        approval = await approval_service.get_approval_by_id(uuid)

        if approval is None:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Proposal not found: {proposal_id}",
            )

        logger.info(
            "proposal_fetched",
            proposal_id=proposal_id,
            status=approval.status.value,
        )

        return ProposalResponse.from_approval(approval)

    except HTTPException:
        raise
    except Exception as e:
        logger.exception(
            "proposal_get_error",
            proposal_id=proposal_id,
            error=str(e),
        )
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to get proposal: {str(e)}",
        ) from e


# =============================================================================
# PATCH /api/v1/proposals/{proposal_id}/approve - 批准提案
# =============================================================================

@router.patch(
    "/{proposal_id}/approve",
    response_model=ProposalApproveResponse,
    summary="批准提案",
    description="""
    對提案進行簽核批准。

    Multi-Sig 規則:
    - LOW 風險: 0 人簽核，自動放行
    - MEDIUM 風險: 1 人簽核
    - CRITICAL 風險: 2 人 Multi-Sig 雙重簽核

    當簽核數滿足時，狀態自動變更為 APPROVED。
    """,
)
async def approve_proposal(
    proposal_id: str,
    request: ProposalApproveRequest,
) -> ProposalApproveResponse:
    """
    批准提案

    Args:
        proposal_id: 提案 ID
        request: 批准請求

    Returns:
        ProposalApproveResponse: 批准結果

    Raises:
        HTTPException: 404 提案不存在, 400 簽核失敗
    """
    try:
        approval_service = get_approval_service()

        # 驗證 UUID 格式
        try:
            uuid = UUID(proposal_id)
        except ValueError as e:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"Invalid proposal ID format: {proposal_id}",
            ) from e

        # 取得現有提案
        approval = await approval_service.get_approval_by_id(uuid)
        if approval is None:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Proposal not found: {proposal_id}",
            )

        # 檢查狀態
        if approval.status != ApprovalStatus.PENDING:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"Cannot approve proposal in status: {approval.status.value}",
            )

        # 檢查是否已簽核
        if approval.has_signer(request.signer_id):
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"Signer {request.signer_id} has already signed this proposal",
            )

        # 執行簽核 (sign_approval 返回 tuple[ApprovalRequest, str, bool])
        updated_approval, message, execution_triggered = await approval_service.sign_approval(
            approval_id=uuid,
            signer_id=request.signer_id,
            signer_name=request.signer_name,
            comment=request.comment,
        )

        if updated_approval is None:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=message,
            )

        # 檢查是否滿足簽核數
        fully_approved = updated_approval.status == ApprovalStatus.APPROVED
        execution_triggered = fully_approved  # 滿足簽核數即觸發執行

        logger.info(
            "proposal_approved",
            proposal_id=proposal_id,
            signer_id=request.signer_id,
            current_signatures=updated_approval.current_signatures,
            required_signatures=updated_approval.required_signatures,
            fully_approved=fully_approved,
        )

        return ProposalApproveResponse(
            success=True,
            message=message,
            proposal=ProposalResponse.from_approval(updated_approval),
            fully_approved=fully_approved,
            execution_triggered=execution_triggered,
        )

    except HTTPException:
        raise
    except Exception as e:
        logger.exception(
            "proposal_approve_error",
            proposal_id=proposal_id,
            error=str(e),
        )
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to approve proposal: {str(e)}",
        ) from e
