"""Configuration helpers for CyberFlage."""

from __future__ import annotations

from copy import deepcopy
import json
from numbers import Real
from pathlib import Path
from typing import Any

DEFAULT_CONFIG: dict[str, Any] = {
    "thresholds": {"MEDIUM": 30.0, "HIGH": 60.0, "CRITICAL": 85.0},
    "risk": {
        "decay_factor": 0.98,
        "signal_weight_scale": 1.0,
        "feature_weight_scale": 0.25,
        "feature_weights": {
            "signal_count": 1.0,
            "total_signal_weight": 0.2,
            "max_signal_weight": 0.1,
        },
    },
    "processing": {
        "max_events_per_cycle": 5000,
    },
    "persistence": {"state_file": "cyberflage_state.json"},
    "dashboard": {"enabled": False, "host": "127.0.0.1", "port": 5000},
    "alerts": {"channel": "logging"},
    "safety": {
        "destructive_actions_enabled": False,
        "destructive_acknowledged": False,
    },
    "logging": {"level": "INFO"},
}


def _merge(base: dict[str, Any], overrides: dict[str, Any]) -> dict[str, Any]:
    merged = deepcopy(base)
    for key, value in overrides.items():
        if isinstance(value, dict) and isinstance(merged.get(key), dict):
            merged[key] = _merge(merged[key], value)
        else:
            merged[key] = value
    return merged


def _to_float(value: Any, default: float) -> float:
    if isinstance(value, Real):
        return float(value)
    return default


def _to_int(value: Any, default: int) -> int:
    if isinstance(value, Real):
        return int(value)
    return default


def _normalize_config(config: dict[str, Any]) -> dict[str, Any]:
    normalized = deepcopy(config)

    thresholds = normalized.setdefault("thresholds", {})
    medium = _to_float(thresholds.get("MEDIUM"), 30.0)
    high = _to_float(thresholds.get("HIGH"), 60.0)
    critical = _to_float(thresholds.get("CRITICAL"), 85.0)
    high = max(high, medium)
    critical = max(critical, high)
    thresholds["MEDIUM"] = medium
    thresholds["HIGH"] = high
    thresholds["CRITICAL"] = critical

    risk = normalized.setdefault("risk", {})
    risk["decay_factor"] = max(0.0, min(1.0, _to_float(risk.get("decay_factor"), 0.98)))
    risk["signal_weight_scale"] = max(0.0, _to_float(risk.get("signal_weight_scale"), 1.0))
    risk["feature_weight_scale"] = max(0.0, _to_float(risk.get("feature_weight_scale"), 0.25))
    feature_weights = risk.setdefault("feature_weights", {})
    if not isinstance(feature_weights, dict):
        feature_weights = {}
    risk["feature_weights"] = {str(k): max(0.0, _to_float(v, 0.0)) for k, v in feature_weights.items()}

    processing = normalized.setdefault("processing", {})
    processing["max_events_per_cycle"] = max(1, _to_int(processing.get("max_events_per_cycle"), 5000))

    dashboard = normalized.setdefault("dashboard", {})
    dashboard["enabled"] = bool(dashboard.get("enabled", False))
    dashboard["host"] = str(dashboard.get("host", "127.0.0.1"))
    dashboard["port"] = max(1, min(65535, _to_int(dashboard.get("port"), 5000)))

    persistence = normalized.setdefault("persistence", {})
    state_file = str(persistence.get("state_file", "cyberflage_state.json")).strip()
    persistence["state_file"] = state_file or "cyberflage_state.json"

    safety = normalized.setdefault("safety", {})
    destructive_enabled = bool(safety.get("destructive_actions_enabled", False))
    destructive_ack = bool(safety.get("destructive_acknowledged", False))
    safety["destructive_actions_enabled"] = destructive_enabled and destructive_ack
    safety["destructive_acknowledged"] = destructive_ack

    logging_cfg = normalized.setdefault("logging", {})
    logging_cfg["level"] = str(logging_cfg.get("level", "INFO")).upper()

    return normalized


def build_config(overrides: dict[str, Any] | None) -> dict[str, Any]:
    """Build a runtime config from safe defaults and optional overrides."""
    if overrides is None:
        return _normalize_config(DEFAULT_CONFIG)
    return _normalize_config(_merge(DEFAULT_CONFIG, overrides))


def load_config_file(path: str) -> dict[str, Any]:
    """Load config overrides from a JSON file."""
    config_path = Path(path)
    if not config_path.exists():
        raise ValueError(f"Config file not found: {config_path}")

    try:
        parsed = json.loads(config_path.read_text(encoding="utf-8"))
    except json.JSONDecodeError as exc:
        raise ValueError(f"Invalid JSON in config file '{config_path}': {exc}") from exc
    except OSError as exc:
        raise ValueError(f"Unable to read config file '{config_path}': {exc}") from exc

    if not isinstance(parsed, dict):
        raise ValueError(f"Config file '{config_path}' must contain a JSON object.")
    return parsed
