"""
Telegram Gateway API - OpenClaw 行動簽核通道
=============================================
Phase 5.4: Telegram Gateway 整合
Phase 5.5: Long Polling 重構 (內網修復)

架構變更 (2026-03-22):
- 舊: Webhook 模式 (需外網可達) - 已廢除
- 新: Long Polling 模式 (主動輪詢) - 適用內網環境

Endpoints:
- POST /api/v1/telegram/webhook - [已棄用] 接收 Telegram Bot Update
- POST /api/v1/telegram/test-push - 測試推送 (僅開發模式)
- GET /api/v1/telegram/health - Gateway 健康檢查

安全鐵律:
- 所有簽核必須通過 SecurityInterceptor 驗證
- 只有白名單內的 user_id 可以簽核
- 每個 Nonce 只能使用一次
"""

import asyncio
from uuid import UUID

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

from src.core.config import settings
from src.core.logging import get_logger
from src.services.approval_action_classifier import is_no_action_approval_action
from src.services.approval_db import get_approval_service
from src.services.approval_execution import get_execution_service
from src.services.incident_approval_service import get_incident_approval_service
from src.services.security_interceptor import (
    NonceReplayError,
    UserNotWhitelistedError,
)
from src.services.telegram_gateway import TelegramGatewayError, get_telegram_gateway

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


# =============================================================================
# Request Models
# =============================================================================

class TelegramUpdate(BaseModel):
    """
    Telegram Bot API Update

    簡化版本，僅處理 callback_query (簽核按鈕點擊)
    """
    update_id: int
    callback_query: dict | None = None
    message: dict | None = None


class TestPushRequest(BaseModel):
    """測試推送請求 (僅開發模式)"""
    approval_id: str
    risk_level: str = "medium"
    resource_name: str = "test-pod-123"
    root_cause: str = "Test alert for development"
    suggested_action: str = "DELETE_POD"
    estimated_downtime: str = "~30s"
    # 2026-04-05 Claude Code: 支援 incident_id 以測試第二排按鈕渲染
    incident_id: str = ""


async def _run_telegram_approved_execution(approval) -> None:
    """Run the approved action that originated from a Telegram callback."""
    approval_id = str(getattr(approval, "id", ""))
    incident_id = getattr(approval, "incident_id", None)
    try:
        result = await get_execution_service().execute_approved_action(approval)
        logger.info(
            "telegram_approval_execution_completed",
            approval_id=approval_id,
            incident_id=incident_id,
            success=bool(result),
        )
    except Exception as exc:
        logger.error(
            "telegram_approval_execution_failed",
            approval_id=approval_id,
            incident_id=incident_id,
            error=str(exc),
        )


def _schedule_telegram_approved_execution(approval) -> bool:
    """Schedule execution after Telegram approval reaches required signatures."""
    try:
        asyncio.create_task(_run_telegram_approved_execution(approval))
        logger.info(
            "telegram_approval_execution_scheduled",
            approval_id=str(getattr(approval, "id", "")),
            incident_id=getattr(approval, "incident_id", None),
        )
        return True
    except Exception as exc:
        logger.error(
            "telegram_approval_execution_schedule_failed",
            approval_id=str(getattr(approval, "id", "")),
            incident_id=getattr(approval, "incident_id", None),
            error=str(exc),
        )
        return False


async def _finalize_telegram_approval(approval, execution_triggered: bool) -> bool:
    """Complete the execution handoff for Telegram approvals.

    ApprovalDBService only records the signature/status transition. The actual
    executor scheduling lives in API callers, so Telegram must mirror the REST
    approval endpoint instead of stopping at a visual approval stamp.
    """
    if not execution_triggered:
        return False
    approval_action = getattr(approval, "action", None)
    if approval_action is not None and is_no_action_approval_action(approval_action):
        logger.warning(
            "telegram_approval_execution_suppressed_no_repair_action",
            approval_id=str(getattr(approval, "id", "")),
            incident_id=getattr(approval, "incident_id", None),
            action=str(approval_action)[:200],
        )
        return False
    return _schedule_telegram_approved_execution(approval)


async def _sync_telegram_rejection(approval_id: str) -> bool:
    """Keep Incident state aligned when an approval is rejected from Telegram."""
    try:
        await get_incident_approval_service().on_approval_status_change(
            approval_id=approval_id,
            new_status="rejected",
        )
        logger.info(
            "telegram_rejection_incident_synced",
            approval_id=approval_id,
        )
        return True
    except Exception as exc:
        logger.error(
            "telegram_rejection_incident_sync_failed",
            approval_id=approval_id,
            error=str(exc),
        )
        return False


# =============================================================================
# Endpoints
# =============================================================================

@router.post(
    "/webhook",
    summary="[已棄用] Telegram Bot Webhook",
    description="⚠️ 已棄用：內網環境請使用 Long Polling 模式。此端點保留供外網環境或測試使用。",
    deprecated=True,
)
async def telegram_webhook(
    update: TelegramUpdate,
) -> dict:
    """
    接收 Telegram Bot Update

    處理流程:
    1. 驗證 Update 類型 (僅處理 callback_query)
    2. 安全驗證 (白名單 + Nonce)
    3. 解析簽核動作 (approve/reject)
    4. 更新資料庫
    5. 回應 Telegram
    """
    logger.info("telegram_webhook_received", update_id=update.update_id)

    # =========================================================================
    # Step 1: 路由 Update 類型
    # =========================================================================
    # Phase 34 (ADR-067 2026-04-10): 圖片訊息 → image_analysis_service
    if not update.callback_query and update.message:
        msg = update.message
        if msg.get("photo"):
            # 取最高解析度 (photos 陣列最後一個)
            photos = msg["photo"]
            best = max(photos, key=lambda p: p.get("file_size", 0))
            file_id = best.get("file_id", "")
            chat_id = str(msg.get("chat", {}).get("id", ""))
            caption = msg.get("caption", "請用繁體中文描述這張圖片")
            if file_id and chat_id:
                try:
                    from src.services.image_analysis_service import get_image_analysis_service
                    svc = get_image_analysis_service()
                    # download_and_analyze 內部自行下載 + 分析 + 發送 Telegram
                    await svc.download_and_analyze(
                        chat_id=chat_id,
                        file_id=file_id,
                        question=caption,
                    )
                except Exception as _img_err:
                    logger.warning("image_analysis_webhook_failed", error=str(_img_err))
            return {"ok": True, "message": "photo_processed"}

    if not update.callback_query:
        logger.debug("telegram_webhook_ignored", reason="not callback_query")
        return {"ok": True, "message": "Ignored (not callback_query)"}

    callback = update.callback_query
    callback_query_id = callback.get("id")
    callback_data = callback.get("data")
    user = callback.get("from", {})
    user_id = user.get("id")
    username = user.get("username") or user.get("first_name") or str(user_id)
    message = callback.get("message", {})
    message_id = message.get("message_id")
    original_text = message.get("text", "")

    if not all([callback_query_id, callback_data, user_id]):
        logger.warning("telegram_webhook_invalid", reason="missing required fields")
        return {"ok": False, "message": "Invalid callback data"}

    # =========================================================================
    # Step 2: 安全驗證 + 處理回調
    # =========================================================================
    try:
        gateway = get_telegram_gateway()
        mirror_callback = getattr(gateway, "mirror_callback_query_received", None)
        if callable(mirror_callback):
            await mirror_callback(
                update_id=update.update_id,
                callback_query_id=callback_query_id,
                callback_data=callback_data,
                user_id=user_id,
                username=username,
                message_id=message_id,
                chat_id=message.get("chat", {}).get("id"),
            )
        result = await gateway.handle_callback(
            callback_query_id=callback_query_id,
            callback_data=callback_data,
            user_id=user_id,
            message_id=message_id,
            original_text=original_text,
            username=username,
        )

        if not result.get("success"):
            return {"ok": False, "message": result.get("error")}

        # =====================================================================
        # Step 2.5: ADR-050 Info Actions (read-only, 無需 DB 操作)
        # =====================================================================
        if result.get("info_action"):
            return {"ok": True, "message": f"info:{result['action']}", "approval_id": result["approval_id"]}

        # =====================================================================
        # Step 3: 更新資料庫 (簽核/拒絕)
        # =====================================================================
        action = result["action"]
        approval_id = result["approval_id"]
        _telegram_user = result["user"]  # reserved for future audit logging

        service = get_approval_service()

        # 2026-04-08 Claude Code: USER_ACTION 記錄
        async def _log_user_action(action_name: str, success: bool, incident_id: str | None = None) -> None:
            try:
                from src.repositories.alert_operation_log_repository import get_alert_operation_log_repository
                await get_alert_operation_log_repository().append(
                    "USER_ACTION",
                    incident_id=incident_id,
                    approval_id=approval_id,
                    actor=f"telegram:{user_id}",
                    action_detail=action_name,
                    success=success,
                    context={
                        "username": username,
                        "user_id": user_id,
                        "message_id": message_id,
                        "action": action_name,
                    },
                )
            except Exception as _e:
                logger.warning("alert_op_log_user_action_failed", error=str(_e))

        # 2026-03-29 ogt: 修復方法呼叫 - add_signature/reject 不存在
        # 正確方法: sign_approval / reject_approval
        if action == "approve":
            approval, msg, execution_triggered = await service.sign_approval(
                approval_id=UUID(approval_id),
                signer_id=f"tg_{user_id}",
                signer_name=user.get("username") or user.get("first_name") or str(user_id),
                comment="Telegram 簽核",
            )

            if approval:
                status_value = approval.status.value if hasattr(approval.status, "value") else str(approval.status)
                if (
                    "Cannot sign" in msg
                    or "already signed" in msg
                    or "Concurrent modification" in msg
                ):
                    logger.info(
                        "telegram_approval_ignored_already_processed",
                        approval_id=approval_id,
                        user_id=user_id,
                        status=status_value,
                        message=msg,
                    )
                    await _log_user_action("approve_duplicate", False, getattr(approval, "incident_id", None))
                    return {
                        "ok": True,
                        "message": "Already processed",
                        "approval_id": approval_id,
                        "status": status_value,
                        "execution_triggered": False,
                        "execution_scheduled": False,
                    }

                execution_scheduled = await _finalize_telegram_approval(
                    approval=approval,
                    execution_triggered=execution_triggered,
                )
                approval_action = getattr(approval, "action", None)
                execution_suppressed = bool(
                    execution_triggered
                    and approval_action is not None
                    and is_no_action_approval_action(approval_action)
                )
                logger.info(
                    "telegram_approval_signed",
                    approval_id=approval_id,
                    user_id=user_id,
                    status=status_value,
                    execution_triggered=execution_triggered,
                    execution_scheduled=execution_scheduled,
                    execution_suppressed=execution_suppressed,
                )
                await _log_user_action("approve", True, getattr(approval, "incident_id", None))

                return {
                    "ok": True,
                    "message": (
                        "ApprovedWithoutExecution"
                        if execution_suppressed
                        else ("Approved" if execution_triggered else "Signed")
                    ),
                    "approval_id": approval_id,
                    "status": status_value,
                    "execution_triggered": execution_triggered,
                    "execution_scheduled": execution_scheduled,
                    "execution_suppressed": execution_suppressed,
                }

        elif action == "reject":
            approval, msg = await service.reject_approval(
                approval_id=UUID(approval_id),
                rejector_id=f"tg_{user_id}",
                rejector_name=user.get("username") or str(user_id),
                reason="Telegram 拒絕",
            )

            if approval:
                incident_synced = await _sync_telegram_rejection(approval_id)
                logger.info(
                    "telegram_approval_rejected",
                    approval_id=approval_id,
                    user_id=user_id,
                    incident_synced=incident_synced,
                )
                await _log_user_action("reject", False, getattr(approval, "incident_id", None))

                return {
                    "ok": True,
                    "message": "Rejected",
                    "approval_id": approval_id,
                    "status": approval.status.value,
                    "incident_synced": incident_synced,
                }

        return {"ok": False, "message": "Unknown action"}

    except UserNotWhitelistedError as e:
        logger.warning("telegram_webhook_denied", user_id=user_id, error=str(e))
        return {"ok": False, "message": "User not authorized"}

    except NonceReplayError as e:
        logger.warning("telegram_webhook_replay", error=str(e))
        return {"ok": False, "message": "Already processed"}

    except Exception as e:
        logger.error("telegram_webhook_error", error=str(e))
        return {"ok": False, "message": str(e)}


@router.post(
    "/test-push",
    summary="測試推送 (僅開發模式)",
    description="測試推送簽核卡片到 Telegram (僅在 dev 環境可用)",
)
async def test_push(
    request: TestPushRequest,
) -> dict:
    """
    測試推送簽核卡片到 Telegram

    僅在開發模式下可用
    """
    # 生產環境禁止
    if settings.ENVIRONMENT == "prod":
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Test push is disabled in production",
        )

    try:
        gateway = get_telegram_gateway()

        result = await gateway.send_approval_card(
            approval_id=request.approval_id,
            risk_level=request.risk_level,
            resource_name=request.resource_name,
            root_cause=request.root_cause,
            suggested_action=request.suggested_action,
            estimated_downtime=request.estimated_downtime,
            incident_id=request.incident_id,
        )

        return {
            "ok": True,
            "message": "Test push sent",
            "telegram_response": result,
        }

    except TelegramGatewayError as e:
        raise HTTPException(
            status_code=status.HTTP_502_BAD_GATEWAY,
            detail=f"Telegram API error: {str(e)}",
        ) from e


@router.get(
    "/health",
    summary="Telegram Gateway 健康檢查",
)
async def telegram_health() -> dict:
    """Telegram Gateway 健康狀態 (含 Long Polling 狀態)"""
    gateway = get_telegram_gateway()

    return {
        "status": "configured" if settings.OPENCLAW_TG_BOT_TOKEN else "not_configured",
        "mode": "long_polling",  # Phase 5.5: 已從 webhook 切換至 long_polling
        "polling_active": gateway._polling_active,
        "bot_token_set": bool(settings.OPENCLAW_TG_BOT_TOKEN),
        "chat_id_set": bool(settings.SRE_GROUP_CHAT_ID),
        "sre_group_chat_id_set": bool(settings.SRE_GROUP_CHAT_ID),
        "whitelist_count": len(settings.OPENCLAW_TG_USER_WHITELIST),
        "last_update_id": gateway._last_update_id,
        "environment": settings.ENVIRONMENT,
    }
