"""Wake-word sensitivity tuner.

WAKE_WORD_THRESHOLD is otherwise a number you guess at in .env, restart, and
then test by saying "thunderbolt" at your computer repeatedly. This window
makes it evidence-based: a live peak-score readout while you talk, a rolling
list of near misses (frames that scored just under the threshold — i.e. the
times it *nearly* heard you), and a slider that takes effect immediately,
mid-listen, without a restart.

The controller owns the threshold; this window is a view over it.
"""

from __future__ import annotations

import time
from typing import Callable

from PySide6.QtCore import Qt, QTimer
from PySide6.QtWidgets import (
    QDialog, QHBoxLayout, QLabel, QListWidget, QPushButton, QSlider, QVBoxLayout,
)

_SLIDER_SCALE = 100  # QSlider is integer-only; threshold is 0.00-1.00


class WakeTunerWindow(QDialog):
    def __init__(
        self,
        get_threshold: Callable[[], float],
        set_threshold: Callable[[float], None],
        get_stats: Callable[[], dict],
        on_reset: Callable[[], None],
    ):
        super().__init__()
        self._get_threshold = get_threshold
        self._set_threshold = set_threshold
        self._get_stats = get_stats
        self._on_reset = on_reset

        self.setWindowTitle("Bolt — wake word tuning")
        self.resize(460, 380)

        self._threshold_label = QLabel()
        self._slider = QSlider(Qt.Horizontal)
        self._slider.setRange(5, 99)
        self._slider.setValue(int(round(get_threshold() * _SLIDER_SCALE)))
        self._slider.valueChanged.connect(self._threshold_changed)

        self._peak_label = QLabel("Peak score since reset: —")
        self._peak_label.setToolTip(
            'Say "thunderbolt" a few times and watch this. Set the threshold '
            "just below the peak you can hit reliably."
        )

        self._misses = QListWidget()

        reset_button = QPushButton("Reset stats")
        reset_button.clicked.connect(self._reset)
        close_button = QPushButton("Close")
        close_button.clicked.connect(self.close)
        close_button.setDefault(True)

        buttons = QHBoxLayout()
        buttons.addWidget(reset_button)
        buttons.addStretch(1)
        buttons.addWidget(close_button)

        layout = QVBoxLayout(self)
        layout.addWidget(self._threshold_label)
        layout.addWidget(self._slider)
        layout.addWidget(self._peak_label)
        layout.addWidget(QLabel("Near misses (heard something, didn't quite fire):"))
        layout.addWidget(self._misses)
        layout.addLayout(buttons)

        # Polled rather than signal-driven: scores arrive ~12x/second on the
        # audio thread, and a queued signal per frame to repaint a label is
        # more traffic than this is worth.
        self._timer = QTimer(self)
        self._timer.timeout.connect(self.refresh)
        self._update_threshold_label()

    def _threshold_changed(self, value: int) -> None:
        self._set_threshold(value / _SLIDER_SCALE)
        self._update_threshold_label()

    def _update_threshold_label(self) -> None:
        threshold = self._slider.value() / _SLIDER_SCALE
        self._threshold_label.setText(
            f"Threshold: {threshold:.2f}   (lower = more sensitive, more false triggers)"
        )

    def _reset(self) -> None:
        self._on_reset()
        self.refresh()

    def refresh(self) -> None:
        stats = self._get_stats() or {}
        peak = stats.get("peak", 0.0)
        self._peak_label.setText(f"Peak score since reset: {peak:.3f}")
        self._misses.clear()
        for timestamp, score, threshold in reversed(stats.get("near_misses", [])):
            when = time.strftime("%H:%M:%S", time.localtime(timestamp))
            self._misses.addItem(f"{when}   scored {score:.3f}   (threshold {threshold:.2f})")
        if self._misses.count() == 0:
            self._misses.addItem("Nothing yet — say the wake phrase a few times.")

    def show_refreshed(self) -> None:
        self._slider.setValue(int(round(self._get_threshold() * _SLIDER_SCALE)))
        self.refresh()
        self.show()
        self.raise_()
        self.activateWindow()
        self._timer.start(500)

    def closeEvent(self, event) -> None:
        self._timer.stop()
        super().closeEvent(event)
