"""
Local HTTP server on localhost:9999.
Receives SDK traces from the customer's application process.
Puts them into the shared queue for the main loop to batch and ship.
"""
import json
import logging
import queue
from http.server import BaseHTTPRequestHandler, HTTPServer
from threading import Thread

logger = logging.getLogger(__name__)
_span_queue: queue.Queue = queue.Queue(maxsize=10_000)

# M5: Maximum request body size (10 MB)
MAX_REQUEST_SIZE = 10 * 1024 * 1024

# M4: Only allow localhost binding
ALLOWED_HOSTS = ('localhost', '127.0.0.1', '::1')


class _Handler(BaseHTTPRequestHandler):
    def do_POST(self):
        try:
            length = int(self.headers.get('Content-Length', 0))

            # M5: Reject oversized requests
            if length > MAX_REQUEST_SIZE:
                self._respond(413, {'error': f'Request body too large. Max {MAX_REQUEST_SIZE} bytes.'})
                return

            body = self.rfile.read(length)
            data = json.loads(body)
            for span in data.get('spans', []):
                try:
                    _span_queue.put_nowait(span)
                except queue.Full:
                    logger.warning('Span queue full — dropping span.')
            self._respond(200, {'status': 'ok'})
        except json.JSONDecodeError:
            logger.warning('Receiver got invalid JSON.')
            self._respond(400, {'error': 'Invalid JSON'})
        except Exception:
            logger.warning('Receiver error processing request.', exc_info=True)
            self._respond(400, {'error': 'Bad request'})

    def _respond(self, code: int, body: dict):
        payload = json.dumps(body).encode()
        self.send_response(code)
        self.send_header('Content-Type', 'application/json')
        self.send_header('Content-Length', str(len(payload)))
        self.end_headers()
        self.wfile.write(payload)

    def log_message(self, *args):
        pass  # suppress default request logs


def start(host: str = 'localhost', port: int = 9999) -> None:
    # M4: Validate host is localhost-only
    if host not in ALLOWED_HOSTS:
        raise ValueError(
            f'Receiver must bind to localhost only. Got: {host!r}. '
            f'Allowed: {ALLOWED_HOSTS}'
        )
    server = HTTPServer((host, port), _Handler)
    thread = Thread(target=server.serve_forever, daemon=True, name='southstar-receiver')
    thread.start()
    logger.info('SDK receiver listening on %s:%d', host, port)


def drain() -> list:
    """Drain all queued spans. Called by the main loop before shipping."""
    spans = []
    try:
        while True:
            spans.append(_span_queue.get_nowait())
    except queue.Empty:
        pass
    return spans
