"""Publish warning/error logs into EPICS ring-buffer PVs."""

from __future__ import annotations

import logging
import threading
from collections import deque
from queue import Empty, Queue
from typing import Deque

from .io import BridgeIO
from .pv import PV

_QUEUE_GET_TIMEOUT_S = 0.2


class LogPvPublisherHandler(logging.Handler):
    """Best-effort log fan-out from Python logging to EPICS PV buffers."""

    def __init__(
        self,
        warning_pv: PV,
        error_pv: PV,
        buffer_size: int = 20,
    ) -> None:
        super().__init__(level=logging.WARNING)
        if buffer_size < 1:
            raise ValueError("buffer_size must be >= 1")

        self._warning_buffer: Deque[str] = deque(maxlen=buffer_size)
        self._error_buffer: Deque[str] = deque(maxlen=buffer_size)
        self._queue: Queue[tuple[int, str]] = Queue()
        self._stop_event = threading.Event()
        self._publisher = BridgeIO()
        self._warning_pv = warning_pv
        self._error_pv = error_pv

        self._worker = threading.Thread(
            target=self._run_worker,
            daemon=True,
            name="epics_log_pv_publisher",
        )
        self._worker.start()

    def emit(self, record: logging.LogRecord) -> None:
        """Enqueue a warning/error record for asynchronous PV publishing."""
        if record.levelno < logging.WARNING:
            return

        try:
            formatted = self.format(record)
            self._queue.put_nowait((record.levelno, formatted))
        except Exception:
            self.handleError(record)

    def _run_worker(self) -> None:
        while not self._stop_event.is_set():
            try:
                levelno, message = self._queue.get(timeout=_QUEUE_GET_TIMEOUT_S)
            except Empty:
                continue

            if levelno >= logging.ERROR:
                self._error_buffer.append(message)
                self._publish_buffer(self._error_pv, self._error_buffer)
            else:
                self._warning_buffer.append(message)
                self._publish_buffer(self._warning_pv, self._warning_buffer)

    def _publish_buffer(self, pv: PV, entries: Deque[str]) -> None:
        """Publish the current ring-buffer snapshot to a string-array PV."""
        pv.val = list(entries)[::-1]  # Last in, first out
        try:
            self._publisher.pvput(pv)
        except Exception:
            return

    def close(self) -> None:
        self._stop_event.set()
        if self._worker.is_alive():
            self._worker.join(timeout=1.0)
        super().close()
