"""
FinOps Cost Analyzer - 閒置資源掃描與成本換算
Phase 3.3: 商業變現能力 - Day-1 ROI

核心功能:
1. Orphaned PVCs (孤兒儲存卷) - 沒有被任何 Pod 掛載
2. Zombie Pods (殭屍容器) - CPU 使用率連續 7 天 < 1%
3. Over-provisioned Nodes (過度配置節點) - Request 高但 Usage 低

輸出格式:
- total_wasted_usd: 每月浪費金額
- recommended_actions: OpenClaw 可執行的建議清單
"""

import logging
from dataclasses import dataclass, field
from datetime import datetime, timedelta
from enum import Enum
from typing import Literal

from src.utils.timezone import now_taipei

logger = logging.getLogger(__name__)


# ==================== Types ====================


class ResourceType(str, Enum):
    """資源類型"""
    PVC = "pvc"                 # PersistentVolumeClaim
    POD = "pod"                 # Pod
    NODE = "node"               # Node
    DEPLOYMENT = "deployment"   # Deployment
    SERVICE = "service"         # Service


class WasteReason(str, Enum):
    """浪費原因"""
    ORPHANED = "orphaned"               # 孤兒資源 (無連結)
    ZOMBIE = "zombie"                   # 殭屍 (幾乎無活動)
    OVER_PROVISIONED = "over_provisioned"  # 過度配置
    IDLE = "idle"                       # 閒置


@dataclass
class WastedResource:
    """浪費的資源"""
    resource_type: ResourceType
    name: str
    namespace: str
    reason: WasteReason
    details: str
    monthly_cost_usd: float
    created_at: datetime
    last_used_at: datetime | None = None

    # 資源規格
    spec: dict = field(default_factory=dict)

    def to_dict(self) -> dict:
        return {
            "resourceType": self.resource_type.value,
            "name": self.name,
            "namespace": self.namespace,
            "reason": self.reason.value,
            "details": self.details,
            "monthlyCostUsd": round(self.monthly_cost_usd, 2),
            "createdAt": self.created_at.isoformat(),
            "lastUsedAt": self.last_used_at.isoformat() if self.last_used_at else None,
            "spec": self.spec,
        }


class SavingsType(str, Enum):
    """節省類型 - 區分真實省錢 vs 釋放資源"""
    REALIZABLE = "realizable"  # 真實省錢 (例如刪除 PVC → AWS 帳單立刻減少)
    FREED = "freed"            # 釋放資源 (例如刪除 Pod → 除非 Node 縮容否則不省錢)


@dataclass
class RecommendedAction:
    """建議的優化動作 (OpenClaw 可執行)"""
    action_id: str
    action_type: Literal["delete", "scale_down", "resize", "migrate"]
    resource_type: ResourceType
    resource_name: str
    namespace: str
    description: str
    estimated_savings_usd: float
    risk_level: Literal["low", "medium", "high", "critical"]
    command_hint: str  # 給 OpenClaw 的執行提示
    savings_type: SavingsType = SavingsType.REALIZABLE  # 節省類型

    def to_dict(self) -> dict:
        return {
            "actionId": self.action_id,
            "actionType": self.action_type,
            "resourceType": self.resource_type.value,
            "resourceName": self.resource_name,
            "namespace": self.namespace,
            "description": self.description,
            "estimatedSavingsUsd": round(self.estimated_savings_usd, 2),
            "riskLevel": self.risk_level,
            "commandHint": self.command_hint,
            "savingsType": self.savings_type.value,
        }


@dataclass
class CostReport:
    """成本報告 (OpenClaw 整合用)"""
    scan_id: str
    scanned_at: datetime
    cluster_name: str

    # 核心指標
    total_wasted_usd: float
    total_resources_scanned: int
    wasted_resources_count: int

    # 詳細資料
    wasted_resources: list[WastedResource]
    recommended_actions: list[RecommendedAction]

    # 分類統計
    waste_by_type: dict[str, float]
    waste_by_namespace: dict[str, float]

    def to_dict(self) -> dict:
        """輸出 OpenClaw 可讀取的 JSON 格式"""
        return {
            "scanId": self.scan_id,
            "scannedAt": self.scanned_at.isoformat(),
            "clusterName": self.cluster_name,

            # OpenClaw 核心關注
            "totalWastedUsd": round(self.total_wasted_usd, 2),
            "totalResourcesScanned": self.total_resources_scanned,
            "wastedResourcesCount": self.wasted_resources_count,

            # 詳細資料
            "wastedResources": [r.to_dict() for r in self.wasted_resources],
            "recommendedActions": [a.to_dict() for a in self.recommended_actions],

            # 統計
            "wasteByType": {k: round(v, 2) for k, v in self.waste_by_type.items()},
            "wasteByNamespace": {k: round(v, 2) for k, v in self.waste_by_namespace.items()},

            # 摘要 (給 AI 的自然語言描述)
            "summary": self._generate_summary(),
        }

    def _generate_summary(self) -> str:
        """產生 AI 可讀的摘要"""
        if self.total_wasted_usd < 10:
            return f"Cluster {self.cluster_name} is well-optimized. Only ${self.total_wasted_usd:.2f}/month potential savings."

        top_waste = max(self.waste_by_type.items(), key=lambda x: x[1]) if self.waste_by_type else ("none", 0)
        return (
            f"Cluster {self.cluster_name} has ${self.total_wasted_usd:.2f}/month in wasted resources. "
            f"Found {self.wasted_resources_count} idle resources. "
            f"Biggest waste: {top_waste[0]} (${top_waste[1]:.2f}/month). "
            f"{len(self.recommended_actions)} optimization actions available."
        )


# ==================== Pricing Configuration ====================


@dataclass
class PricingConfig:
    """
    費率配置 (可依雲端供應商調整)

    預設值基於 AWS 美東區域 (us-east-1)
    """
    # 儲存 (per GB/month)
    storage_gp3_per_gb: float = 0.08      # EBS gp3
    storage_gp2_per_gb: float = 0.10      # EBS gp2
    storage_io1_per_gb: float = 0.125     # EBS io1
    storage_standard_per_gb: float = 0.05 # Standard HDD

    # 運算 (per vCPU/month, 假設 on-demand)
    compute_per_vcpu: float = 30.0        # ~$0.04/hr * 720hr
    compute_per_gb_ram: float = 4.0       # ~$0.005/hr/GB * 720hr

    # 網路
    load_balancer_per_month: float = 18.0 # ALB/NLB 固定費
    nat_gateway_per_month: float = 32.0   # NAT Gateway

    # ╔════════════════════════════════════════════════════════════════╗
    # ║  SAFETY_BUFFER: 縮容安全係數                                    ║
    # ║  避免建議縮到剛好 actual usage，造成 OOM/CPU throttling         ║
    # ║  公式: wasted = requested - (actual × 1.2)                     ║
    # ╚════════════════════════════════════════════════════════════════╝
    safety_buffer: float = 1.2

    def get_storage_price(self, storage_class: str) -> float:
        """依 StorageClass 取得費率"""
        mapping = {
            "gp3": self.storage_gp3_per_gb,
            "gp2": self.storage_gp2_per_gb,
            "io1": self.storage_io1_per_gb,
            "standard": self.storage_standard_per_gb,
        }
        return mapping.get(storage_class.lower(), self.storage_gp3_per_gb)


# 預設費率
DEFAULT_PRICING = PricingConfig()


# ==================== Idle Resource Scanner ====================


class IdleResourceScanner:
    """
    閒置資源掃描器

    偵測並量化 K8s 叢集中的浪費資源，
    轉換為美金金額，供 OpenClaw 決策
    """

    def __init__(self, pricing: PricingConfig | None = None):
        self.pricing = pricing or DEFAULT_PRICING
        self._scan_counter = 0

    async def full_scan(self, cluster_name: str = "default") -> CostReport:
        """
        執行完整掃描

        Returns:
            CostReport 包含所有浪費資源與建議動作
        """
        self._scan_counter += 1
        scan_id = f"scan-{self._scan_counter:04d}-{now_taipei().strftime('%Y%m%d%H%M%S')}"

        logger.info(f"[FinOps] Starting full scan: {scan_id}")

        # 執行各類掃描
        orphaned_pvcs = await self._scan_orphaned_pvcs()
        zombie_pods = await self._scan_zombie_pods()
        over_provisioned = await self._scan_over_provisioned_nodes()

        # 合併所有浪費資源
        all_wasted = orphaned_pvcs + zombie_pods + over_provisioned

        # 產生建議動作
        actions = self._generate_recommendations(all_wasted)

        # 計算統計
        total_wasted = sum(r.monthly_cost_usd for r in all_wasted)
        waste_by_type = self._group_by_type(all_wasted)
        waste_by_ns = self._group_by_namespace(all_wasted)

        report = CostReport(
            scan_id=scan_id,
            scanned_at=now_taipei(),
            cluster_name=cluster_name,
            total_wasted_usd=total_wasted,
            total_resources_scanned=self._get_mock_total_resources(),
            wasted_resources_count=len(all_wasted),
            wasted_resources=all_wasted,
            recommended_actions=actions,
            waste_by_type=waste_by_type,
            waste_by_namespace=waste_by_ns,
        )

        logger.info(
            f"[FinOps] Scan complete: {scan_id} - "
            f"${total_wasted:.2f}/month wasted, {len(actions)} actions"
        )

        return report

    # ==================== Orphaned PVCs ====================

    async def _scan_orphaned_pvcs(self) -> list[WastedResource]:
        """
        掃描孤兒 PVC

        孤兒 PVC = 已建立但沒有被任何 Pod 掛載的 PersistentVolumeClaim
        常見原因: Pod 刪除後忘記清理 PVC
        """
        # Phase 3: Mock 資料 (實際連接 K8s API 待 Phase 4)
        mock_orphans = [
            {
                "name": "data-postgres-backup-old",
                "namespace": "database",
                "size_gb": 500,
                "storage_class": "gp3",
                "created": now_taipei() - timedelta(days=90),
                "last_used": now_taipei() - timedelta(days=60),
            },
            {
                "name": "logs-elasticsearch-2023",
                "namespace": "logging",
                "size_gb": 200,
                "storage_class": "gp2",
                "created": now_taipei() - timedelta(days=180),
                "last_used": now_taipei() - timedelta(days=120),
            },
            {
                "name": "cache-redis-temp",
                "namespace": "default",
                "size_gb": 50,
                "storage_class": "gp3",
                "created": now_taipei() - timedelta(days=30),
                "last_used": None,
            },
        ]

        results = []
        for pvc in mock_orphans:
            price_per_gb = self.pricing.get_storage_price(pvc["storage_class"])
            monthly_cost = pvc["size_gb"] * price_per_gb

            results.append(WastedResource(
                resource_type=ResourceType.PVC,
                name=pvc["name"],
                namespace=pvc["namespace"],
                reason=WasteReason.ORPHANED,
                details=f"PVC not mounted by any Pod. Size: {pvc['size_gb']}GB ({pvc['storage_class']})",
                monthly_cost_usd=monthly_cost,
                created_at=pvc["created"],
                last_used_at=pvc["last_used"],
                spec={
                    "sizeGb": pvc["size_gb"],
                    "storageClass": pvc["storage_class"],
                },
            ))

        logger.info(f"[FinOps] Found {len(results)} orphaned PVCs")
        return results

    # ==================== Zombie Pods ====================

    async def _scan_zombie_pods(self) -> list[WastedResource]:
        """
        掃描殭屍 Pod

        殭屍 Pod = CPU 使用率連續 7 天 < 1% 的 Pod
        常見原因: 被遺忘的測試 Pod、已下線但未刪除的服務
        """
        mock_zombies = [
            {
                "name": "legacy-api-5d7b8c9f6-abc12",
                "namespace": "legacy",
                "cpu_request": 2.0,  # vCPU
                "mem_request_gb": 4.0,
                "avg_cpu_percent": 0.3,
                "created": now_taipei() - timedelta(days=120),
                "last_active": now_taipei() - timedelta(days=45),
            },
            {
                "name": "test-worker-batch-xyz99",
                "namespace": "testing",
                "cpu_request": 1.0,
                "mem_request_gb": 2.0,
                "avg_cpu_percent": 0.1,
                "created": now_taipei() - timedelta(days=60),
                "last_active": now_taipei() - timedelta(days=30),
            },
            {
                "name": "debug-shell-admin",
                "namespace": "default",
                "cpu_request": 0.5,
                "mem_request_gb": 1.0,
                "avg_cpu_percent": 0.0,
                "created": now_taipei() - timedelta(days=14),
                "last_active": now_taipei() - timedelta(days=10),
            },
        ]

        results = []
        for pod in mock_zombies:
            # 計算成本: CPU + Memory
            cpu_cost = pod["cpu_request"] * self.pricing.compute_per_vcpu
            mem_cost = pod["mem_request_gb"] * self.pricing.compute_per_gb_ram
            monthly_cost = cpu_cost + mem_cost

            results.append(WastedResource(
                resource_type=ResourceType.POD,
                name=pod["name"],
                namespace=pod["namespace"],
                reason=WasteReason.ZOMBIE,
                details=(
                    f"CPU usage < 1% for 7+ days. "
                    f"Avg: {pod['avg_cpu_percent']:.1f}%. "
                    f"Resources: {pod['cpu_request']} vCPU, {pod['mem_request_gb']}GB RAM"
                ),
                monthly_cost_usd=monthly_cost,
                created_at=pod["created"],
                last_used_at=pod["last_active"],
                spec={
                    "cpuRequest": pod["cpu_request"],
                    "memoryGb": pod["mem_request_gb"],
                    "avgCpuPercent": pod["avg_cpu_percent"],
                },
            ))

        logger.info(f"[FinOps] Found {len(results)} zombie Pods")
        return results

    # ==================== Over-provisioned Nodes ====================

    async def _scan_over_provisioned_nodes(self) -> list[WastedResource]:
        """
        掃描過度配置節點

        過度配置 = Request 很高但實際 Usage 很低
        例如: Request 8 vCPU 但只用 1 vCPU
        """
        mock_nodes = [
            {
                "name": "worker-large-01",
                "namespace": "kube-system",
                "total_cpu": 16.0,
                "total_mem_gb": 64.0,
                "requested_cpu": 12.0,
                "requested_mem_gb": 48.0,
                "actual_cpu": 2.0,
                "actual_mem_gb": 8.0,
                "created": now_taipei() - timedelta(days=200),
            },
            {
                "name": "worker-gpu-unused",
                "namespace": "kube-system",
                "total_cpu": 8.0,
                "total_mem_gb": 32.0,
                "requested_cpu": 4.0,
                "requested_mem_gb": 16.0,
                "actual_cpu": 0.5,
                "actual_mem_gb": 2.0,
                "created": now_taipei() - timedelta(days=90),
            },
        ]

        results = []
        for node in mock_nodes:
            # ╔════════════════════════════════════════════════════════════════╗
            # ║  安全緩衝計算: wasted = requested - (actual × SAFETY_BUFFER)   ║
            # ║  避免縮容建議導致 OOM / CPU throttling                         ║
            # ╚════════════════════════════════════════════════════════════════╝
            buffered_cpu = node["actual_cpu"] * self.pricing.safety_buffer
            buffered_mem = node["actual_mem_gb"] * self.pricing.safety_buffer

            wasted_cpu = node["requested_cpu"] - buffered_cpu
            wasted_mem = node["requested_mem_gb"] - buffered_mem

            if wasted_cpu < 1 and wasted_mem < 4:
                continue  # 浪費不夠顯著 (含安全緩衝後)

            cpu_waste_cost = wasted_cpu * self.pricing.compute_per_vcpu
            mem_waste_cost = wasted_mem * self.pricing.compute_per_gb_ram
            monthly_cost = cpu_waste_cost + mem_waste_cost

            utilization = node["actual_cpu"] / node["requested_cpu"] * 100

            results.append(WastedResource(
                resource_type=ResourceType.NODE,
                name=node["name"],
                namespace=node["namespace"],
                reason=WasteReason.OVER_PROVISIONED,
                details=(
                    f"Utilization: {utilization:.0f}%. "
                    f"Requested: {node['requested_cpu']} vCPU, {node['requested_mem_gb']}GB. "
                    f"Actual: {node['actual_cpu']} vCPU, {node['actual_mem_gb']}GB"
                ),
                monthly_cost_usd=monthly_cost,
                created_at=node["created"],
                last_used_at=now_taipei(),
                spec={
                    "totalCpu": node["total_cpu"],
                    "totalMemoryGb": node["total_mem_gb"],
                    "requestedCpu": node["requested_cpu"],
                    "requestedMemoryGb": node["requested_mem_gb"],
                    "actualCpu": node["actual_cpu"],
                    "actualMemoryGb": node["actual_mem_gb"],
                    "utilizationPercent": utilization,
                },
            ))

        logger.info(f"[FinOps] Found {len(results)} over-provisioned resources")
        return results

    # ==================== Recommendations ====================

    def _generate_recommendations(
        self,
        wasted: list[WastedResource],
    ) -> list[RecommendedAction]:
        """
        產生優化建議 (OpenClaw 可執行)
        """
        actions = []
        action_counter = 0

        for resource in wasted:
            action_counter += 1
            action_id = f"action-{action_counter:03d}"

            if resource.resource_type == ResourceType.PVC:
                # ✅ REALIZABLE: 刪除 PVC → AWS 帳單立刻減少
                actions.append(RecommendedAction(
                    action_id=action_id,
                    action_type="delete",
                    resource_type=resource.resource_type,
                    resource_name=resource.name,
                    namespace=resource.namespace,
                    description=f"Delete orphaned PVC '{resource.name}' - not mounted by any Pod",
                    estimated_savings_usd=resource.monthly_cost_usd,
                    risk_level="low",
                    command_hint=f"kubectl delete pvc {resource.name} -n {resource.namespace}",
                    savings_type=SavingsType.REALIZABLE,
                ))

            elif resource.resource_type == ResourceType.POD:
                # ⚠️ FREED: 刪除 Pod 只是釋放資源，除非 Node 縮容否則不省錢
                risk = "medium" if resource.monthly_cost_usd > 50 else "low"
                actions.append(RecommendedAction(
                    action_id=action_id,
                    action_type="delete",
                    resource_type=resource.resource_type,
                    resource_name=resource.name,
                    namespace=resource.namespace,
                    description=f"Delete zombie Pod '{resource.name}' - CPU < 1% for 7+ days",
                    estimated_savings_usd=resource.monthly_cost_usd,
                    risk_level=risk,
                    command_hint=f"kubectl delete pod {resource.name} -n {resource.namespace}",
                    savings_type=SavingsType.FREED,
                ))

            elif resource.resource_type == ResourceType.NODE:
                # ✅ REALIZABLE: Node 縮容/刪除 → AWS 帳單減少
                actions.append(RecommendedAction(
                    action_id=action_id,
                    action_type="resize",
                    resource_type=resource.resource_type,
                    resource_name=resource.name,
                    namespace=resource.namespace,
                    description=(
                        f"Resize node '{resource.name}' - "
                        f"utilization only {resource.spec.get('utilizationPercent', 0):.0f}%"
                    ),
                    estimated_savings_usd=resource.monthly_cost_usd,
                    risk_level="high",
                    command_hint=f"# Consider migrating workloads and downsizing {resource.name}",
                    savings_type=SavingsType.REALIZABLE,
                ))

        # 按節省金額排序 (最大節省優先)
        actions.sort(key=lambda a: a.estimated_savings_usd, reverse=True)

        return actions

    # ==================== Utilities ====================

    def _group_by_type(self, resources: list[WastedResource]) -> dict[str, float]:
        """依類型分組統計"""
        result: dict[str, float] = {}
        for r in resources:
            key = r.resource_type.value
            result[key] = result.get(key, 0) + r.monthly_cost_usd
        return result

    def _group_by_namespace(self, resources: list[WastedResource]) -> dict[str, float]:
        """依 Namespace 分組統計"""
        result: dict[str, float] = {}
        for r in resources:
            result[r.namespace] = result.get(r.namespace, 0) + r.monthly_cost_usd
        return result

    def _get_mock_total_resources(self) -> int:
        """Mock: 總掃描資源數"""
        return 150  # 假設叢集有 150 個資源

    def calculate_monthly_savings(self, report: CostReport) -> dict:
        """
        計算月度節省摘要

        ╔════════════════════════════════════════════════════════════════╗
        ║  嚴格區分真實省錢 vs 釋放資源                                   ║
        ║  - realizableSavingsUsd: 刪除後 AWS 帳單立刻減少               ║
        ║  - freedResourcesUsd: 釋放 Pod/Container，需要 Node 縮容才省錢 ║
        ╚════════════════════════════════════════════════════════════════╝

        Returns:
            OpenClaw 可直接使用的 JSON 格式
        """
        realizable = sum(
            a.estimated_savings_usd
            for a in report.recommended_actions
            if a.savings_type == SavingsType.REALIZABLE
        )
        freed = sum(
            a.estimated_savings_usd
            for a in report.recommended_actions
            if a.savings_type == SavingsType.FREED
        )

        return {
            "totalWastedUsd": round(report.total_wasted_usd, 2),

            # ⚠️ 嚴格區分
            "realizableSavingsUsd": round(realizable, 2),  # 真實省錢
            "freedResourcesUsd": round(freed, 2),           # 釋放資源 (需縮容才省錢)

            "potentialSavingsUsd": round(realizable + freed, 2),  # 總計 (參考用)
            "actionCount": len(report.recommended_actions),
            "topActions": [
                {
                    "action": a.description,
                    "savings": round(a.estimated_savings_usd, 2),
                    "risk": a.risk_level,
                    "savingsType": a.savings_type.value,
                }
                for a in report.recommended_actions[:5]  # Top 5
            ],
            "annualProjection": round(realizable * 12, 2),  # 年度預估僅計真實省錢
            "annualProjectionWithFreed": round((realizable + freed) * 12, 2),
        }


# 全域實例
idle_scanner = IdleResourceScanner()
