"""Instrumentor registry for OpenTelemetry auto-instrumentation.

Maps instrumentor names to their import paths and classes, organized by
category. Used by ``auto_instrument()`` to discover and activate installed
instrumentors at runtime.
"""

from __future__ import annotations

from typing import Dict, NamedTuple


class InstrumentorEntry(NamedTuple):
    """Metadata for a single OpenTelemetry instrumentor."""

    module: str  # e.g. "opentelemetry.instrumentation.flask"
    class_name: str  # e.g. "FlaskInstrumentor"
    needs_app: bool  # True if instrument_app(app) is required


INSTRUMENTOR_REGISTRY: Dict[str, Dict[str, InstrumentorEntry]] = {
    "web": {
        "flask": InstrumentorEntry("opentelemetry.instrumentation.flask", "FlaskInstrumentor", True),
        "django": InstrumentorEntry("opentelemetry.instrumentation.django", "DjangoInstrumentor", False),
        "fastapi": InstrumentorEntry("opentelemetry.instrumentation.fastapi", "FastAPIInstrumentor", True),
        "starlette": InstrumentorEntry("opentelemetry.instrumentation.starlette", "StarletteInstrumentor", True),
        "tornado": InstrumentorEntry("opentelemetry.instrumentation.tornado", "TornadoInstrumentor", False),
        "falcon": InstrumentorEntry("opentelemetry.instrumentation.falcon", "FalconInstrumentor", True),
        "pyramid": InstrumentorEntry("opentelemetry.instrumentation.pyramid", "PyramidInstrumentor", False),
        "aiohttp-client": InstrumentorEntry("opentelemetry.instrumentation.aiohttp_client", "AioHttpClientInstrumentor", False),
        "aiohttp-server": InstrumentorEntry("opentelemetry.instrumentation.aiohttp_server", "AioHttpServerInstrumentor", False),
        "asgi": InstrumentorEntry("opentelemetry.instrumentation.asgi", "OpenTelemetryMiddleware", True),
        "wsgi": InstrumentorEntry("opentelemetry.instrumentation.wsgi", "OpenTelemetryMiddleware", True),
    },
    "db": {
        "psycopg": InstrumentorEntry("opentelemetry.instrumentation.psycopg", "PsycopgInstrumentor", False),
        "psycopg2": InstrumentorEntry("opentelemetry.instrumentation.psycopg2", "Psycopg2Instrumentor", False),
        "pymysql": InstrumentorEntry("opentelemetry.instrumentation.pymysql", "PyMySQLInstrumentor", False),
        "mysql": InstrumentorEntry("opentelemetry.instrumentation.mysql", "MySQLInstrumentor", False),
        "mysqlclient": InstrumentorEntry("opentelemetry.instrumentation.mysqlclient", "MySQLClientInstrumentor", False),
        "sqlite3": InstrumentorEntry("opentelemetry.instrumentation.sqlite3", "SQLite3Instrumentor", False),
        "asyncpg": InstrumentorEntry("opentelemetry.instrumentation.asyncpg", "AsyncPGInstrumentor", False),
        "aiopg": InstrumentorEntry("opentelemetry.instrumentation.aiopg", "AiopgInstrumentor", False),
        "pymongo": InstrumentorEntry("opentelemetry.instrumentation.pymongo", "PymongoInstrumentor", False),
        "pymssql": InstrumentorEntry("opentelemetry.instrumentation.pymssql", "PyMSSQLInstrumentor", False),
        "redis": InstrumentorEntry("opentelemetry.instrumentation.redis", "RedisInstrumentor", False),
        "pymemcache": InstrumentorEntry("opentelemetry.instrumentation.pymemcache", "PymemcacheInstrumentor", False),
        "dbapi": InstrumentorEntry("opentelemetry.instrumentation.dbapi", "DatabaseApiIntegration", False),
        "sqlalchemy": InstrumentorEntry("opentelemetry.instrumentation.sqlalchemy", "SQLAlchemyInstrumentor", False),
        "elasticsearch": InstrumentorEntry("opentelemetry.instrumentation.elasticsearch", "ElasticsearchInstrumentor", False),
        "cassandra": InstrumentorEntry("opentelemetry.instrumentation.cassandra", "CassandraInstrumentor", False),
        "tortoiseorm": InstrumentorEntry("opentelemetry.instrumentation.tortoiseorm", "TortoiseORMInstrumentor", False),
    },
    "messaging": {
        "celery": InstrumentorEntry("opentelemetry.instrumentation.celery", "CeleryInstrumentor", False),
        "pika": InstrumentorEntry("opentelemetry.instrumentation.pika", "PikaInstrumentor", False),
        "aio-pika": InstrumentorEntry("opentelemetry.instrumentation.aio_pika", "AioPikaInstrumentor", False),
        "kafka-python": InstrumentorEntry("opentelemetry.instrumentation.kafka", "KafkaInstrumentor", False),
        "confluent-kafka": InstrumentorEntry("opentelemetry.instrumentation.confluent_kafka", "ConfluentKafkaInstrumentor", False),
        "aiokafka": InstrumentorEntry("opentelemetry.instrumentation.aiokafka", "AIOKafkaInstrumentor", False),
        "boto3sqs": InstrumentorEntry("opentelemetry.instrumentation.boto3sqs", "Boto3SQSInstrumentor", False),
        "remoulade": InstrumentorEntry("opentelemetry.instrumentation.remoulade", "RemouladeInstrumentor", False),
    },
    "http-clients": {
        "requests": InstrumentorEntry("opentelemetry.instrumentation.requests", "RequestsInstrumentor", False),
        "urllib": InstrumentorEntry("opentelemetry.instrumentation.urllib", "URLLibInstrumentor", False),
        "urllib3": InstrumentorEntry("opentelemetry.instrumentation.urllib3", "URLLib3Instrumentor", False),
        "httpx": InstrumentorEntry("opentelemetry.instrumentation.httpx", "HTTPXClientInstrumentor", False),
        "grpc": InstrumentorEntry("opentelemetry.instrumentation.grpc", "GrpcInstrumentorClient", False),
    },
    "aws": {
        "botocore": InstrumentorEntry("opentelemetry.instrumentation.botocore", "BotocoreInstrumentor", False),
        "aws-lambda": InstrumentorEntry("opentelemetry.instrumentation.aws_lambda", "AwsLambdaInstrumentor", False),
    },
    "misc": {
        "logging": InstrumentorEntry("opentelemetry.instrumentation.logging", "LoggingInstrumentor", False),
        "jinja2": InstrumentorEntry("opentelemetry.instrumentation.jinja2", "Jinja2Instrumentor", False),
        "click": InstrumentorEntry("opentelemetry.instrumentation.click", "ClickInstrumentor", False),
        "asyncclick": InstrumentorEntry("opentelemetry.instrumentation.asyncclick", "AsyncClickInstrumentor", False),
        "asyncio": InstrumentorEntry("opentelemetry.instrumentation.asyncio", "AsyncioInstrumentor", False),
        "threading": InstrumentorEntry("opentelemetry.instrumentation.threading", "ThreadingInstrumentor", False),
        "system-metrics": InstrumentorEntry("opentelemetry.instrumentation.system_metrics", "SystemMetricsInstrumentor", False),
    },
}


def get_all_instrumentors() -> Dict[str, InstrumentorEntry]:
    """Return a flat dict of all instrumentor entries across all categories."""
    result: Dict[str, InstrumentorEntry] = {}
    for entries in INSTRUMENTOR_REGISTRY.values():
        result.update(entries)
    return result
