"""
S4 — Bluetooth audio settings.

Audio ROUTE (Speakers / Bluetooth), Mute, and a Test tone for a connected BT
device. System VOLUME is NOT here anymore — it lives on the Display & Sound
screen and the Quick Menu (one shared, real element). Focusable zones: route
toggle, Mute, Test tone.
"""

from __future__ import annotations

import pygame

from ..design import tokens as t
from ..shared import widgets as w
from ..shared import fonts
from ..shared import backend

# focusable zones
Z_ROUTE, Z_MUTE, Z_TONE = 0, 1, 2


class AudioScreen:
    name = "audio"

    def __init__(self, app, device=None):
        self.app = app
        self.device = device or {"name": "Audio", "connected": False, "battery": ""}
        # Default to the BUILT-IN SPEAKER. Bluetooth is only selected when a BT
        # audio device is actually connected (live status flips this below).
        # A dongle that isn't plugged in must never leave the UI — or the
        # system — routed to a dead BT sink with no way back to the speaker.
        self.route_bt = False
        self.muted = False
        self.focus = Z_TONE        # design shows Test tone focused
        self._load_live_audio()

    def _load_live_audio(self):
        # Real audio route comes from the csr-fix pulse daemon (via audioops),
        # which works WITHOUT aactl — read it first so the screen reflects
        # reality even when the BT job server isn't deployed to this port.
        live_route = backend.audio_route_status()
        if live_route in ("bluetooth", "speaker"):
            self.route_bt = live_route == "bluetooth"
        env = backend.bluetooth_status()
        if not env:
            return
        data = env.get("data") or {}
        dev = data.get("device")
        audio_route_ready = bool(data.get("audio_route_ready"))
        if isinstance(dev, dict) and dev.get("name"):
            battery_pct = dev.get("battery_pct")
            self.device = {
                "name": dev["name"],
                "connected": bool(dev.get("connected")),
                "audio_ready": bool(dev.get("audio_ready") or (dev.get("connected") and audio_route_ready)),
                "battery": f"{battery_pct}%" if battery_pct is not None else "",
            }
        elif not self.device.get("connected"):
            self.device = {"name": "Bluetooth audio", "connected": False, "battery": ""}
        route = data.get("route")
        if route in ("bluetooth", "speaker"):
            self.route_bt = route == "bluetooth"

    def _apply_live_data(self, data):
        dev = data.get("device") if isinstance(data, dict) else None
        audio_route_ready = bool(data.get("audio_route_ready")) if isinstance(data, dict) else False
        if isinstance(dev, dict) and dev.get("name"):
            battery_pct = dev.get("battery_pct")
            self.device = {
                "name": dev["name"],
                "connected": bool(dev.get("connected")),
                "audio_ready": bool(dev.get("audio_ready") or (dev.get("connected") and audio_route_ready)),
                "battery": f"{battery_pct}%" if battery_pct is not None else "",
            }
        route = data.get("route") if isinstance(data, dict) else None
        if route in ("bluetooth", "speaker"):
            self.route_bt = route == "bluetooth"

    def on_action(self, action):
        # Layout is two rows: the route segmented (top) and the
        # Mute / Test-tone button pair (bottom). up/down moves between
        # rows; left/right toggles the route on the top row or hops
        # between the two buttons on the bottom row.
        if action == "up":
            if self.focus in (Z_MUTE, Z_TONE):
                self.focus = Z_ROUTE
        elif action == "down":
            if self.focus == Z_ROUTE:
                self.focus = Z_TONE
        elif action == "left":
            if self.focus == Z_ROUTE:
                old_route = self.route_bt
                self.route_bt = False
                self._apply_route(old_route)
            elif self.focus == Z_TONE:
                self.focus = Z_MUTE
        elif action == "right":
            if self.focus == Z_ROUTE:
                old_route = self.route_bt
                self.route_bt = True
                self._apply_route(old_route)
            elif self.focus == Z_MUTE:
                self.focus = Z_TONE
        elif action == "a":
            self._activate()
        elif action == "b":
            self.app.pop()

    def _activate(self):
        # The route is a live segmented control — it switches on left/right
        # and applies immediately. A ("confirm") deliberately does NOT flip
        # it: users expect A to confirm the current choice, not toggle it.
        if self.focus == Z_MUTE:
            self.muted = not self.muted
            self.app.toast("Muted" if self.muted else "Unmuted")
        elif self.focus == Z_TONE:
            # Plays through the CURRENT route — the built-in speaker too, no BT
            # required. Run off the render thread so the ~1.5s tone doesn't
            # freeze the UI; the user's ears are the real test.
            where = "Bluetooth" if self.route_bt else "the speaker"
            self.app.toast(f"Playing test tone on {where}…")
            backend.call_async("audio.test_tone", backend.audio_test_tone)

    def _apply_route(self, previous_route=None):
        # Write the csr-fix daemon's route mode (via audioops) — no aactl needed.
        # The daemon moves the PulseAudio sink within ~1s; we also nudge it now.
        target = "bluetooth" if self.route_bt else "speaker"
        ok = backend.audio_route(target)   # True/False live, None=not live (dev)
        if ok is False:
            # couldn't write the route (no pulse daemon) — revert + say so
            if previous_route is not None:
                self.route_bt = previous_route
            self.app.toast("Audio route unavailable")
        else:
            self.app.toast("Sound goes to Bluetooth" if self.route_bt
                           else "Sound goes to the speaker")

    def update(self, dt_ms):
        pass

    def draw(self, surf):
        w.status_bar(surf, title="Audio settings", back=True)

        # mini hero @ y60
        display_name = self.device["name"] if self.device.get("connected") else "Bluetooth audio"
        name_s = fonts.render(t.Type.HERO, display_name, t.Color.TEXT_PRIMARY)
        surf.blit(name_s, (16, 60))
        if self.device.get("connected"):
            sub = "Connected" if self.device.get("audio_ready") else "Connected · audio route missing"
            if self.device.get("battery"):
                sub += f" · {self.device['battery']} battery"
        else:
            sub = "No Bluetooth audio device connected"
        sub_s = fonts.render(t.Type.MONO_TECH, sub, t.Color.TEXT_SECONDARY)
        surf.blit(sub_s, (16, 60 + name_s.get_height() + 6))
        pygame.draw.line(surf, t.Color.BORDER_SOFT, (16, 122), (624, 122), 1)

        # segmented route @ y134
        seg_rect = pygame.Rect(16, 134, 608, 50)
        w.segmented(surf, seg_rect,
                    [("ph-speaker-simple-high", "Speakers"), ("ph-bluetooth", "Bluetooth")],
                    selected_idx=1 if self.route_bt else 0,
                    focused_idx=(1 if self.route_bt else 0) if self.focus == Z_ROUTE else None)

        # where the sound goes — a plain line under the route toggle
        route_name = self.device["name"] if self.route_bt and self.device.get("connected") else "Bluetooth audio"
        out = fonts.render(t.Type.CAPTION,
                           f"Sound goes to {route_name}" if self.route_bt else "Sound goes to the speaker",
                           t.Color.TEXT_MUTED)
        surf.blit(out, (16, 196))
        vol_note = fonts.render(t.Type.CAPTION, "Volume lives in Display & Sound",
                                t.Color.TEXT_MUTED)
        surf.blit(vol_note, (624 - 14 - vol_note.get_width(), 196))

        # buttons @ y228
        w.button(surf, pygame.Rect(16, 228, 296, 44), "Mute",
                 icon_name="ph-speaker-simple-x", focused=(self.focus == Z_MUTE))
        w.button(surf, pygame.Rect(328, 228, 296, 44), "Test tone",
                 icon_name="ph-play-circle", focused=(self.focus == Z_TONE))

        w.footer_hints(surf, [("A", "select"), ("B", "back")])
