import time
import queue
import numpy as np
try:
    import sounddevice as sd
except (ImportError, OSError):
    sd = None
from ..schema.protocol import SpeakerRequest, SpeakerResponse

def handle_command(cmd: SpeakerRequest, state: dict):
    if cmd is None or cmd.msg_type == "EXIT":
        state["stop"] = True
        return
    if cmd.msg_type == "STOP":
        state["current_data"] = np.zeros((0, 1), dtype=np.float32)
        state["started"] = False
        return
    if cmd.msg_type == "PAUSE":
        state["paused"] = True
        return
    if cmd.msg_type == "CONTINUE":
        state["paused"] = False
        return
    if cmd.msg_type == "AUDIO":
        if cmd.audio is not None and len(cmd.audio) > 0:
             state["current_data"] = np.concatenate(
                 [state["current_data"], cmd.audio.reshape(-1, 1).astype(np.float32)],
                 axis=0
             )

def sync_playback_status(state: dict, result_queue):
    if result_queue is None: return
    if state.get("paused", False):
        target = "PAUSED"
    elif state.get("started", False):
        target = "PLAYING"
    else:
        target = "IDLE"
    if target == state["playback_state"]:
        return
    msg_map = {"PAUSED": "PAUSED", "PLAYING": "STARTED", "IDLE": "FINISHED"}
    result_queue.put(SpeakerResponse(msg_type=msg_map[target]))
    state["playback_state"] = target

def fill_audio(outdata, frames, state: dict):
    if state.get("paused", False):
        outdata.fill(0)
        return
    if not state["started"]:
        if len(state["current_data"]) >= state["threshold"]:
            state["started"] = True
        else:
            outdata.fill(0)
            return
    avail = len(state["current_data"])
    to_copy = min(avail, frames)
    if to_copy > 0:
        outdata[:to_copy] = state["current_data"][:to_copy]
        state["current_data"] = state["current_data"][to_copy:]
    if to_copy < frames:
        outdata[to_copy:].fill(0)
        state["started"] = False

def speaker_worker_proc(play_queue, result_queue=None, sample_rate=24000):
    state = {
        "current_data": np.zeros((0, 1), dtype=np.float32),
        "started": False,
        "threshold": 1200,
        "stop": False,
        "paused": False,
        "playback_state": "IDLE"
    }

    def audio_callback(outdata, frames, time_info, status):
        while True:
            try:
                command = play_queue.get_nowait()
                handle_command(command, state)
            except queue.Empty:
                break
        fill_audio(outdata, frames, state)
        sync_playback_status(state, result_queue)

    if sd is None:
        # Headless mode: no audio device available
        if result_queue:
            result_queue.put(SpeakerResponse(msg_type="READY"))
        while not state.get("stop"):
            try:
                command = play_queue.get(timeout=0.5)
                handle_command(command, state)
            except queue.Empty:
                pass
        return

    try:
        with sd.OutputStream(samplerate=sample_rate, channels=1, callback=audio_callback, blocksize=2048):
            if result_queue:
                result_queue.put(SpeakerResponse(msg_type="READY"))
            while True:
                time.sleep(0.2)
                if state.get("stop"): break
    except KeyboardInterrupt:
        pass
    except Exception as e:
        print(f"⚠️ [SpeakerWorker] No audio device, running headless: {e}")
        if result_queue:
            result_queue.put(SpeakerResponse(msg_type="READY"))
        while not state.get("stop"):
            try:
                command = play_queue.get(timeout=0.5)
                handle_command(command, state)
            except Exception:
                pass
