import asyncio
import traceback
from typing import Any, Dict, Optional
import aiohttp
from jazzmine.logging.exceptions import LoggerDependencyError


class AsyncHttpWorker:
    def __init__(self, url: str, method: str = "POST", headers: Optional[Dict[str, str]] = None, max_queue: int = 1000):
        if aiohttp is None:
            raise LoggerDependencyError("aiohttp is required for async http sinks (pip install aiohttp)")
        self.url = url
        self.method = method.upper()
        self.headers = headers or {}
        self.queue: "asyncio.Queue[Dict[str, Any]]" = asyncio.Queue(maxsize=max_queue)
        self._task: Optional[asyncio.Task] = None
        self._stopped = asyncio.Event()
        self._session: Optional[aiohttp.ClientSession] = None

    async def start(self):
        if self._task is not None:
            return
        self._session = aiohttp.ClientSession()
        self._stopped.clear()
        self._task = asyncio.create_task(self._run())

    async def stop(self, timeout: float = 2.0):
        if self._task is None:
            return
        await self.queue.join()
        self._stopped.set()
        try:
            await asyncio.wait_for(self._task, timeout=timeout)
        except asyncio.TimeoutError:
            self._task.cancel()
        if self._session:
            await self._session.close()
        self._task = None

    async def submit(self, payload: Dict[str, Any]) -> None:
        try:
            await self.queue.put(payload)
        except asyncio.QueueFull:
            pass

    async def _run(self):
        while not self._stopped.is_set() or not self.queue.empty():
            try:
                item = await self.queue.get()
                try:
                    await self._send(item)
                except Exception:
                    print("AsyncHttpWorker send error:", traceback.format_exc())
                finally:
                    self.queue.task_done()
            except Exception:
                await asyncio.sleep(0.1)

    async def _send(self, payload: Dict[str, Any]):
        if not self._session:
            return
        kwargs = {"json": payload, "headers": self.headers}
        async with self._session.request(self.method, self.url, **kwargs) as resp:
            if resp.status >= 400:
                text = await resp.text()
                print(f"AsyncHttpWorker: remote returned status {resp.status}: {text}")
