"""Runtime initialization for probe patches."""

from __future__ import annotations

import atexit
import json
import platform
import sys
from importlib import metadata
from pathlib import Path

from autodebug_probe import env_patch, exception_patch, http_patch, time_patch, sql_patch, file_patch
from autodebug_probe import unsupported_detect, async_patch, sqlalchemy_detect, redis_patch, aiohttp_patch, grpc_patch, boto3_patch, pymongo_patch
from autodebug_probe import asyncpg_patch, aiomysql_patch, aiosqlite_patch, celery_patch
from autodebug_probe.recorder import get_recorder, get_run_dir

# Bundle format version. Increment when capture format changes.
# 1.0 = original format (type-based sequencing)
# 2.0 = adds async_task_graph.jsonl.zst + async_risk_level + format_version
BUNDLE_FORMAT_VERSION = "2.0"


_initialized = False


def _installed_packages() -> dict:
    packages = {}
    for dist in metadata.distributions():
        name = dist.metadata.get("Name")
        version = dist.version
        if name and version:
            packages[name] = version
    return dict(sorted(packages.items()))


def _update_manifest() -> None:
    """Update manifest with final environment info.

    This runs during atexit, so we wrap everything in try/except
    to handle interpreter shutdown gracefully (some libraries like
    boto3 register their own atexit handlers which can fail if
    imported during shutdown).
    """
    try:
        run_dir = get_run_dir()
        manifest_path = run_dir / "manifest.json"
        if not manifest_path.exists():
            return
        data = json.loads(manifest_path.read_text(encoding="utf-8"))
        data["format_version"] = BUNDLE_FORMAT_VERSION
        data["env_fingerprint"] = {
            "python_version": platform.python_version(),
            "python_implementation": platform.python_implementation(),
            "platform": f"{sys.platform}-{platform.machine()}",
            "installed_packages": _installed_packages(),
            "env_vars_read": env_patch.env_fingerprint(),
        }

        # Run unsupported detection at end of capture
        detection = unsupported_detect.get_full_detection()
        data["unsupported_detected"] = detection["unsupported_detected"]
        data["async_detected"] = detection["async_info"]["async_detected"]
        data["async_info"] = detection["async_info"]
        data["threading_info"] = detection["threading_info"]
        data["has_blocking_unsupported"] = detection["has_blocking_unsupported"]

        # Add primary unsupported reason if any
        primary_reason = unsupported_detect.get_primary_unsupported_reason(detection)
        if primary_reason:
            data["unsupported_reason"] = primary_reason

        # Add async task graph stats
        task_graph_stats = async_patch.get_task_graph_stats()
        data["async_task_graph_stats"] = task_graph_stats
        data["async_risk_level"] = task_graph_stats["async_risk_level"]

        # Add files read info
        data["files_read"] = file_patch.get_recorded_files()

        # Record whether PII redaction is available in this environment.
        # When the autodebug.redaction module is not installed, the probe
        # captures raw data without scrubbing — flag this in the manifest
        # so downstream consumers know the bundle may contain PII.
        from autodebug_probe.recorder import REDACTION_ENABLED
        data["pii_redaction"] = {
            "redaction_available": REDACTION_ENABLED,
            "redaction_disabled": not REDACTION_ENABLED,
        }

        # Record patch activation status
        data["patch_status"] = {
            "http": http_patch is not None,
            "sql": sql_patch is not None,
            "redis": redis_patch is not None,
            "grpc": grpc_patch is not None,
            "boto3": boto3_patch is not None,
            "pymongo": pymongo_patch is not None,
            "aiohttp": aiohttp_patch is not None,
            "asyncpg": asyncpg_patch is not None,
            "aiomysql": aiomysql_patch is not None,
            "aiosqlite": aiosqlite_patch is not None,
            "celery": celery_patch is not None,
            "env": env_patch is not None,
            "exception": exception_patch is not None,
            "time": time_patch is not None,
            "file": file_patch is not None,
            "async": async_patch is not None,
        }

        # Add SQLAlchemy detection info
        try:
            sqlalchemy_info = sqlalchemy_detect.get_sqlalchemy_info()
            if sqlalchemy_info["detected"]:
                data["sqlalchemy_info"] = sqlalchemy_info
        except (RuntimeError, ImportError):
            pass

        # Add Redis detection info
        try:
            redis_info = redis_patch.get_redis_info()
            if redis_info["detected"]:
                data["redis_info"] = redis_info
        except (RuntimeError, ImportError):
            pass

        # Add aiohttp detection info
        try:
            aiohttp_info = aiohttp_patch.get_aiohttp_info()
            if aiohttp_info["detected"]:
                data["aiohttp_info"] = aiohttp_info
        except (RuntimeError, ImportError):
            pass

        # Add gRPC detection info
        try:
            grpc_info = grpc_patch.get_grpc_info()
            if grpc_info["detected"]:
                data["grpc_info"] = grpc_info
        except (RuntimeError, ImportError):
            pass

        # Add boto3 detection info
        try:
            boto3_info = boto3_patch.get_boto3_info()
            if boto3_info["detected"]:
                data["boto3_info"] = boto3_info
        except (RuntimeError, ImportError):
            pass

        # Add pymongo detection info
        try:
            pymongo_info = pymongo_patch.get_pymongo_info()
            if pymongo_info["detected"]:
                data["pymongo_info"] = pymongo_info
        except (RuntimeError, ImportError):
            pass

        manifest_path.write_text(json.dumps(data, indent=2, sort_keys=True), encoding="utf-8")
    except Exception as exc:
        # Log to stderr during interpreter shutdown — regular logging may be torn down
        # Note: use module-level sys (line 8), do NOT re-import locally during atexit
        try:
            print(f"[hikaflow-probe] atexit manifest update failed: {exc}", file=sys.stderr)
        except Exception:
            pass


def initialize() -> None:
    global _initialized
    if _initialized:
        return
    _initialized = True
    recorder = get_recorder()
    recorder.events.open()
    recorder.deps_http.open()
    recorder.deps_sql.open()
    recorder.deps_redis.open()
    recorder.deps_grpc.open()
    recorder.deps_aws.open()
    recorder.deps_mongodb.open()
    recorder.time_rng.open()
    recorder.async_task_graph.open()
    env_patch.install()
    exception_patch.install()
    time_patch.install()
    file_patch.install()
    http_patch.install_requests()
    http_patch.install_httpx()
    sql_patch.install_psycopg2()
    sql_patch.install_psycopg()

    # Install Redis patches
    redis_patch.install()

    # Install aiohttp patches
    aiohttp_patch.install()

    # Install gRPC patches
    grpc_patch.install()

    # Install boto3/botocore patches
    boto3_patch.install()

    # Install pymongo patches
    pymongo_patch.install()

    # Install asyncpg patches
    asyncpg_patch.install_asyncpg()

    # Install aiomysql patches
    aiomysql_patch.install()

    # Install aiosqlite patches
    aiosqlite_patch.install()

    # Install celery patches
    celery_patch.install_celery()

    # Install async patches
    async_patch.install_all_async_patches()

    # Install SQLAlchemy patches
    sqlalchemy_detect.install_all_sqlalchemy_patches()

    atexit.register(_update_manifest)
    atexit.register(lambda: get_recorder().close())
