"""Module containing bug report helper(s)."""

from __future__ import annotations

import json
import platform

try:
    import ssl
except ImportError:
    ssl = None  # type: ignore

import sys
import warnings
from json import JSONDecodeError

import charset_normalizer
import h11

try:
    import idna  # type: ignore[import-not-found]
except ImportError:
    idna = None  # type: ignore[assignment]

import jh2  # type: ignore
import wassima

from . import HTTPError, RequestException, Session
from . import __version__ as niquests_version
from ._compat import HAS_LEGACY_URLLIB3

if HAS_LEGACY_URLLIB3 is True:
    import urllib3_future as urllib3

    try:
        from urllib3 import __version__ as __legacy_urllib3_version__
    except (ImportError, AttributeError):
        __legacy_urllib3_version__ = None  # type: ignore[assignment]
else:
    import urllib3  # type: ignore[no-redef]

    __legacy_urllib3_version__ = None  # type: ignore[assignment]

try:
    import qh3  # type: ignore
except ImportError:
    qh3 = None  # type: ignore

try:
    import certifi  # type: ignore
except ImportError:
    certifi = None  # type: ignore

try:
    from .extensions.revocation._ocsp import verify as ocsp_verify
except ImportError:
    ocsp_verify = None  # type: ignore

try:
    import wsproto  # type: ignore[import-not-found]
except ImportError:
    wsproto = None  # type: ignore


_IS_GIL_DISABLED: bool = hasattr(sys, "_is_gil_enabled") and sys._is_gil_enabled() is False


def _implementation():
    """Return a dict with the Python implementation and version.

    Provide both the name and the version of the Python implementation
    currently running. For example, on CPython 3.10.3 it will return
    {'name': 'CPython', 'version': '3.10.3'}.

    This function works best on CPython and PyPy: in particular, it probably
    doesn't work for Jython or IronPython. Future investigation should be done
    to work out the correct shape of the code for those platforms.
    """
    implementation = platform.python_implementation()

    if implementation == "CPython":
        implementation_version = platform.python_version()
    elif implementation == "PyPy":
        implementation_version = (
            f"{sys.pypy_version_info.major}"  # type: ignore[attr-defined]
            f".{sys.pypy_version_info.minor}"  # type: ignore[attr-defined]
            f".{sys.pypy_version_info.micro}"  # type: ignore[attr-defined]
        )
        if sys.pypy_version_info.releaselevel != "final":  # type: ignore[attr-defined]
            implementation_version = "".join(
                [implementation_version, sys.pypy_version_info.releaselevel]  # type: ignore[attr-defined]
            )
    elif implementation == "Jython":
        implementation_version = platform.python_version()  # Complete Guess
    elif implementation == "IronPython":
        implementation_version = platform.python_version()  # Complete Guess
    else:
        implementation_version = "Unknown"

    return {"name": implementation, "version": implementation_version}


def info():
    """Generate information for a bug report."""
    try:
        platform_info = {
            "system": platform.system(),
            "release": platform.release(),
        }
    except OSError:
        platform_info = {
            "system": "Unknown",
            "release": "Unknown",
        }

    implementation_info = _implementation()
    urllib3_info = {
        "version": urllib3.__version__,
        "cohabitation_version": __legacy_urllib3_version__,
    }

    charset_normalizer_info = {"version": charset_normalizer.__version__}

    idna_info = {
        "version": getattr(idna, "__version__", "N/A"),
    }

    if ssl is not None:
        system_ssl = ssl.OPENSSL_VERSION_NUMBER

        system_ssl_info = {
            "version": f"{system_ssl:x}" if system_ssl is not None else "N/A",
            "name": ssl.OPENSSL_VERSION,
        }
    else:
        system_ssl_info = {"version": "N/A", "name": "N/A"}

    return {
        "platform": platform_info,
        "implementation": implementation_info,
        "system_ssl": system_ssl_info,
        "gil": not _IS_GIL_DISABLED,
        "urllib3.future": urllib3_info,
        "charset_normalizer": charset_normalizer_info,
        "idna": idna_info,
        "niquests": {
            "version": niquests_version,
        },
        "http3": {
            "enabled": qh3 is not None,
            "qh3": qh3.__version__ if qh3 is not None else None,
        },
        "http2": {
            "jh2": jh2.__version__,
        },
        "http1": {
            "h11": h11.__version__,
        },
        "wassima": {
            "version": wassima.__version__,
        },
        "ocsp": {"enabled": ocsp_verify is not None},
        "websocket": {
            "enabled": wsproto is not None,
            "wsproto": wsproto.__version__ if wsproto is not None else None,
        },
    }


pypi_session = Session()


def check_update(package_name: str, actual_version: str) -> None:
    """
    Small and concise utility to check for updates.
    """
    try:
        response = pypi_session.get(f"https://pypi.org/pypi/{package_name}/json")
        package_info = response.raise_for_status().json()

        if isinstance(package_info, dict) and "info" in package_info and "version" in package_info["info"]:
            if package_info["info"]["version"] != actual_version:
                warnings.warn(
                    f"You are using {package_name} {actual_version} and "
                    f"PyPI yield version ({package_info['info']['version']}) as the stable one. "
                    "We invite you to install this version as soon as possible. "
                    f"Run `python -m pip install {package_name} -U`.",
                    UserWarning,
                )
    except (RequestException, JSONDecodeError, HTTPError):
        pass


PACKAGE_TO_CHECK_FOR_UPGRADE = {
    "niquests": niquests_version,
    "urllib3-future": urllib3.__version__,
    "qh3": qh3.__version__ if qh3 is not None else None,
    "jh2": jh2.__version__,
    "h11": h11.__version__,
    "charset-normalizer": charset_normalizer.__version__,
    "wassima": wassima.__version__,
    "idna": idna.__version__ if idna is not None else None,
    "wsproto": wsproto.__version__ if wsproto is not None else None,
}


def main() -> None:
    """Pretty-print the bug information as JSON."""
    for package, actual_version in PACKAGE_TO_CHECK_FOR_UPGRADE.items():
        if actual_version is None:
            continue
        check_update(package, actual_version)

    if __legacy_urllib3_version__ is not None:
        warnings.warn(
            "urllib3-future is installed alongside (legacy) urllib3. This may cause compatibility issues. "
            "Some (Requests) 3rd parties may be bound to urllib3, therefor the plugins may wrongfully invoke "
            "urllib3 (legacy) instead of urllib3-future. To remediate this, run "
            "`python -m pip uninstall -y urllib3 urllib3-future`, then run `python -m pip install urllib3-future`.",
            UserWarning,
        )

    print(json.dumps(info(), sort_keys=True, indent=2))


if __name__ == "__main__":
    main()
