"""Caliper SDK — auto-instrumentation for LLM API observability."""

from __future__ import annotations

import atexit
from importlib.metadata import version as _get_version
from typing import TYPE_CHECKING

import structlog

from caliper._annotation import Annotation, get_last_request_id
from caliper._features import features, get_feature_metadata

if TYPE_CHECKING:
    from caliper._buffer import RecordBuffer

__version__: str = _get_version("caliper-sdk")

__all__ = ["__version__", "init", "shutdown", "features", "get_feature_metadata", "annotate", "last_request_id"]

logger = structlog.get_logger()

_initialized: bool = False
_buffer: RecordBuffer | None = None


def _get_buffer() -> RecordBuffer | None:
    """Internal accessor used by _anthropic.py to push records."""
    return _buffer


def last_request_id() -> str | None:
    """Return the caliper-generated request ID from the most recent LLM call in this context."""
    return get_last_request_id()


def annotate(request_id: str | None = None, /, **metadata: str) -> None:
    """Attach metadata to a completed LLM request.

    If request_id is not provided, uses the most recent request ID from this context.
    Silently no-ops if: not initialized, empty metadata, or no request_id available.
    """
    if not _initialized or _buffer is None:
        return
    if not metadata:
        return

    resolved_id = request_id if request_id is not None else get_last_request_id()
    if resolved_id is None:
        return

    annotation = Annotation(request_id=resolved_id, metadata=metadata)
    _buffer.push(annotation)


def init(*, target: str = "dev", **kwargs) -> None:
    """Initialize Caliper SDK. Patches supported LLM SDKs and starts background export.

    Env vars (CALIPER_*) take highest priority, then kwargs passed here,
    then pydantic defaults. This lets ops teams override SDK settings at
    runtime without code changes. Unrecognized kwargs are silently dropped
    (with a structlog warning).

    Common kwargs (all targets):
        flush_interval: Seconds between flushes (default 2.0).
        batch_size: Max records per flush (default 250).
        max_queue_size: Max queued records before dropping (default 10_000).
        max_retries: Retry attempts for export failures (default 3).
        capture_tools: Include tool-use details in records (default False).
        patch_anthropic: Monkey-patch the Anthropic SDK (default True).
        patch_openai: Monkey-patch the OpenAI SDK (default True).
        debug: Emit caliper.* structlog lines (default False).

    target="dev" kwargs:
        file_path: JSONL output path (default "caliper_records.jsonl").
        annotations_file_path: Annotations JSONL path (default None).

    target="s3" kwargs:
        s3_bucket: S3 bucket name (required).
        s3_prefix: Key prefix (default "").
        s3_region: AWS region (default "us-east-1").
        s3_access_key: AWS access key ID (default None — uses boto3 defaults).
        s3_secret_key: AWS secret access key (default None).
        s3_endpoint: Custom S3 endpoint URL (default None).
        s3_partition: Time partitioning granularity — "none", "year", "month",
            "day", or "hour" (default "hour").
    """
    global _initialized, _buffer

    if _initialized:
        return

    from caliper._buffer import RecordBuffer
    from caliper._config import build_config
    from caliper._export import create_handler
    from caliper._patch_registry import patch_all

    config = build_config(target, **kwargs)

    handler = create_handler(target, config)

    _buffer = RecordBuffer(
        handler=handler,
        flush_interval=config.flush_interval,
        batch_size=config.batch_size,
        max_queue_size=config.max_queue_size,
    )

    from caliper._patch_helpers import set_capture_tools

    set_capture_tools(config.capture_tools)
    patch_all(config)
    _initialized = True
    atexit.register(shutdown)

    if config.debug:
        logger.info("caliper.initialized", target=config.target)


def shutdown() -> None:
    """Flush remaining records, restore original SDK methods, stop background thread."""
    global _initialized, _buffer

    if not _initialized:
        return

    from caliper._patch_registry import unpatch_all

    unpatch_all()

    if _buffer is not None:
        _buffer.shutdown()
        _buffer = None

    _initialized = False
