# Copyright (C) 2022-2026  The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU Affero General Public License version 3, or any later version
# See top-level LICENSE file for more information

import json
from typing import Any, Dict, List

from django.contrib.auth.decorators import user_passes_test
from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist
from django.core.paginator import Paginator
from django.db.models import Q
from django.http.request import HttpRequest
from django.http.response import HttpResponse, HttpResponseForbidden, JsonResponse
from django.shortcuts import render

from swh.web.add_forge_now.api_views import (
    AddForgeNowRequestActorRole,
    AddForgeNowRequestForm,
    AddForgeNowRequestHistory,
    AddForgeNowRequestPublicSerializer,
    AddForgeNowRequestSerializer,
    AddForgeNowRequestStatus,
)
from swh.web.add_forge_now.models import Request as AddForgeRequest
from swh.web.add_forge_now.models import RequestHistory
from swh.web.auth.utils import is_add_forge_now_moderator
from swh.web.utils import datatables_order_params, datatables_pagination_params


def add_forge_request_list_datatables(request: HttpRequest) -> HttpResponse:
    """Dedicated endpoint used by datatables to display the add-forge
    requests in the Web UI.
    """

    draw = int(request.GET.get("draw", 0))

    add_forge_requests = AddForgeRequest.objects.all()

    table_data: Dict[str, Any] = {
        "recordsTotal": add_forge_requests.count(),
        "draw": draw,
    }

    search_value = request.GET.get("search[value]")

    field_order = datatables_order_params(request, "id", "desc")

    add_forge_requests = add_forge_requests.order_by(*field_order)

    per_page, page_num = datatables_pagination_params(request)

    if search_value:
        add_forge_requests = add_forge_requests.filter(
            Q(forge_type__icontains=search_value)
            | Q(forge_url__icontains=search_value)
            | Q(status__icontains=search_value)
        )

    if (
        int(request.GET.get("user_requests_only", "0"))
        and request.user.is_authenticated
    ):
        add_forge_requests = add_forge_requests.filter(
            submitter_name=request.user.username
        )

    paginator = Paginator(add_forge_requests, per_page)
    page = paginator.page(page_num)

    if is_add_forge_now_moderator(request.user):
        requests = AddForgeNowRequestSerializer(page.object_list, many=True).data
    else:
        requests = AddForgeNowRequestPublicSerializer(page.object_list, many=True).data

    results = [dict(req) for req in requests]
    table_data["recordsFiltered"] = add_forge_requests.count()
    table_data["data"] = results
    return JsonResponse(table_data)


FORGE_TYPES: List[str] = [
    "bitbucket",
    "cgit",
    "forgejo",
    "gitea",
    "gitiles",
    "gitlab",
    "gitweb",
    "gogs",
    "heptapod",
    "stagit",
]


def create_request_create(request):
    """View to create a new 'add_forge_now' request."""

    submit_status = ""
    alert_level = ""
    status_code = 200

    if request.method == "POST":
        if not request.user.is_authenticated:
            return HttpResponseForbidden(
                "You must be authenticated to update a new add-forge request"
            )
        add_forge_request = AddForgeRequest()
        form = AddForgeNowRequestForm(request.POST, instance=add_forge_request)

        if form.errors:
            status_code = 400
            alert_level = "danger"
            submit_status = json.dumps(form.errors)
        else:
            try:
                existing_request = AddForgeRequest.objects.get(
                    forge_url=add_forge_request.forge_url
                )
            except ObjectDoesNotExist:
                pass
            else:
                status_code = 409
                submit_status = (
                    f"Request for forge already exists (id {existing_request.id})"
                )
                alert_level = "danger"

            if not submit_status:
                assert isinstance(request.user, User)
                add_forge_request.submitter_name = request.user.username
                add_forge_request.submitter_email = request.user.email

                form.save()

                request_history = AddForgeNowRequestHistory()
                request_history.request = add_forge_request
                request_history.new_status = AddForgeNowRequestStatus.PENDING.name
                request_history.actor = request.user.username
                request_history.actor_role = AddForgeNowRequestActorRole.SUBMITTER.name
                request_history.save()

                add_forge_request.last_modified_date = request_history.date
                add_forge_request.save()

                submit_status = "Your request has been submitted"
                alert_level = "success"

    return render(
        request,
        "add-forge-creation-form.html",
        {
            "forge_types": FORGE_TYPES,
            "submit_status": submit_status,
            "alert_level": alert_level,
        },
        status=status_code,
    )


def create_request_list(request):
    """View to list existing 'add_forge_now' requests."""

    user_requests = []

    if request.user.is_authenticated and "no_js" in request.GET:
        user_requests = AddForgeRequest.objects.filter(
            submitter_name=request.user.username
        ).order_by("-submission_date")

    return render(
        request,
        "add-forge-list.html",
        {
            "user_requests": [
                AddForgeNowRequestPublicSerializer(user_request).data
                for user_request in user_requests
            ]
        },
    )


def create_request_help(request):
    """View to explain 'add_forge_now'."""

    return render(
        request,
        "add-forge-help.html",
    )


@user_passes_test(is_add_forge_now_moderator)
def create_request_message_source(request: HttpRequest, id: int) -> HttpResponse:
    """View to retrieve the message source for a given request history entry"""

    try:
        history_entry = RequestHistory.objects.select_related("request").get(
            pk=id, message_source__isnull=False
        )
        assert history_entry.message_source is not None
    except RequestHistory.DoesNotExist:
        return HttpResponse(status=404)

    response = HttpResponse(
        bytes(history_entry.message_source), content_type="text/email"
    )
    filename = f"add-forge-now-{history_entry.request.forge_domain}-message{id}.eml"

    response["Content-Disposition"] = f'attachment; filename="{filename}"'

    return response
