import json
from pathlib import Path
from typing import Dict, List

# edited by glg


class FleetRolloutGateService:
    @staticmethod
    def _to_float(value, default=0.0) -> float:
        try:
            return float(value)
        except (TypeError, ValueError, KeyError, AttributeError, RuntimeError, OSError, LookupError, ArithmeticError, ImportError):
            return float(default)

    @staticmethod
    def _to_non_negative_float(value, default=0.0) -> float:
        return max(0.0, FleetRolloutGateService._to_float(value, default=default))

    @staticmethod
    def _collect_records(payload) -> List[Dict]:
        if isinstance(payload, list):
            return [item for item in payload if isinstance(item, dict)]
        if not isinstance(payload, dict):
            return []

        for key in ("metrics", "records", "items", "branches"):
            val = payload.get(key)
            if isinstance(val, list):
                return [item for item in val if isinstance(item, dict)]

        if "error_rate_pct" in payload or "p95_latency_ms" in payload:
            return [payload]
        return []

    @staticmethod
    def _aggregate_records(records: List[Dict]) -> Dict:
        normalized = [item for item in (records or []) if isinstance(item, dict)]
        if not normalized:
            return {
                "sample_count": 0,
                "error_rate_pct": 0.0,
                "p95_latency_ms": 0.0,
            }
        error_values = [
            FleetRolloutGateService._to_non_negative_float(item.get("error_rate_pct"), default=0.0)
            for item in normalized
        ]
        p95_values = [
            FleetRolloutGateService._to_non_negative_float(item.get("p95_latency_ms"), default=0.0)
            for item in normalized
        ]
        return {
            "sample_count": int(len(normalized)),
            "error_rate_pct": float(sum(error_values) / max(1, len(error_values))),
            "p95_latency_ms": float(max(p95_values) if p95_values else 0.0),
        }

    def evaluate_metrics(
        self,
        payload,
        fail_threshold_pct: float = 2.0,
        latency_threshold_ms: float = 2000.0,
    ) -> Dict:
        records = self._collect_records(payload)
        aggregate = self._aggregate_records(records)
        err = float(aggregate.get("error_rate_pct") or 0.0)
        p95 = float(aggregate.get("p95_latency_ms") or 0.0)
        fail_thr = max(0.1, self._to_non_negative_float(fail_threshold_pct, default=2.0))
        lat_thr = max(100.0, self._to_non_negative_float(latency_threshold_ms, default=2000.0))
        healthy = bool(err <= fail_thr and p95 <= lat_thr)

        source = payload if isinstance(payload, dict) else {}
        return {
            "healthy": bool(healthy),
            "decision": "continue" if healthy else "halt_and_rollback",
            "error_rate_pct": err,
            "p95_latency_ms": p95,
            "fail_threshold_pct": fail_thr,
            "latency_threshold_ms": lat_thr,
            "sample_count": int(aggregate.get("sample_count") or 0),
            "wave": str(source.get("wave") or source.get("wave_name") or "").strip(),
            "config_version": str(source.get("config_version") or "").strip(),
            "reason": "healthy" if healthy else "threshold_exceeded",
        }

    def evaluate_metrics_file(
        self,
        metrics_file: str,
        fail_threshold_pct: float = 2.0,
        latency_threshold_ms: float = 2000.0,
    ) -> Dict:
        path = Path(str(metrics_file or "")).expanduser().resolve()
        if not path.exists() or not path.is_file():
            raise FileNotFoundError(f"Metrics file tidak ditemukan: {path}")
        payload = json.loads(path.read_text(encoding="utf-8"))
        result = self.evaluate_metrics(
            payload,
            fail_threshold_pct=fail_threshold_pct,
            latency_threshold_ms=latency_threshold_ms,
        )
        result["metrics_file"] = str(path)
        return result

