"""Async AudioProxy that manages the subprocess."""

from __future__ import annotations

import asyncio
import logging
import sys

from ._worker import SRTP_OPUS_CLOCK_RATE

_LOGGER = logging.getLogger(__name__)


class AudioProxy:
    """
    Proxy that converts FFmpeg's Opus RTP timestamps for HomeKit.

    Runs as a subprocess to keep crypto work off the caller's event loop.
    """

    def __init__(
        self,
        dest_addr: str,
        dest_port: int,
        srtp_key_b64: str,
        target_clock_rate: int,
    ) -> None:
        """Initialize the audio proxy."""
        self._dest_addr = dest_addr
        self._dest_port = dest_port
        self._srtp_key_b64 = srtp_key_b64
        self._target_clock_rate = target_clock_rate
        self._process: asyncio.subprocess.Process | None = None
        self._stderr_task: asyncio.Task[None] | None = None
        self.local_port: int = 0

    async def async_start(self) -> None:
        """Start the proxy subprocess."""
        self._process = await asyncio.create_subprocess_exec(
            sys.executable,
            "-m",
            "homekit_audio_proxy",
            self._dest_addr,
            str(self._dest_port),
            str(self._target_clock_rate),
            stdin=asyncio.subprocess.PIPE,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE,
        )
        # Pass SRTP key via stdin to avoid exposing it in process args
        if self._process.stdin:
            self._process.stdin.write(self._srtp_key_b64.encode() + b"\n")
            await self._process.stdin.drain()
            self._process.stdin.close()

        if self._process.stdout is None:
            _LOGGER.error("Audio proxy subprocess has no stdout")
            await self.async_stop()
            return

        line = await self._process.stdout.readline()
        if not line:
            # Process exited before writing the port — read stderr for details
            stderr_output = b""
            if self._process.stderr:
                stderr_output = await self._process.stderr.read()
            _LOGGER.error(
                "Audio proxy subprocess failed to start: %s",
                stderr_output.decode(errors="replace").strip(),
            )
            await self.async_stop()
            return

        self.local_port = int(line.strip())

        # Log stderr in the background so errors are visible
        if self._process.stderr:
            self._stderr_task = asyncio.create_task(
                self._log_stderr(self._process.stderr)
            )

        _LOGGER.debug(
            "Audio proxy subprocess started (PID %d) on port %d -> %s:%d"
            " (clock %d->%d)",
            self._process.pid,
            self.local_port,
            self._dest_addr,
            self._dest_port,
            SRTP_OPUS_CLOCK_RATE,
            self._target_clock_rate,
        )

    @staticmethod
    async def _log_stderr(stderr: asyncio.StreamReader) -> None:
        """Forward subprocess stderr to logger."""
        while True:
            line = await stderr.readline()
            if not line:
                return
            _LOGGER.warning(
                "Audio proxy: %s",
                line.decode(errors="replace").rstrip(),
            )

    async def async_stop(self) -> None:
        """Stop the proxy subprocess and wait for port release."""
        if self._stderr_task and not self._stderr_task.done():
            self._stderr_task.cancel()
            try:
                await self._stderr_task
            except asyncio.CancelledError:
                pass
            self._stderr_task = None
        if self._process is not None:
            if self._process.returncode is None:
                self._process.kill()
                await self._process.wait()
            self._process = None
