from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.db.models import Case, When
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.views.decorators.http import require_GET, require_http_methods

from NEMO.decorators import staff_member_required
from NEMO.forms import SafetyIssueCreationForm, SafetyIssueUpdateForm
from NEMO.models import Chemical, ChemicalHazard, Notification, SafetyCategory, SafetyIssue, SafetyItem
from NEMO.templatetags.custom_tags_and_filters import navigation_url
from NEMO.utilities import (
    BasicDisplayTable,
    EmailCategory,
    distinct_qs_value_list,
    export_format_datetime,
    get_full_url,
    queryset_search_filter,
    render_email_template,
    send_mail,
)
from NEMO.views.customization import EmailsCustomization, SafetyCustomization, get_media_file_contents
from NEMO.views.notifications import create_safety_notification, delete_notification, get_notifications


@login_required
@require_GET
def safety(request):
    safety_items_expand_categories = SafetyCustomization.get_bool("safety_items_expand_categories")
    dictionary = safety_dictionary("")
    if not dictionary["show_safety"]:
        if not dictionary["show_safety_issues"]:
            if not dictionary["show_safety_data_sheets"]:
                return redirect("safety_issues")
            return redirect("safety_data_sheets")
        return redirect("safety_issues")
    return redirect("safety_categories") if not safety_items_expand_categories else redirect("safety_all_in_one")


@login_required
@require_GET
def safety_all_in_one(request):
    dictionary = safety_dictionary("safety")
    dictionary["safety_categories"] = SafetyCategory.objects.filter(
        id__in=distinct_qs_value_list(SafetyItem.objects.all(), "category_id")
    )
    dictionary["safety_general"] = SafetyItem.objects.filter(category__isnull=True)
    dictionary["safety_items_expand_categories"] = True
    return render(request, "safety/safety.html", dictionary)


@login_required
@require_GET
def safety_categories(request, category_id=None):
    dictionary = safety_dictionary("safety")
    try:
        safety_item_id = request.GET.get("safety_item_id")
        if safety_item_id:
            category_id = SafetyItem.objects.get(pk=safety_item_id).category_id
        SafetyCategory.objects.get(pk=category_id)
    except SafetyCategory.DoesNotExist:
        pass
    safety_items_qs = SafetyItem.objects.filter(category_id=category_id)
    if not category_id and not safety_items_qs.exists():
        first_category = SafetyCategory.objects.first()
        category_id = first_category.id if first_category else None
    dictionary.update(
        {
            "category_id": category_id,
            "safety_items": SafetyItem.objects.filter(category_id=category_id),
            "safety_categories": SafetyCategory.objects.filter(
                id__in=distinct_qs_value_list(SafetyItem.objects.all(), "category_id")
            ),
            "safety_general": SafetyItem.objects.filter(category_id__isnull=True).exists(),
        }
    )
    return render(request, "safety/safety.html", dictionary)


@login_required
@require_GET
def safety_item(request, safety_item_id: int):
    # Redirect to the appropriate URL with hashtag included to scroll to the item
    safety_items_expand_categories = SafetyCustomization.get_bool("safety_items_expand_categories")
    url_params = f"?safety_item_id={safety_item_id}#safety_item_{safety_item_id}"
    redirect_url = reverse("safety_categories") if not safety_items_expand_categories else reverse("safety_all_in_one")
    return redirect(redirect_url + url_params)


@login_required
@require_GET
def safety_items_search(request):
    return queryset_search_filter(SafetyItem.objects.all(), ["name", "description"], request)


@login_required
@require_http_methods(["GET", "POST"])
def safety_issues(request):
    dictionary = safety_dictionary("safety_issues")
    tickets = SafetyIssue.objects.filter(resolved=False).order_by("-creation_time")
    if not request.user.is_staff:
        tickets = tickets.filter(visible=True)
    dictionary["tickets"] = tickets
    dictionary["notifications"] = get_notifications(request.user, Notification.Types.SAFETY)
    return render(request, "safety/safety_issues.html", dictionary)


@login_required
@require_http_methods(["GET", "POST"])
def create_safety_issue(request):
    dictionary = safety_dictionary("safety_issues")
    if request.method == "POST":
        form = SafetyIssueCreationForm(request.user, data=request.POST)
        if form.is_valid():
            issue = form.save()
            send_safety_email_notification(request, issue)
            create_safety_notification(issue)
            messages.success(request, "Your safety concern was sent to the staff and will be addressed promptly")
            return redirect("safety_issues")
    return render(request, "safety/safety_issues_create.html", dictionary)


def send_safety_email_notification(request, issue):
    recipient = EmailsCustomization.get("safety_email_address")
    message = get_media_file_contents("safety_issue_email.html")
    if recipient and message:
        subject = "Safety issue"
        dictionary = {"issue": issue, "issue_absolute_url": get_full_url(issue.get_absolute_url(), request)}
        rendered_message = render_email_template(message, dictionary, request)
        from_email = issue.reporter.email if issue.reporter else recipient
        send_mail(
            subject=subject,
            content=rendered_message,
            from_email=from_email,
            to=[recipient],
            email_category=EmailCategory.SAFETY,
        )


@login_required
@require_GET
def resolved_safety_issues(request):
    dictionary = safety_dictionary("safety_issues")
    tickets = SafetyIssue.objects.filter(resolved=True)
    if not request.user.is_staff:
        tickets = tickets.filter(visible=True)
    dictionary["tickets"] = tickets
    return render(request, "safety/safety_issues_resolved.html", dictionary)


@staff_member_required
@require_http_methods(["GET", "POST"])
def update_safety_issue(request, ticket_id):
    dictionary = safety_dictionary("safety_issues")
    if request.method == "POST":
        ticket = get_object_or_404(SafetyIssue, id=ticket_id)
        form = SafetyIssueUpdateForm(request.user, data=request.POST, instance=ticket)
        if form.is_valid():
            issue = form.save()
            if issue.resolved:
                delete_notification(Notification.Types.SAFETY, issue.id)
            messages.success(request, "This safety issue was updated successfully")
            return redirect("safety_issues")
    dictionary["ticket"] = get_object_or_404(SafetyIssue, id=ticket_id)
    return render(request, "safety/safety_issues_update.html", dictionary)


@login_required
@require_GET
def safety_data_sheets(request):
    chemicals = Chemical.objects.all().prefetch_related("hazards").order_by()
    hazards = ChemicalHazard.objects.all()

    for hazard in hazards:
        chemicals = chemicals.annotate(
            **{f"hazard_{hazard.id}": Case(When(hazards__in=[hazard.id], then=True), default=False)}
        )

    order_by = request.GET.get("o", "name")
    reverse_order = order_by.startswith("-")
    order = order_by[1:] if reverse_order else order_by
    chemicals = list(set(chemicals))
    if order == "name":
        chemicals.sort(key=lambda x: x.name.lower(), reverse=reverse_order)
    elif order.startswith("hazard_"):
        hazard_id = int(order[7:])
        chemicals.sort(key=lambda x: x.name.lower())
        chemicals.sort(key=lambda x: hazard_id in [h.id for h in x.hazards.all()], reverse=not reverse_order)

    dictionary = safety_dictionary("safety_data_sheets")
    dictionary.update(
        {
            "chemicals": chemicals,
            "hazards": hazards,
            "order_by": order_by,
            "search_keywords": SafetyCustomization.get_bool("safety_data_sheets_keywords_default"),
        }
    )

    return render(request, "safety/safety_data_sheets.html", dictionary)


@staff_member_required
@require_GET
def export_safety_data_sheets(request):
    hazards = ChemicalHazard.objects.all()

    table = BasicDisplayTable()
    table.add_header(("name", "Name"))
    for hazard in hazards:
        table.add_header((f"hazard_{hazard.id}", hazard.name))
    table.add_header(("keywords", "Keywords"))

    for chemical in Chemical.objects.all():
        chemical: Chemical = chemical
        values = {f"hazard_{hazard.id}": "X" for hazard in hazards if hazard in chemical.hazards.all()}
        values["name"] = chemical.name
        values["keywords"] = chemical.keywords
        table.add_row(values)

    response = table.to_csv()
    filename = f"safety_data_sheets_{export_format_datetime()}.csv"
    response["Content-Disposition"] = f'attachment; filename="{filename}"'
    return response


def safety_dictionary(tab):
    sds_url_exist = navigation_url("safety_data_sheets", "")
    dictionary = dict(
        show_safety=SafetyCustomization.get_bool("safety_show_safety"),
        show_safety_issues=SafetyCustomization.get_bool("safety_show_safety_issues"),
        show_safety_data_sheets=SafetyCustomization.get_bool("safety_show_safety_data_sheets") and sds_url_exist,
    )
    dictionary["show_tabs"] = len([key for key, value in dictionary.items() if value])
    dictionary["tab"] = tab
    if tab == "safety_issues":
        dictionary["safety_introduction"] = get_media_file_contents("safety_introduction.html")
    return dictionary
