"""Lightweight ingestion queue used to decouple writes from API requests."""
from __future__ import annotations

import threading
import time
from queue import Queue
from typing import Callable, List, Optional

from hieromem_core.episode import Episode

IngestionConsumer = Callable[[List[Episode]], None]


class IngestionQueue:
    def __init__(
        self,
        consumer: IngestionConsumer,
        *,
        batch_size: int = 32,
        flush_interval: float = 0.5,
    ) -> None:
        self.consumer = consumer
        self.batch_size = max(1, int(batch_size))
        self.flush_interval = max(0.1, float(flush_interval))
        self._queue: Queue[Episode] = Queue()
        self._stop = threading.Event()
        self._thread: Optional[threading.Thread] = None

    def start(self) -> None:
        if self._thread and self._thread.is_alive():
            return
        self._stop.clear()
        self._thread = threading.Thread(target=self._run, daemon=True)
        self._thread.start()

    def stop(self) -> None:
        self._stop.set()
        if self._thread:
            self._thread.join(timeout=1)

    def enqueue(self, episode: Episode) -> None:
        self._queue.put(episode)

    @property
    def size(self) -> int:
        return self._queue.qsize()

    def _drain_batch(self) -> List[Episode]:
        items: List[Episode] = []
        while len(items) < self.batch_size and not self._queue.empty():
            try:
                items.append(self._queue.get_nowait())
            except Exception:
                break
        return items

    def _run(self) -> None:
        while not self._stop.is_set():
            batch = self._drain_batch()
            if batch:
                self.consumer(batch)
            time.sleep(self.flush_interval)
        # flush remaining
        tail = self._drain_batch()
        if tail:
            self.consumer(tail)


class SynchronousIngestionQueue(IngestionQueue):
    """Queue facade that executes synchronously for deterministic tests."""

    def __init__(self, consumer: IngestionConsumer) -> None:
        super().__init__(consumer, batch_size=1, flush_interval=0)

    def start(self) -> None:  # pragma: no cover - compatibility hook
        return

    def enqueue(self, episode: Episode) -> None:
        self.consumer([episode])

    def stop(self) -> None:  # pragma: no cover - nothing to stop in sync mode
        return
