import asyncio
import aiohttp
from queue import Queue
from threading import Thread, Event, Lock
from typing import List
from .types import TraceSpan
from .config import TracingConfig


class TraceSender:
    """Asynchronous sender for trace spans"""

    def __init__(self):
        self.queue = Queue()
        self.batch: List[TraceSpan] = []
        self.batch_lock = Lock()
        self.stop_event = Event()
        self.worker_thread = Thread(target=self._worker_loop, daemon=True)
        self.worker_thread.start()

    def add_span(self, span: TraceSpan):
        """Add a span to the queue"""
        self.queue.put(span)

    def _worker_loop(self):
        """Main processing loop running in a separate thread"""
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)

        try:
            loop.run_until_complete(self._process_queue())
        finally:
            loop.close()

    async def _process_queue(self):
        """Process the queue with periodic sending"""
        while not self.stop_event.is_set():
            # Collect batch
            with self.batch_lock:
                while not self.queue.empty() and len(self.batch) < TracingConfig.get_batch_size():
                    span = self.queue.get()
                    self.batch.append(span)

            # Send if there is something to send
            if self.batch:
                await self._send_batch()

            # Wait before the next iteration
            await asyncio.sleep(TracingConfig.get_flush_interval())

    async def _send_batch(self):
        """Send batch to the server"""
        url = TracingConfig.get_url()
        if not url:
            self.batch.clear()
            return

        with self.batch_lock:
            if not self.batch:
                return

            payload = {
                "project": TracingConfig.get_project(),
                "spans": [span.to_dict() for span in self.batch]
            }

            headers = {"Content-Type": "application/json"}
            api_key = TracingConfig.get_api_key()
            if api_key:
                headers["Authorization"] = f"Bearer {api_key}"

            try:
                async with aiohttp.ClientSession() as session:
                    async with session.post(
                        url,
                        json=payload,
                        headers=headers,
                        timeout=aiohttp.ClientTimeout(total=10)
                    ) as resp:
                        if resp.status >= 400:
                            # Can add logging here
                            pass
            except Exception:
                # Graceful degradation - do not break main logic
                pass
            finally:
                self.batch.clear()

    def flush(self):
        """Force send all spans"""
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)

        try:
            loop.run_until_complete(self._send_batch())
        finally:
            loop.close()

    def stop(self):
        """Stop the sender"""
        self.stop_event.set()
        self.flush()
