from django.http import HttpResponseBadRequest, JsonResponse
from django.template.loader import render_to_string
from django.views.decorators.csrf import csrf_exempt

from debug_toolbar._compat import login_not_required
from debug_toolbar.decorators import render_with_toolbar_language, require_show_toolbar
from debug_toolbar.forms import SignedDataForm
from debug_toolbar.panels.sql.forms import SQLSelectForm


def get_signed_data(request):
    """Unpack a signed data form, if invalid returns None"""
    data = request.GET if request.method == "GET" else request.POST
    signed_form = SignedDataForm(data)
    if signed_form.is_valid():
        return signed_form.verified_data()
    return None


@csrf_exempt
@login_not_required
@require_show_toolbar
@render_with_toolbar_language
def sql_select(request):
    """Returns the output of the SQL SELECT statement"""
    verified_data = get_signed_data(request)
    if not verified_data:
        return HttpResponseBadRequest("Invalid signature")
    form = SQLSelectForm(verified_data)

    if form.is_valid():
        sql = form.cleaned_data["raw_sql"]
        params = form.cleaned_data["params"]
        with form.cursor as cursor:
            cursor.execute(sql, params)
            headers = [d[0] for d in cursor.description]
            result = cursor.fetchall()

        context = {
            "result": result,
            "sql": form.reformat_sql(),
            "duration": form.cleaned_data["duration"],
            "headers": headers,
            "alias": form.cleaned_data["alias"],
        }
        content = render_to_string("debug_toolbar/panels/sql_select.html", context)
        return JsonResponse({"content": content})
    return HttpResponseBadRequest("Form errors")


@csrf_exempt
@login_not_required
@require_show_toolbar
@render_with_toolbar_language
def sql_explain(request):
    """Returns the output of the SQL EXPLAIN on the given query"""
    verified_data = get_signed_data(request)
    if not verified_data:
        return HttpResponseBadRequest("Invalid signature")
    form = SQLSelectForm(verified_data)

    if form.is_valid():
        sql = form.cleaned_data["raw_sql"]
        params = form.cleaned_data["params"]
        vendor = form.connection.vendor
        with form.cursor as cursor:
            if vendor == "sqlite":
                # SQLite's EXPLAIN dumps the low-level opcodes generated for a query;
                # EXPLAIN QUERY PLAN dumps a more human-readable summary
                # See https://www.sqlite.org/lang_explain.html for details
                cursor.execute(f"EXPLAIN QUERY PLAN {sql}", params)
            elif vendor == "postgresql":
                cursor.execute(f"EXPLAIN ANALYZE {sql}", params)
            else:
                cursor.execute(f"EXPLAIN {sql}", params)
            headers = [d[0] for d in cursor.description]
            result = cursor.fetchall()

        context = {
            "result": result,
            "sql": form.reformat_sql(),
            "duration": form.cleaned_data["duration"],
            "headers": headers,
            "alias": form.cleaned_data["alias"],
        }
        content = render_to_string("debug_toolbar/panels/sql_explain.html", context)
        return JsonResponse({"content": content})
    return HttpResponseBadRequest("Form errors")


@csrf_exempt
@login_not_required
@require_show_toolbar
@render_with_toolbar_language
def sql_profile(request):
    """Returns the output of running the SQL and getting the profiling statistics"""
    verified_data = get_signed_data(request)
    if not verified_data:
        return HttpResponseBadRequest("Invalid signature")
    form = SQLSelectForm(verified_data)

    if form.is_valid():
        sql = form.cleaned_data["raw_sql"]
        params = form.cleaned_data["params"]
        result = None
        headers = None
        result_error = None
        with form.cursor as cursor:
            try:
                cursor.execute("SET PROFILING=1")  # Enable profiling
                cursor.execute(sql, params)  # Execute SELECT
                cursor.execute("SET PROFILING=0")  # Disable profiling
                # The Query ID should always be 1 here but I'll subselect to get
                # the last one just in case...
                cursor.execute(
                    """
                    SELECT *
                    FROM information_schema.profiling
                    WHERE query_id = (
                        SELECT query_id
                        FROM information_schema.profiling
                        ORDER BY query_id DESC
                        LIMIT 1
                    )
                    """
                )
                headers = [d[0] for d in cursor.description]
                result = cursor.fetchall()
            except Exception:
                result_error = (
                    "Profiling is either not available or not supported by your "
                    "database."
                )

        context = {
            "result": result,
            "result_error": result_error,
            "sql": form.reformat_sql(),
            "duration": form.cleaned_data["duration"],
            "headers": headers,
            "alias": form.cleaned_data["alias"],
        }
        content = render_to_string("debug_toolbar/panels/sql_profile.html", context)
        return JsonResponse({"content": content})
    return HttpResponseBadRequest("Form errors")
