# apps/api/src/services/velero_client.py
# Velero Backup 查詢客戶端 (kubectl 方式，Q7 決策)
# 撰寫: Claude Sonnet 4.6 / 2026-04-08 Asia/Taipei
# 架構: leWOOOgo 積木化，純 Service 層
# 參考: ADR-062

from __future__ import annotations

import asyncio
import json
import time
from datetime import datetime

import structlog

from src.utils.timezone import now_taipei

logger = structlog.get_logger(__name__)

_VELERO_NAMESPACE = "velero"
_KUBECTL_TIMEOUT = 30  # 秒


class VeleroClient:
    """
    透過 kubectl 查詢 Velero 備份狀態
    設計原則: 失敗時 fallback「假設備份過期」（保守原則）
    """

    async def get_latest_backup_age_hours(self) -> float:
        """
        查詢最近一次 Completed 備份距今幾小時
        失敗時返回 999.0（視為嚴重過期，觸發 Abort）
        """
        try:
            result = await asyncio.wait_for(
                self._run_kubectl(
                    ["get", "backup", "-n", _VELERO_NAMESPACE,
                     "-o", "json", "--field-selector", "status.phase=Completed"]
                ),
                timeout=_KUBECTL_TIMEOUT,
            )
            data = json.loads(result)
            items = data.get("items", [])
            if not items:
                logger.warning("velero_no_completed_backups")
                return 999.0

            latest = max(
                items,
                key=lambda x: x.get("status", {}).get("completionTimestamp", ""),
            )
            completion_ts = latest["status"].get("completionTimestamp", "")
            if not completion_ts:
                return 999.0

            completed_at = datetime.fromisoformat(completion_ts.replace("Z", "+00:00"))
            age = (now_taipei() - completed_at).total_seconds() / 3600
            logger.info("velero_backup_age_checked", completion_ts=completion_ts, age_hours=round(age, 1))
            return age

        except asyncio.TimeoutError:
            logger.error("velero_kubectl_timeout")
            return 999.0
        except Exception as e:
            logger.error("velero_query_failed", error=str(e))
            return 999.0

    async def trigger_emergency_backup(self, backup_name: str | None = None) -> bool:
        """
        觸發緊急備份（非同步，不等待完成）
        返回 True 表示指令已成功發送
        """
        # S3-3 修正: kubectl apply Backup CRD（非 kubectl create backup，不存在此子命令）
        # (2026-04-08 審查修正 Claude Sonnet 4.6 Asia/Taipei)
        name = backup_name or f"emergency-{int(time.time())}"
        manifest = (
            f"apiVersion: velero.io/v1\n"
            f"kind: Backup\n"
            f"metadata:\n"
            f"  name: {name}\n"
            f"  namespace: {_VELERO_NAMESPACE}\n"
            f"spec:\n"
            f"  includedNamespaces:\n"
            f"    - awoooi-prod\n"
            f"  ttl: 720h0m0s\n"
        )
        try:
            # kubectl apply -f - (from stdin)
            proc = await asyncio.wait_for(
                asyncio.create_subprocess_exec(
                    "kubectl", "apply", "-f", "-",
                    stdin=asyncio.subprocess.PIPE,
                    stdout=asyncio.subprocess.PIPE,
                    stderr=asyncio.subprocess.PIPE,
                ),
                timeout=_KUBECTL_TIMEOUT,
            )
            stdout, stderr = await proc.communicate(input=manifest.encode())
            if proc.returncode != 0:
                raise RuntimeError(f"kubectl apply 失敗: {stderr.decode()}")
            logger.info("velero_emergency_backup_triggered", backup_name=name)
            return True
        except Exception as e:
            logger.error("velero_emergency_backup_failed", backup_name=name, error=str(e))
            return False

    async def _run_kubectl(self, args: list[str]) -> str:
        proc = await asyncio.create_subprocess_exec(
            "kubectl", *args,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE,
        )
        stdout, stderr = await proc.communicate()
        if proc.returncode != 0:
            raise RuntimeError(f"kubectl 失敗: {stderr.decode()}")
        return stdout.decode()


_velero_client: VeleroClient | None = None


def get_velero_client() -> VeleroClient:
    global _velero_client
    if _velero_client is None:
        _velero_client = VeleroClient()
    return _velero_client


def set_velero_client(client: VeleroClient) -> None:
    """測試注入用 (P4 規範)"""
    global _velero_client
    _velero_client = client
