"""Core Fenra SDK functionality: background worker, queue, and configuration."""

import logging
import queue
import threading
import time
from dataclasses import dataclass, field
from typing import Any

import requests

logger = logging.getLogger(__name__)


@dataclass
class FenraConfig:
    """Configuration for Fenra SDK."""

    api_key: str
    base_url: str = "https://ingest.fenra.io"
    content_logging: bool = False  # Privacy-first default
    enabled: bool = True
    flush_interval: float = 1.0
    batch_size: int = 10
    queue_size: int = 10000

    def __post_init__(self) -> None:
        """Validate configuration."""
        if not self.api_key:
            raise ValueError("api_key is required")


# Global configuration singleton
_config: FenraConfig | None = None
_transaction_queue: queue.Queue[dict[str, Any]] | None = None
_worker_thread: threading.Thread | None = None
_worker_stop_event: threading.Event | None = None


def _get_config() -> FenraConfig:
    """Get the global configuration, raising if not initialized."""
    if _config is None:
        raise RuntimeError("Fenra not initialized. Call fenra.init() first.")
    return _config


def _get_queue() -> queue.Queue[dict[str, Any]]:
    """Get the global transaction queue, raising if not initialized."""
    if _transaction_queue is None:
        raise RuntimeError("Fenra not initialized. Call fenra.init() first.")
    return _transaction_queue


def _background_worker() -> None:
    """Background worker thread that processes transactions from the queue."""
    batch: list[dict[str, Any]] = []
    last_flush = time.time()

    while True:
        try:
            # Check if we should stop
            if _worker_stop_event and _worker_stop_event.is_set():
                # Flush any remaining items before stopping
                if batch:
                    _flush_batch(batch)
                break

            config = _get_config()
            if not config.enabled:
                time.sleep(0.1)
                continue

            # Try to get an item from the queue with timeout
            try:
                transaction = _get_queue().get(timeout=config.flush_interval)
                batch.append(transaction)
            except queue.Empty:
                # Timeout - flush if we have items and enough time has passed
                if batch and (time.time() - last_flush) >= config.flush_interval:
                    _flush_batch(batch)
                    batch = []
                    last_flush = time.time()
                continue

            # Check if we should flush due to batch size
            if len(batch) >= config.batch_size:
                _flush_batch(batch)
                batch = []
                last_flush = time.time()

        except Exception as e:
            # Never crash the host application
            logger.error(f"Error in Fenra background worker: {e}", exc_info=True)
            time.sleep(0.1)


def _flush_batch(batch: list[dict[str, Any]]) -> None:
    """Flush a batch of transactions to the Fenra API."""
    if not batch:
        return

    try:
        config = _get_config()

        # Prepare payload - single transaction or bulk
        if len(batch) == 1:
            payload = batch[0]
        else:
            payload = {"transactions": batch}

        # Send to API
        response = requests.post(
            f"{config.base_url}/usage/transactions",
            json=payload,
            headers={"X-Api-Key": config.api_key},
            timeout=10.0,
        )

        # Log errors but don't raise
        if response.status_code >= 400:
            logger.warning(
                f"Fenra API error: {response.status_code} - {response.text}"
            )
        else:
            logger.debug(f"Fenra: Successfully sent {len(batch)} transaction(s)")

    except Exception as e:
        # Never crash the host application
        logger.error(f"Error sending transactions to Fenra API: {e}", exc_info=True)


def init(
    api_key: str,
    *,
    base_url: str = "https://ingest.fenra.io",
    content_logging: bool = False,
    enabled: bool = True,
    flush_interval: float = 1.0,
    batch_size: int = 10,
    queue_size: int = 10000,
) -> None:
    """
    Initialize Fenra SDK.

    Args:
        api_key: Your Fenra API key
        base_url: Base URL for Fenra API (default: https://ingest.fenra.io)
        content_logging: Whether to log prompt content (default: False, privacy-first)
        enabled: Whether tracking is enabled (default: True)
        flush_interval: Seconds between automatic flushes (default: 1.0)
        batch_size: Maximum transactions per batch (default: 10)
        queue_size: Maximum queue size before dropping transactions (default: 10000)
    """
    global _config, _transaction_queue, _worker_thread, _worker_stop_event

    # Stop existing worker if any
    shutdown()

    # Create configuration
    _config = FenraConfig(
        api_key=api_key,
        base_url=base_url,
        content_logging=content_logging,
        enabled=enabled,
        flush_interval=flush_interval,
        batch_size=batch_size,
        queue_size=queue_size,
    )

    _transaction_queue = queue.Queue(maxsize=_config.queue_size)

    # Create and start worker thread
    _worker_stop_event = threading.Event()
    _worker_thread = threading.Thread(target=_background_worker, daemon=True)
    _worker_thread.start()

    logger.info("Fenra SDK initialized")


def shutdown() -> None:
    """Shutdown Fenra SDK and flush remaining transactions."""
    global _worker_thread, _worker_stop_event

    if _worker_thread is not None and _worker_thread.is_alive():
        if _worker_stop_event:
            _worker_stop_event.set()
        _worker_thread.join(timeout=5.0)
        _worker_thread = None
        _worker_stop_event = None

    logger.info("Fenra SDK shut down")


def flush() -> None:
    """Manually flush all pending transactions."""
    if _transaction_queue is None:
        return

    batch: list[dict[str, Any]] = []
    # Drain the queue
    while True:
        try:
            transaction = _transaction_queue.get_nowait()
            batch.append(transaction)
        except queue.Empty:
            break

    if batch:
        _flush_batch(batch)


def enqueue_transaction(transaction: dict[str, Any]) -> None:
    """
    Enqueue a transaction for background processing.

    This function is safe to call from any thread. Failures are logged
    but never raise exceptions.
    """
    try:
        config = _get_config()
        if not config.enabled:
            return

        # Ensure billable_customer_id is set (required by API)
        if "context" not in transaction:
            transaction["context"] = {}
        if not transaction["context"].get("billable_customer_id"):
            transaction["context"]["billable_customer_id"] = "unknown"
            logger.warning(
                "Fenra: billable_customer_id not set. "
                "Use fenra.set_context(billable_customer_id='...') for customer attribution."
            )

        logger.debug(
            f"Fenra: Enqueuing transaction - provider={transaction.get('provider')}, "
            f"model={transaction.get('model')}, context={transaction.get('context')}"
        )

        _get_queue().put_nowait(transaction)
    except queue.Full:
        logger.warning("Fenra transaction queue is full, dropping transaction")
    except Exception as e:
        # Never crash the host application
        logger.error(f"Error enqueueing Fenra transaction: {e}", exc_info=True)
