import warnings
from typing import Any

from django.db.transaction import non_atomic_requests
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.shortcuts import render
from django.utils.translation import gettext as _

from watchman import __version__, settings
from watchman.decorators import auth
from watchman.utils import get_checks

WATCHMAN_VERSION_HEADER: str = "X-Watchman-Version"


def _get_check_params(
    request: HttpRequest,
) -> tuple[list[str] | None, list[str] | None]:
    check_list: list[str] | None = None
    skip_list: list[str] | None = None

    if len(request.GET) > 0:
        if "check" in request.GET:
            check_list = request.GET.getlist("check")
        if "skip" in request.GET:
            skip_list = request.GET.getlist("skip")

    return (check_list, skip_list)


def _deprecation_warnings() -> None:
    if settings.WATCHMAN_TOKEN:
        warnings.warn(
            "`WATCHMAN_TOKEN` setting is deprecated, use `WATCHMAN_TOKENS` instead. It will be removed in django-watchman 1.0",
            DeprecationWarning,
            stacklevel=2,
        )


def _disable_apm() -> None:
    # New Relic
    try:
        import newrelic.agent

        newrelic.agent.ignore_transaction(flag=True)
    except ImportError:
        pass
    # Datadog
    try:
        from ddtrace import tracer
        from ddtrace.constants import MANUAL_DROP_KEY

        tracer.current_span().set_tag(MANUAL_DROP_KEY)
    except (AttributeError, ImportError):
        pass


def run_checks(request: HttpRequest) -> tuple[dict[str, Any], bool]:
    _deprecation_warnings()

    if settings.WATCHMAN_DISABLE_APM:
        _disable_apm()

    checks: dict[str, Any] = {}
    ok: bool = True

    check_list, skip_list = _get_check_params(request)

    for check in get_checks(check_list=check_list, skip_list=skip_list):
        if callable(check):
            _check = check()
            # Set our HTTP status code if there were any errors
            if settings.WATCHMAN_ERROR_CODE != 200:
                for _type in _check:
                    if isinstance(_check[_type], dict):
                        result = _check[_type]
                        if not result["ok"]:
                            ok = False
                    elif isinstance(_check[_type], list):
                        for entry in _check[_type]:
                            for result in entry:
                                if not entry[result]["ok"]:
                                    ok = False
            checks.update(_check)

    return checks, ok


@auth
@non_atomic_requests
def status(request: HttpRequest) -> HttpResponse:
    checks, ok = run_checks(request)

    if not checks:
        response = JsonResponse(
            {
                "error": 404,
                "message": _("No checks found"),
            },
            status=404,
        )
    else:
        http_code = 200 if ok else settings.WATCHMAN_ERROR_CODE
        response = JsonResponse(checks, status=http_code)

    if settings.EXPOSE_WATCHMAN_VERSION:
        response[WATCHMAN_VERSION_HEADER] = __version__

    return response


@non_atomic_requests
def bare_status(request: HttpRequest) -> HttpResponse:
    checks, ok = run_checks(request)
    http_code = 200 if ok else settings.WATCHMAN_ERROR_CODE
    return HttpResponse(status=http_code, content_type="text/plain")


def ping(request: HttpRequest) -> HttpResponse:
    if settings.WATCHMAN_DISABLE_APM:
        _disable_apm()
    return HttpResponse("pong", content_type="text/plain")


@auth
@non_atomic_requests
def dashboard(request: HttpRequest) -> HttpResponse:
    checks, overall_status = run_checks(request)

    expanded_checks: dict[str, Any] = {}
    for key, value in checks.items():
        if isinstance(value, dict):
            # For some systems (eg: email, storage) value is a
            # dictionary of status
            #
            # Example:
            # {
            #     'ok': True,  # Status
            # }
            #
            # Example:
            # {
            #     'ok': False,  # Status
            #     'error': "RuntimeError",
            #     'stacktrace': "...",
            # }
            single_status = value.copy()
            single_status["name"] = ""
            expanded_check: dict[str, Any] = {
                "ok": value["ok"],
                "statuses": [single_status],
            }
        else:
            # For other systems (eg: cache, database) value is a
            # list of dictionaries of dictionaries of statuses
            #
            # Example:
            # [
            #     {
            #         'default': {  # Cache/database name
            #             'ok': True,  # Status
            #         }
            #     },
            #     {
            #         'non-default': {  # Cache/database name
            #             'ok': False,  # Status
            #             'error': "RuntimeError",
            #             'stacktrace': "...",
            #         }
            #     },
            # ]
            statuses: list[dict[str, Any]] = []
            for outer_status in value:
                for name, inner_status in outer_status.items():
                    detail = inner_status.copy()
                    detail["name"] = name
                    statuses.append(detail)

            expanded_check = {
                "ok": all(detail["ok"] for detail in statuses),
                "statuses": statuses,
            }
        expanded_checks[key] = expanded_check

    response = render(
        request,
        "watchman/dashboard.html",
        {
            "checks": expanded_checks,
            "overall_status": overall_status,
            "watchman_version": __version__,
            "expose_watchman_version": settings.EXPOSE_WATCHMAN_VERSION,
        },
    )

    if settings.EXPOSE_WATCHMAN_VERSION:
        response[WATCHMAN_VERSION_HEADER] = __version__

    return response
