from __future__ import annotations

from dataclasses import dataclass
from typing import Any

from app.utils.logging import log_flow_event


@dataclass(frozen=True, slots=True)
class DynamicVADThresholdConfig:
    enabled: bool
    baseline_activation_threshold: float
    baseline_deactivation_threshold: float
    retry_activation_threshold: float
    retry_deactivation_threshold: float


class DynamicVADThresholdController:
    def __init__(
        self,
        vad: Any,
        *,
        config: DynamicVADThresholdConfig,
        call_logger: Any = None,
        timeline: Any = None,
    ) -> None:
        self._vad = vad
        self._config = config
        self._call_logger = call_logger
        self._timeline = timeline
        self._retry_active = False
        self._current_mode = "baseline"
        self._current_activation_threshold = config.baseline_activation_threshold
        self._current_deactivation_threshold = config.baseline_deactivation_threshold

    @property
    def enabled(self) -> bool:
        return bool(self._config.enabled)

    @property
    def retry_active(self) -> bool:
        return self._retry_active

    def current_thresholds(self) -> dict[str, Any]:
        return {
            "mode": self._current_mode,
            "activation_threshold": self._current_activation_threshold,
            "deactivation_threshold": self._current_deactivation_threshold,
        }

    def activate_agent_wait_timeout_retry(self, *, attempt: int, reason: str) -> bool:
        if not self.enabled or self._retry_active:
            return False
        applied = self._apply(
            activation_threshold=self._config.retry_activation_threshold,
            deactivation_threshold=self._config.retry_deactivation_threshold,
            mode="agent_wait_timeout_retry",
            reason=reason,
            attempt=attempt,
        )
        if applied:
            self._retry_active = True
        return applied

    def restore(self, *, reason: str) -> bool:
        if not self.enabled or not self._retry_active:
            return False
        applied = self._apply(
            activation_threshold=self._config.baseline_activation_threshold,
            deactivation_threshold=self._config.baseline_deactivation_threshold,
            mode="baseline",
            reason=reason,
            attempt=None,
        )
        if applied:
            self._retry_active = False
        return applied

    def _apply(
        self,
        *,
        activation_threshold: float,
        deactivation_threshold: float,
        mode: str,
        reason: str,
        attempt: int | None,
    ) -> bool:
        update_options = getattr(self._vad, "update_options", None)
        if not callable(update_options):
            self._emit(
                "vad_dynamic_threshold_failed",
                mode=mode,
                reason=reason,
                attempt=attempt,
                error="update_options_unavailable",
            )
            return False

        try:
            update_options(
                activation_threshold=activation_threshold,
                deactivation_threshold=deactivation_threshold,
            )
        except Exception as exc:
            if self._call_logger is not None:
                self._call_logger.exception("[vad] failed updating dynamic threshold")
            self._emit(
                "vad_dynamic_threshold_failed",
                mode=mode,
                reason=reason,
                attempt=attempt,
                error=type(exc).__name__,
            )
            return False

        self._current_mode = mode
        self._current_activation_threshold = activation_threshold
        self._current_deactivation_threshold = deactivation_threshold
        self._emit(
            "vad_dynamic_threshold_updated",
            mode=mode,
            reason=reason,
            attempt=attempt,
            activation_threshold=activation_threshold,
            deactivation_threshold=deactivation_threshold,
        )
        return True

    def _emit(self, event: str, **fields: Any) -> None:
        clean_fields = {key: value for key, value in fields.items() if value is not None}
        if self._call_logger is not None:
            log_flow_event(self._call_logger, event, **clean_fields)
        if self._timeline is not None:
            self._timeline.emit(event, **clean_fields)
