from __future__ import annotations

from typing import Any

from .cpu_runtime import CpuVisionRuntime, RuntimeOutcome
from .schemas import InferenceResult, Resource


SCENE_MAPPING = {
    "违法开挖": ("挖机", "vision-detector", 0.90),
    "深基坑隐患": ("基坑", "vision-segmenter", 0.82),
    "违规堆土": ("堆土", "vision-segmenter", 0.81),
    "塔吊": ("塔吊", "change-detector", 0.91),
    "烟火": ("烟火", "video-analyzer", 0.86),
    "危树侵限": ("危树", "vision-segmenter", 0.80),
    "异物漂浮": ("施工防尘网", "vision-detector", 0.84),
    "接触网异物（高危）": ("塑料薄膜", "vision-detector", 0.89),
    "接触网异物": ("塑料薄膜", "vision-detector", 0.89),
    "广告牌隐患": ("广告牌破损", "vision-detector", 0.78),
    "彩钢板清理": ("彩钢板", "change-detector", 0.87),
    "落石": ("落石", "vision-segmenter", 0.83),
    "滑坡预警": ("滑坡", "vision-segmenter", 0.82),
    "裂缝识别": ("裂缝", "vision-segmenter", 0.76),
    "水毁核查": ("路基漫水", "vision-segmenter", 0.79),
    "沟渠疏通": ("沟渠堵塞", "vision-segmenter", 0.78),
    "围墙隐患": ("围墙破损", "vision-segmenter", 0.80),
    "人员入侵": ("人员", "video-analyzer", 0.92),
    "动物防控": ("大型动物", "video-analyzer", 0.85),
    "现场规范": ("未佩戴安全帽", "vision-detector", 0.86),
    "施工合规": ("未穿反光衣", "vision-detector", 0.84),
    "鸟巢": ("鸟巢", "vision-detector", 0.88),
    "螺栓缺失": ("螺栓缺失", "vision-detector", 0.72),
    "信号机破损倾斜": ("信号机破损", "vision-detector", 0.83),
    "电缆槽盖板缺失": ("盖板缺失", "vision-detector", 0.87),
    "线缆悬挂异物": ("施工防尘网", "vision-detector", 0.84),
    "接头严重发热": ("设备发热", "thermal-analyzer", 0.90),
    "杆上设备发热": ("设备发热", "thermal-analyzer", 0.90),
}


class VisionInferenceEngine:
    """CPU model runtime with an explicitly identified baseline fallback."""

    def __init__(self, runtime: CpuVisionRuntime | None = None):
        self.runtime = runtime or CpuVisionRuntime()

    def infer(self, resource: Resource, scenes: list[str]) -> list[InferenceResult]:
        if resource.metadata.get("mock_results"):
            return [InferenceResult(**item) for item in resource.metadata["mock_results"]]
        grouped: dict[str, list[str]] = {}
        for scene in scenes:
            model_group = SCENE_MAPPING.get(scene, ("", "vision-detector", 0.0))[1]
            if model_group in {"vision-detector", "vision-segmenter", "thermal-analyzer"}:
                grouped.setdefault(model_group, []).append(scene)
        outcomes: dict[str, RuntimeOutcome] = {}
        results: list[InferenceResult] = []
        for model_group, group_scenes in grouped.items():
            outcome = self.runtime.infer(resource, model_group, self._default_parameters(model_group))
            outcomes[model_group] = outcome
            if outcome.execution_mode.startswith("onnxruntime"):
                results.extend(self._runtime_results(outcome, group_scenes[0], model_group, "cpu-local", resource, {}))
        lon = float(resource.metadata.get("longitude", 116.1))
        lat = float(resource.metadata.get("latitude", 39.1))
        for scene in scenes:
            if scene in {"防洪点精密监测", "危树侵限"}:
                continue
            model_group = SCENE_MAPPING.get(scene, ("", "vision-detector", 0.0))[1]
            if outcomes.get(model_group) and outcomes[model_group].execution_mode.startswith("onnxruntime"):
                continue
            if not self._fallback_enabled():
                continue
            item = self._single(scene, lon, lat, resource.metadata)
            if item:
                outcome = outcomes.get(model_group)
                item.attributes.update(
                    {
                        "execution_mode": "baseline",
                        "fallback_reason": outcome.reason if outcome else "模型组未配置真实 CPU 推理",
                        "configured_model": outcome.model if outcome else {"model_group": model_group},
                    }
                )
                results.append(item)
        return results

    def test_infer(
        self,
        resource: Resource,
        scenes: list[str],
        model_group: str,
        model_version: str,
        parameters: dict[str, Any],
    ) -> list[InferenceResult]:
        selected_scenes = scenes or [self._default_scene(model_group)]
        outcome = self.runtime.infer(resource, model_group, parameters, model_version)
        if outcome.execution_mode.startswith("onnxruntime"):
            return self._runtime_results(
                outcome,
                selected_scenes[0],
                model_group,
                model_version,
                resource,
                parameters,
            )
        if not self._fallback_enabled():
            return []
        threshold = float(parameters.get("confidence_threshold", parameters.get("mask_threshold", 0.5)))
        version_bonus = 0.018 if model_version != "v1.0.0" else 0.0
        results: list[InferenceResult] = []
        for scene in selected_scenes:
            item = self._single(
                scene,
                float(resource.metadata.get("longitude", 116.1)),
                float(resource.metadata.get("latitude", 39.1)),
                resource.metadata,
            )
            if item is None:
                item = self._generic_result(scene, model_group, resource.metadata)
            adjusted_confidence = min(0.99, max(0.05, item.confidence + version_bonus + (0.55 - threshold) * 0.04))
            if model_group == "vision-detector" and adjusted_confidence < threshold:
                continue
            geometry = item.geometry
            if model_group == "vision-segmenter":
                geometry = {
                    "type": "Polygon",
                    "coordinates": [[[0.18, 0.64], [0.30, 0.42], [0.48, 0.38], [0.67, 0.55], [0.58, 0.76], [0.32, 0.81], [0.18, 0.64]]],
                    "coordinate_space": "normalized",
                }
            measurements = dict(item.measurements)
            if model_group == "thermal-analyzer":
                emissivity = float(parameters.get("emissivity", 0.95))
                raw_temperature = float(resource.metadata.get("temperature_c", 82.6))
                corrected = raw_temperature + (0.95 - emissivity) * 7.5
                measurements.update(
                    {
                        "temperature_c": round(corrected, 2),
                        "ambient_temperature_c": float(parameters.get("ambient_temperature_c", 25)),
                        "emissivity": emissivity,
                        "relative_rise_c": round(corrected - float(parameters.get("ambient_temperature_c", 25)), 2),
                    }
                )
            results.append(
                InferenceResult(
                    scene=scene,
                    category=item.category,
                    confidence=round(adjusted_confidence, 4),
                    geometry=geometry,
                    measurements=measurements,
                    attributes={
                        **item.attributes,
                        "test_mode": True,
                        "parameter_threshold": threshold,
                        "execution_mode": "baseline",
                        "runtime": "deterministic-baseline",
                        "fallback_reason": outcome.reason,
                        "configured_model": outcome.model,
                    },
                    model_name=model_group,
                    model_version=model_version,
                )
            )
        return results

    def runtime_status(self) -> dict[str, Any]:
        return self.runtime.status()

    def unload(self, model_group: str | None = None) -> list[str]:
        return self.runtime.models.unload(model_group)

    def load(self, model_group: str, model_version: str | None = None) -> dict[str, Any]:
        return self.runtime.load(model_group, model_version)

    def _fallback_enabled(self) -> bool:
        settings = getattr(self.runtime, "settings", None)
        return getattr(settings, "fallback_mode", "baseline") == "baseline"

    def _runtime_results(
        self,
        outcome: RuntimeOutcome,
        scene: str,
        model_group: str,
        model_version: str,
        resource: Resource,
        parameters: dict[str, Any],
    ) -> list[InferenceResult]:
        results: list[InferenceResult] = []
        ambient = float(parameters.get("ambient_temperature_c", resource.metadata.get("ambient_temperature_c", 25)))
        for prediction in outcome.results:
            measurements = dict(prediction.get("measurements", {}))
            if model_group == "thermal-analyzer":
                temperature = float(resource.metadata.get("temperature_c", ambient))
                measurements.update(
                    {
                        "temperature_c": temperature,
                        "ambient_temperature_c": ambient,
                        "relative_rise_c": round(temperature - ambient, 2),
                        "radiometric": bool(resource.metadata.get("radiometric", False)),
                    }
                )
            results.append(
                InferenceResult(
                    scene=scene,
                    category=str(prediction.get("category", "巡检目标")),
                    confidence=float(prediction.get("confidence", 0)),
                    geometry=dict(prediction.get("geometry", {})),
                    measurements=measurements,
                    attributes={
                        "execution_mode": outcome.execution_mode,
                        "runtime": "ONNX Runtime CPU",
                        "latency_ms": outcome.latency_ms,
                        "configured_model": outcome.model,
                    },
                    model_name=model_group,
                    model_version=model_version,
                )
            )
        return results

    def _default_parameters(self, model_group: str) -> dict[str, Any]:
        if model_group == "vision-segmenter":
            return {"mask_threshold": 0.5, "minimum_area": 64, "contour_simplification": 0.8}
        return {"confidence_threshold": 0.55, "max_detections": 100}

    def _default_scene(self, model_group: str) -> str:
        return {
            "vision-detector": "塔吊",
            "vision-segmenter": "裂缝识别",
            "thermal-analyzer": "接头严重发热",
        }.get(model_group, "塔吊")

    def _generic_result(self, scene: str, model_group: str, metadata: dict[str, Any]) -> InferenceResult:
        category = {
            "vision-detector": "巡检目标",
            "vision-segmenter": "异常区域",
            "thermal-analyzer": "设备热点",
        }.get(model_group, "巡检目标")
        return InferenceResult(
            scene=scene,
            category=category,
            confidence=0.84,
            geometry={
                "type": "Point",
                "coordinates": [float(metadata.get("longitude", 116.1)), float(metadata.get("latitude", 39.1))],
            },
            measurements={},
            attributes={"execution_mode": "baseline", "adapter": "model-test"},
            model_name=model_group,
            model_version="v1.0.0",
        )

    def _single(self, scene: str, lon: float, lat: float, metadata: dict[str, Any]) -> InferenceResult | None:
        if scene not in SCENE_MAPPING:
            return None
        category, model_name, confidence = SCENE_MAPPING[scene]
        measurements: dict[str, Any] = {"distance_to_track_m": float(metadata.get("distance_to_track_m", 72.5))}
        if scene in {"接头严重发热", "杆上设备发热"}:
            measurements.update({"temperature_c": float(metadata.get("temperature_c", 86.3)), "relative_rise_c": 28.1})
        if scene == "人员入侵":
            measurements.update({"in_protected_area": True, "maintenance_window": False})
        if scene == "动物防控":
            measurements.update({"in_protected_area": True})
        if scene == "裂缝识别":
            measurements.update({"width_cm": 3.2, "length_cm": 62.0})
        return InferenceResult(
            scene=scene,
            category=category,
            confidence=confidence,
            geometry={"type": "Point", "coordinates": [lon, lat]},
            measurements=measurements,
            attributes={"execution_mode": "baseline", "adapter": "deterministic"},
            model_name=model_name,
            model_version="v1.0.0",
        )
