"""
HITL Approval API Endpoints (Phase 5: Database Persistence)
============================================================
CISO-101: 授權請求與 Multi-Sig 簽核 API
CTO-201: 背景執行整合
Phase 5: 永久記憶植入 (SQLite/PostgreSQL)

Endpoints:
- GET  /api/v1/approvals/pending     - 取得待簽核清單
- POST /api/v1/approvals             - 建立新授權請求
- POST /api/v1/approvals/{id}/sign   - 提交簽核
- POST /api/v1/approvals/{id}/reject - 拒絕請求

信任鏈流程:
1. OpenClaw 發起 CRITICAL 操作 → 建立 ApprovalRequest (PENDING) → 寫入 DB
2. 第一位簽核者簽核 → 仍為 PENDING (1/2) → 更新 DB
3. 第二位簽核者簽核 → 轉為 APPROVED → 更新 DB
4. BackgroundTasks 觸發 K8s 執行 → EXECUTION_SUCCESS/FAILED → 更新 DB

⚠️ Phase 5 變更: 所有資料現在持久化至資料庫，重啟後資料完好無缺！
"""

import asyncio
from uuid import UUID

from fastapi import (
    APIRouter,
    BackgroundTasks,
    Depends,
    Header,
    HTTPException,
    Request,
    status,
)
from fastapi.responses import StreamingResponse

from src.core.config import settings
from src.core.csrf import CSRFToken  # Phase 20: CSRF Protection
from src.core.logging import get_logger
from src.core.sse import EventType, SSEEvent, get_publisher
from src.models.approval import (
    ApprovalRequest,
    ApprovalRequestCreate,
    ApprovalRequestResponse,
    BulkApproveRequest,
    BulkApproveResponse,
    BulkApproveResult,
    PendingApprovalsResponse,
    RejectRequest,
    SignRequest,
    SignResponse,
)
from src.services.approval_db import get_approval_service, get_timeline_service
from src.services.approval_execution import get_execution_service
from src.services.executor import get_executor
from src.services.incident_approval_service import get_incident_approval_service

router = APIRouter(prefix="/approvals", tags=["HITL Approvals"])
logger = get_logger("awoooi.approvals")


# =============================================================================
# K8s Connection Test (CTO-201 Debug) - Protected Endpoint
# =============================================================================


async def verify_k8s_api_key(
    x_k8s_api_key: str | None = Header(None, alias="X-K8s-Api-Key"),
) -> None:
    """
    驗證 K8s 管理端點的 API Key

    安全鐵律 (Fail-Closed):
    - 生產環境: K8S_API_KEY 未設定 → 直接拒絕
    - 開發環境: K8S_API_KEY 未設定 → 允許跳過
    - API Key 必須完全匹配

    Args:
        x_k8s_api_key: X-K8s-Api-Key Header 值

    Raises:
        HTTPException: 401 未認證
    """
    # Fail-Closed 安全策略
    if not settings.K8S_API_KEY:
        if settings.ENVIRONMENT == "prod":
            logger.critical(
                "k8s_api_key_missing_in_production",
                environment=settings.ENVIRONMENT,
            )
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Authentication required",
            )
        # 開發環境: 允許跳過
        logger.warning(
            "k8s_api_key_verification_skipped_dev_only",
            environment=settings.ENVIRONMENT,
        )
        return

    # 必須提供 API Key
    if not x_k8s_api_key:
        logger.warning("k8s_api_key_missing")
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Authentication required",
        )

    # 驗證 API Key
    if x_k8s_api_key != settings.K8S_API_KEY:
        logger.warning("k8s_api_key_invalid")
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Authentication required",
        )

    logger.info("k8s_api_key_verification_success")


@router.get(
    "/k8s-test",
    summary="測試 K8s 連線",
    description="連接 K3s 叢集並列出所有 Namespace。用於驗證 kubeconfig 設定。需要 X-K8s-Api-Key 認證。",
    dependencies=[Depends(verify_k8s_api_key)],
)
async def test_k8s_connection() -> dict:
    """
    測試 K8s 連線 (需要認證)

    Headers:
        X-K8s-Api-Key: K8s 管理端點 API Key

    Returns:
        namespaces: 所有 Namespace 清單
        success: 是否連線成功
    """
    executor = get_executor()
    namespaces = await executor.list_namespaces()

    if namespaces:
        logger.info(
            "k8s_connection_test_success",
            namespaces=namespaces,
        )
        return {
            "success": True,
            "message": f"Connected to K3s cluster. Found {len(namespaces)} namespaces.",
            "namespaces": namespaces,
        }
    else:
        logger.warning("k8s_connection_test_failed")
        return {
            "success": False,
            "message": "Failed to connect to K3s cluster. Check kubeconfig.",
            "namespaces": [],
        }


# =============================================================================
# Phase 16 R4.2: 執行邏輯已抽取至 src/services/approval_execution.py
# =============================================================================


# =============================================================================
# GET /api/v1/approvals/pending
# =============================================================================

@router.get(
    "/pending",
    response_model=PendingApprovalsResponse,
    summary="取得待簽核清單",
    description="獲取所有等待簽核的授權請求，供戰情室前端渲染。(Phase 5: Database)",
)
async def get_pending_approvals() -> PendingApprovalsResponse:
    """
    取得待簽核清單 (Phase 5: 從資料庫讀取)

    Returns:
        PendingApprovalsResponse: 待簽核請求清單與計數
    """
    service = get_approval_service()
    pending = await service.get_pending_approvals()

    logger.info(
        "pending_approvals_fetched_db",
        count=len(pending),
    )

    return PendingApprovalsResponse(
        count=len(pending),
        approvals=[
            ApprovalRequestResponse.from_approval(a) for a in pending
        ],
    )


# =============================================================================
# POST /api/v1/approvals
# =============================================================================

@router.post(
    "",
    response_model=ApprovalRequestResponse,
    status_code=status.HTTP_201_CREATED,
    summary="建立授權請求",
    description="建立新的 HITL 授權請求。LOW 風險自動批准，MEDIUM/CRITICAL 需要簽核。(Phase 5: Database)",
)
async def create_approval(
    request: ApprovalRequestCreate,
) -> ApprovalRequestResponse:
    """
    建立授權請求 (Phase 5: 寫入資料庫)

    風險等級對應所需簽核數:
    - LOW: 0 人 (自動批准)
    - MEDIUM: 1 人
    - CRITICAL: 2 人 (Multi-Sig)

    Args:
        request: 授權請求內容

    Returns:
        ApprovalRequestResponse: 建立的授權請求
    """
    service = get_approval_service()
    approval = await service.create_approval(request)

    # Phase 4: Log timeline event
    timeline = get_timeline_service()
    await timeline.add_event(
        event_type="system",
        status="info",
        title=f"新授權請求建立: {approval.action[:50]}...",
        risk_level=approval.risk_level.value,
        approval_id=str(approval.id),
    )

    logger.info(
        "approval_created_db",
        id=str(approval.id),
        action=approval.action,
        risk_level=approval.risk_level.value,
        status=approval.status.value,
        required_signatures=approval.required_signatures,
    )

    # SSE: 發布建立事件
    asyncio.create_task(_publish_approval_event("created", approval))

    return ApprovalRequestResponse.from_approval(approval)


# =============================================================================
# POST /api/v1/approvals/{id}/sign
# =============================================================================

@router.post(
    "/{approval_id}/sign",
    response_model=SignResponse,
    summary="簽核授權請求",
    description="提交簽核。當滿足所需簽核數時，狀態轉為 APPROVED 並觸發背景執行。(Phase 5: Database + K8s Executor)",
)
async def sign_approval(
    approval_id: UUID,
    request: SignRequest,
    background_tasks: BackgroundTasks,
    _csrf_token: CSRFToken,  # Phase 20: CSRF Protection (驗證用，不需要使用值)
) -> SignResponse:
    """
    簽核授權請求 (Phase 5: Database + K8s Execution)

    Multi-Sig 流程:
    1. CRITICAL 需要 2 人簽核
    2. 第一人簽核後仍為 PENDING
    3. 第二人簽核後轉為 APPROVED → 觸發 K8s Executor

    Args:
        approval_id: 授權請求 ID
        request: 簽核資訊 (簽核者 ID, 名稱, 備註)

    Returns:
        SignResponse: 簽核結果

    Raises:
        HTTPException: 404 找不到請求, 400 無法簽核
    """
    service = get_approval_service()
    timeline = get_timeline_service()

    approval, message, execution_triggered = await service.sign_approval(
        approval_id=approval_id,
        signer_id=request.signer_id,
        signer_name=request.signer_name,
        comment=request.comment,
    )

    if approval is None:
        logger.warning(
            "sign_approval_not_found",
            approval_id=str(approval_id),
        )
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Approval request not found",
        )

    # 檢查是否為錯誤情況 (已簽核或狀態不對)
    if "Cannot sign" in message or "already signed" in message:
        logger.warning(
            "sign_approval_failed",
            approval_id=str(approval_id),
            message=message,
        )
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=message,
        )

    # Phase 4: Log timeline event
    await timeline.add_event(
        event_type="human",
        status="success",
        title=f"{request.signer_name} 簽核成功 ({approval.current_signatures}/{approval.required_signatures})",
        actor=request.signer_name,
        actor_role="signer",
        risk_level=approval.risk_level.value,
        approval_id=str(approval_id),
    )

    logger.info(
        "approval_signed_db",
        approval_id=str(approval_id),
        signer_id=request.signer_id,
        signer_name=request.signer_name,
        current_signatures=approval.current_signatures,
        required_signatures=approval.required_signatures,
        execution_triggered=execution_triggered,
    )

    # Phase 5: 當簽核數滿足時，觸發背景執行 (真實 K8s Executor)
    if execution_triggered:
        logger.info(
            "k8s_executor_scheduled",
            approval_id=str(approval_id),
            action=approval.action,
        )

        # Log timeline event for execution
        await timeline.add_event(
            event_type="exec",
            status="warning",
            title=f"K8s Executor 已排程執行: {approval.action[:40]}...",
            actor="OpenClaw",
            actor_role="executor",
            approval_id=str(approval_id),
        )

        execution_svc = get_execution_service()
        background_tasks.add_task(execution_svc.execute_approved_action, approval)

        # Phase 6.5 + ADR-027: 更新關聯的 Incident 狀態為 RESOLVED
        # 🔴 2026-03-26: 使用 IncidentApprovalService 確保原子同步
        try:
            sync_service = get_incident_approval_service()
            await sync_service.on_approval_status_change(
                approval_id=str(approval_id),
                new_status="approved",
            )
            logger.info(
                "incident_approval_synced",
                approval_id=str(approval_id),
                new_status="approved",
            )
        except Exception as e:
            # 同步失敗不阻斷主流程，但記錄錯誤
            logger.error(
                "incident_approval_sync_failed",
                approval_id=str(approval_id),
                error=str(e),
                note="Incident status may not be updated correctly",
            )

    # SSE: 發布簽核/核准事件
    event_action = "approved" if execution_triggered else "signed"
    asyncio.create_task(_publish_approval_event(event_action, approval))

    return SignResponse(
        success=True,
        message=message,
        approval=ApprovalRequestResponse.from_approval(approval),
        execution_triggered=execution_triggered,
    )


# =============================================================================
# POST /api/v1/approvals/{id}/reject
# =============================================================================

@router.post(
    "/{approval_id}/reject",
    response_model=ApprovalRequestResponse,
    summary="拒絕授權請求",
    description="拒絕並終止授權請求。狀態轉為 REJECTED。(Phase 5: Database)",
)
async def reject_approval(
    approval_id: UUID,
    request: RejectRequest,
    _csrf_token: CSRFToken,  # Phase 20: CSRF Protection
) -> ApprovalRequestResponse:
    """
    拒絕授權請求 (Phase 5: Database)

    Args:
        approval_id: 授權請求 ID
        request: 拒絕資訊 (拒絕者 ID, 名稱, 原因)

    Returns:
        ApprovalRequestResponse: 更新後的授權請求

    Raises:
        HTTPException: 404 找不到請求, 400 無法拒絕
    """
    service = get_approval_service()
    timeline = get_timeline_service()

    approval, message = await service.reject_approval(
        approval_id=approval_id,
        rejector_id=request.rejector_id,
        rejector_name=request.rejector_name,
        reason=request.reason,
    )

    if approval is None:
        logger.warning(
            "reject_approval_not_found",
            approval_id=str(approval_id),
        )
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Approval request not found",
        )

    if "Cannot reject" in message:
        logger.warning(
            "reject_approval_failed",
            approval_id=str(approval_id),
            message=message,
        )
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=message,
        )

    # Phase 4: Log timeline event
    await timeline.add_event(
        event_type="security",
        status="error",
        title=f"{request.rejector_name} 拒絕授權請求",
        description=request.reason,
        actor=request.rejector_name,
        actor_role="rejector",
        approval_id=str(approval_id),
    )

    logger.info(
        "approval_rejected_db",
        approval_id=str(approval_id),
        rejector_id=request.rejector_id,
        rejector_name=request.rejector_name,
        reason=request.reason,
    )

    # ADR-027: 同步更新 Incident 狀態為 rejected
    try:
        sync_service = get_incident_approval_service()
        await sync_service.on_approval_status_change(
            approval_id=str(approval_id),
            new_status="rejected",
        )
        logger.info(
            "incident_approval_synced",
            approval_id=str(approval_id),
            new_status="rejected",
        )
    except Exception as e:
        logger.error(
            "incident_approval_sync_failed",
            approval_id=str(approval_id),
            error=str(e),
        )

    # SSE: 發布拒絕事件
    asyncio.create_task(_publish_approval_event("rejected", approval))

    return ApprovalRequestResponse.from_approval(approval)


# =============================================================================
# POST /api/v1/approvals/bulk-approve (Phase 11: 批次處理)
# =============================================================================

@router.post(
    "/bulk-approve",
    response_model=BulkApproveResponse,
    summary="批次簽核",
    description="批次簽核多個授權請求。CRITICAL 風險或 DESTRUCTIVE 資料影響的請求將被跳過。",
)
async def bulk_approve(
    request: BulkApproveRequest,
    background_tasks: BackgroundTasks,
    _csrf_token: CSRFToken,  # Phase 20: CSRF Protection
) -> BulkApproveResponse:
    """
    批次簽核授權請求 (Phase 11)

    安全限制 (不可繞過):
    - CRITICAL 風險: 跳過，需單獨審核
    - DESTRUCTIVE 資料影響: 跳過，需單獨審核

    Args:
        request: 批次簽核請求

    Returns:
        BulkApproveResponse: 批次處理結果
    """
    from src.models.approval import RiskLevel

    service = get_approval_service()
    timeline = get_timeline_service()

    results: list[BulkApproveResult] = []
    succeeded = 0
    failed = 0
    skipped = 0

    for approval_id_str in request.approval_ids:
        try:
            approval_id = UUID(approval_id_str)

            # 取得 Approval 詳情
            approval = await service.get_approval_by_id(approval_id)

            if approval is None:
                results.append(BulkApproveResult(
                    approval_id=approval_id_str,
                    success=False,
                    message="Not found",
                ))
                failed += 1
                continue

            # 🔴 安全限制: CRITICAL 禁止批次核准
            if approval.risk_level == RiskLevel.CRITICAL:
                results.append(BulkApproveResult(
                    approval_id=approval_id_str,
                    success=False,
                    message="CRITICAL risk requires individual review",
                ))
                skipped += 1
                logger.warning(
                    "bulk_approve_skipped_critical",
                    approval_id=approval_id_str,
                    risk_level=approval.risk_level.value,
                )
                continue

            # 🔴 安全限制: DESTRUCTIVE 禁止批次核准
            if approval.blast_radius and approval.blast_radius.data_impact == "DESTRUCTIVE":
                results.append(BulkApproveResult(
                    approval_id=approval_id_str,
                    success=False,
                    message="DESTRUCTIVE data impact requires individual review",
                ))
                skipped += 1
                logger.warning(
                    "bulk_approve_skipped_destructive",
                    approval_id=approval_id_str,
                    data_impact="DESTRUCTIVE",
                )
                continue

            # 執行簽核
            signed_approval, message, execution_triggered = await service.sign_approval(
                approval_id=approval_id,
                signer_id=request.signer_id,
                signer_name=request.signer_name,
                comment=request.comment or "Bulk approved",
            )

            if signed_approval is None or "Cannot sign" in message or "already signed" in message:
                results.append(BulkApproveResult(
                    approval_id=approval_id_str,
                    success=False,
                    message=message,
                ))
                failed += 1
                continue

            # 成功簽核
            results.append(BulkApproveResult(
                approval_id=approval_id_str,
                success=True,
                message=message,
                execution_triggered=execution_triggered,
            ))
            succeeded += 1

            # Timeline event
            await timeline.add_event(
                event_type="human",
                status="success",
                title=f"[批次] {request.signer_name} 簽核成功",
                actor=request.signer_name,
                actor_role="signer",
                risk_level=signed_approval.risk_level.value,
                approval_id=approval_id_str,
            )

            # 如果觸發執行，加入背景任務
            if execution_triggered:
                bulk_execution_svc = get_execution_service()
                background_tasks.add_task(bulk_execution_svc.execute_approved_action, signed_approval)

                # ADR-027: 同步更新 Incident 狀態
                try:
                    sync_service = get_incident_approval_service()
                    await sync_service.on_approval_status_change(
                        approval_id=approval_id_str,
                        new_status="approved",
                    )
                except Exception as sync_err:
                    logger.error(
                        "bulk_incident_sync_failed",
                        approval_id=approval_id_str,
                        error=str(sync_err),
                    )

            # SSE: 發布事件
            event_action = "approved" if execution_triggered else "signed"
            asyncio.create_task(_publish_approval_event(event_action, signed_approval))

        except ValueError:
            results.append(BulkApproveResult(
                approval_id=approval_id_str,
                success=False,
                message="Invalid UUID format",
            ))
            failed += 1
        except Exception as e:
            logger.error(
                "bulk_approve_error",
                approval_id=approval_id_str,
                error=str(e),
            )
            results.append(BulkApproveResult(
                approval_id=approval_id_str,
                success=False,
                message=f"Error: {str(e)}",
            ))
            failed += 1

    logger.info(
        "bulk_approve_completed",
        total=len(request.approval_ids),
        succeeded=succeeded,
        failed=failed,
        skipped=skipped,
        signer_name=request.signer_name,
    )

    return BulkApproveResponse(
        total=len(request.approval_ids),
        succeeded=succeeded,
        failed=failed,
        skipped=skipped,
        results=results,
    )


# =============================================================================
# SSE Event Publishing (Phase 15: Polling → SSE)
# =============================================================================

async def _publish_approval_event(
    action: str,
    approval: ApprovalRequest,
) -> None:
    """
    發布 Approval SSE 事件

    Args:
        action: 事件動作 (created, signed, approved, rejected, executed)
        approval: 授權請求物件
    """
    try:
        pub = await get_publisher()

        event = SSEEvent(
            type=EventType.APPROVAL,
            data={
                "action": action,
                "approval_id": str(approval.id),
                "status": approval.status.value,
                "risk_level": approval.risk_level.value,
                "current_signatures": approval.current_signatures,
                "required_signatures": approval.required_signatures,
                "action_text": approval.action[:100],
            },
        )

        sent_count = await pub.publish(event, topic="approvals")

        if sent_count > 0:
            logger.debug(
                "approval_sse_published",
                action=action,
                approval_id=str(approval.id),
                sent_count=sent_count,
            )

    except Exception as e:
        logger.error(
            "approval_sse_publish_failed",
            action=action,
            approval_id=str(approval.id),
            error=str(e),
        )


# =============================================================================
# GET /api/v1/approvals/stream (SSE)
# =============================================================================

@router.get(
    "/stream",
    summary="SSE 即時授權更新",
    description="Server-Sent Events 即時推送授權狀態變更，取代輪詢機制",
)
async def stream_approvals(request: Request) -> StreamingResponse:
    """
    SSE 即時授權更新 (Phase 15: Polling → SSE)

    事件類型:
    - connected: 連線建立
    - heartbeat: 心跳 (每 15 秒)
    - approval: 授權狀態變更 (created, signed, approved, rejected, executed)

    Client Usage (JavaScript):
    ```javascript
    const es = new EventSource('/api/v1/approvals/stream');
    es.addEventListener('approval', (e) => {
        const data = JSON.parse(e.data);
        console.log('Approval update:', data.action, data.approval_id);
        // Refresh approval list or update specific item
    });
    es.addEventListener('heartbeat', () => {
        console.log('Connection alive');
    });
    ```
    """
    logger.info(
        "approval_stream_connect",
        client_ip=request.client.host if request.client else "unknown",
    )

    pub = await get_publisher()

    # 訂閱 approvals topic
    client = await pub.subscribe(
        topics=["approvals"],
        metadata={"ip": request.client.host if request.client else "unknown"},
    )

    async def event_generator():
        """SSE 事件生成器，含斷線偵測"""
        try:
            async for data in pub.stream(client):
                if await request.is_disconnected():
                    logger.info("approval_stream_disconnected", client_id=client.id)
                    break
                yield data

        except asyncio.CancelledError:
            logger.info("approval_stream_cancelled", client_id=client.id)
            raise

        finally:
            logger.info("approval_stream_cleanup", client_id=client.id)

    return StreamingResponse(
        event_generator(),
        media_type="text/event-stream",
        headers={
            "Cache-Control": "no-cache, no-store, must-revalidate",
            "Connection": "keep-alive",
            "X-Accel-Buffering": "no",
            "Access-Control-Allow-Origin": "*",
        },
    )
