"""Deterministic closed-loop tuning suggestions."""

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path
from typing import Any

from .control_plane import control_plane_store
from .orchestration_metrics import compute_orchestration_metrics
from .scout_config_store import ScoutConfigStore


@dataclass
class TuningSuggestion:
    module_id: str
    parameter: str
    current_value: Any
    suggested_value: Any
    reasoning: str
    confidence: float
    evidence: dict[str, Any]


def _coerce_int(value: Any, default: int = 0) -> int:
    try:
        return int(value)
    except Exception:
        return default


def _coerce_float(value: Any, default: float = 0.0) -> float:
    try:
        return float(value)
    except Exception:
        return default


def _promote_risk_tiers(current: dict[str, Any]) -> dict[str, int]:
    green = max(0, _coerce_int(current.get("green_max_score"), 30))
    yellow = max(green + 1, _coerce_int(current.get("yellow_max_score"), 70))
    promoted_green = min(green + 5, 60)
    promoted_yellow = min(max(yellow + 5, promoted_green + 1), 95)
    return {
        "green_max_score": promoted_green,
        "yellow_max_score": promoted_yellow,
        "red_min_score": promoted_yellow + 1,
    }


def _demote_risk_tiers(current: dict[str, Any]) -> dict[str, int]:
    green = max(0, _coerce_int(current.get("green_max_score"), 30))
    yellow = max(green + 1, _coerce_int(current.get("yellow_max_score"), 70))
    demoted_green = max(green - 5, 0)
    demoted_yellow = max(yellow - 5, demoted_green + 1)
    return {
        "green_max_score": demoted_green,
        "yellow_max_score": demoted_yellow,
        "red_min_score": demoted_yellow + 1,
    }


def _module_rate_suggestions(
    state_dir: Path,
    *,
    min_sample_size: int,
    lookback_days: int,
) -> list[TuningSuggestion]:
    metrics = compute_orchestration_metrics(state_dir, lookback_days=lookback_days)
    rows = metrics.get("modules")
    if not isinstance(rows, list):
        return []

    suggestions: list[TuningSuggestion] = []
    for row in rows:
        if not isinstance(row, dict):
            continue
        module_id = str(row.get("module_id") or "").strip()
        if not module_id:
            continue
        metric_payload = row.get("metrics") if isinstance(row.get("metrics"), dict) else {}
        sample_size = _coerce_int(metric_payload.get("override_decisions"), 0)
        override_count = _coerce_int(metric_payload.get("override_count"), 0)
        if sample_size < min_sample_size:
            continue

        rejection_rate = max(0.0, min(1.0, (override_count / sample_size) if sample_size > 0 else 0.0))
        approval_rate = max(0.0, min(1.0, 1.0 - rejection_rate))
        current_risk = row.get("current_risk_tiers") if isinstance(row.get("current_risk_tiers"), dict) else {}

        if approval_rate > 0.95:
            suggestions.append(
                TuningSuggestion(
                    module_id=module_id,
                    parameter="risk_tiers",
                    current_value=current_risk,
                    suggested_value=_promote_risk_tiers(current_risk),
                    reasoning=(
                        f"Module {module_id} shows sustained high approval rate; consider promoting lower-risk "
                        "actions to reduce manual approvals."
                    ),
                    confidence=min(0.99, 0.75 + (approval_rate - 0.95) * 4.0),
                    evidence={
                        "approval_rate": round(approval_rate, 6),
                        "rejection_rate": round(rejection_rate, 6),
                        "sample_size": sample_size,
                        "time_window_days": lookback_days,
                    },
                )
            )
            continue

        if rejection_rate > 0.30:
            suggestions.append(
                TuningSuggestion(
                    module_id=module_id,
                    parameter="risk_tiers",
                    current_value=current_risk,
                    suggested_value=_demote_risk_tiers(current_risk),
                    reasoning=(
                        f"Module {module_id} has elevated rejection rate; tighten risk thresholds or confidence gating "
                        "to reduce low-quality proposals."
                    ),
                    confidence=min(0.99, 0.65 + (rejection_rate - 0.30) * 2.0),
                    evidence={
                        "approval_rate": round(approval_rate, 6),
                        "rejection_rate": round(rejection_rate, 6),
                        "sample_size": sample_size,
                        "time_window_days": lookback_days,
                    },
                )
            )

    return suggestions


def _scout_dismiss_suggestions(
    state_dir: Path,
    *,
    min_sample_size: int,
    lookback_days: int,
) -> list[TuningSuggestion]:
    store = control_plane_store(state_dir)
    all_items, _cursor = store.list_inbox(limit=200)
    if not all_items:
        return []

    grouped: dict[str, dict[str, int]] = {}
    for item in all_items:
        pattern_type = str(item.pattern_type or "").strip() or "unknown"
        current = grouped.setdefault(pattern_type, {"total": 0, "ignored": 0})
        current["total"] += 1
        if str(item.status or "").strip().lower() == "ignored":
            current["ignored"] += 1

    scout_cfg = ScoutConfigStore(state_dir).load_all()
    suggestions: list[TuningSuggestion] = []
    for pattern_type, counters in grouped.items():
        total = _coerce_int(counters.get("total"), 0)
        ignored = _coerce_int(counters.get("ignored"), 0)
        if total < min_sample_size or total <= 0:
            continue
        dismiss_rate = ignored / total
        if dismiss_rate <= 0.15:
            continue

        config_payload = scout_cfg.get(pattern_type) if isinstance(scout_cfg.get(pattern_type), dict) else {}
        current_threshold = _coerce_float(config_payload.get("confidence_threshold"), 0.5)
        suggested_threshold = min(0.99, max(0.0, current_threshold + 0.10))
        suggestions.append(
            TuningSuggestion(
                module_id=f"scout:{pattern_type}",
                parameter="confidence_threshold",
                current_value=current_threshold,
                suggested_value=round(suggested_threshold, 4),
                reasoning=(
                    f"Dismiss rate for {pattern_type} exceeds 15%; increasing threshold can reduce noisy findings "
                    "and review fatigue."
                ),
                confidence=min(0.95, 0.55 + dismiss_rate),
                evidence={
                    "dismiss_rate": round(dismiss_rate, 6),
                    "sample_size": total,
                    "time_window_days": lookback_days,
                },
            )
        )
    return suggestions


def compute_tuning_suggestions(
    state_dir: Path,
    *,
    min_sample_size: int = 20,
    lookback_days: int = 30,
) -> list[TuningSuggestion]:
    """Analyze approval/rejection patterns and suggest manual tuning updates."""

    resolved_state = Path(state_dir).resolve()
    bounded_samples = max(1, int(min_sample_size))
    bounded_window = max(1, min(int(lookback_days), 3650))

    suggestions = _module_rate_suggestions(
        resolved_state,
        min_sample_size=bounded_samples,
        lookback_days=bounded_window,
    )
    suggestions.extend(
        _scout_dismiss_suggestions(
            resolved_state,
            min_sample_size=bounded_samples,
            lookback_days=bounded_window,
        )
    )
    suggestions.sort(key=lambda item: (item.module_id, item.parameter))
    return suggestions


__all__ = ["TuningSuggestion", "compute_tuning_suggestions"]
