"""
views.py

This module contains the view functions for handling HTTP requests and rendering
responses in your application.

Each view function corresponds to a specific URL route and performs the necessary
actions to handle the request, process data, and generate a response.

This module is part of the recruitment project and is intended to
provide the main entry points for interacting with the application's functionality.
"""

import contextlib
import json
import logging

from django.contrib import messages
from django.contrib.auth.models import Permission
from django.core import serializers
from django.core.mail import send_mail
from django.core.paginator import Paginator
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.shortcuts import redirect, render
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.views.decorators.http import require_http_methods

from base.backends import ConfiguredEmailBackend
from base.methods import sortby
from employee.models import Employee
from horilla import settings
from horilla.decorators import hx_request_required, login_required, permission_required
from notifications.signals import notify
from recruitment.decorators import manager_can_enter, recruitment_manager_can_enter
from recruitment.filters import CandidateFilter, RecruitmentFilter, StageFilter
from recruitment.forms import (
    ApplicationForm,
    CandidateCreationForm,
    CandidateDropDownForm,
    RecruitmentCreationForm,
    RecruitmentDropDownForm,
    StageCreationForm,
    StageDropDownForm,
    StageNoteForm,
)
from recruitment.methods import recruitment_manages
from recruitment.models import Candidate, Recruitment, Stage, StageNote

logger = logging.getLogger(__name__)


def is_stagemanager(request, stage_id=False):
    """
    This method is used to identify the employee is a stage manager or
    not, if stage_id is passed through args, method will
    check the employee is manager to the corresponding stage, return
    tuple with boolean and all stages that employee is manager.
    if called this method without stage_id args it will return boolean
     with all the stage that the employee is stage manager
    Args:
        request : django http request
        stage_id : stage instance id
    """
    user = request.user
    employee = user.employee_get
    if not stage_id:
        return (
            employee.stage_set.exists() or user.is_superuser,
            employee.stage_set.all(),
        )
    stage_obj = Stage.objects.get(id=stage_id)
    return (
        employee in stage_obj.stage_managers.all()
        or user.is_superuser
        or is_recruitmentmanager(request, rec_id=stage_obj.recruitment_id.id)[0],
        employee.stage_set.all(),
    )


def is_recruitmentmanager(request, rec_id=False):
    """
    This method is used to identify the employee is a recruitment
    manager or not, if rec_id is passed through args, method will
    check the employee is manager to the corresponding recruitment,
    return tuple with boolean and all recruitment that employee is manager.
    if called this method without recruitment args it will return
    boolean with all the recruitment that the employee is recruitment manager
    Args:
        request : django http request
        rec_id : recruitment instance id
    """
    user = request.user
    employee = user.employee_get
    if not rec_id:
        return (
            employee.recruitment_set.exists() or user.is_superuser,
            employee.recruitment_set.all(),
        )
    recruitment_obj = Recruitment.objects.get(id=rec_id)
    return (
        employee in recruitment_obj.recruitment_managers.all() or user.is_superuser,
        employee.recruitment_set.all(),
    )


def paginator_qry(qryset, page_number):
    """
    This method is used to generate common paginator limit.
    """
    paginator = Paginator(qryset, 50)
    qryset = paginator.get_page(page_number)
    return qryset


@login_required
@permission_required(perm="recruitment.add_recruitment")
def recruitment(request):
    """
    This method is used to create recruitment, when create recruitment this method
    add  recruitment view,create candidate, change stage sequence and so on, some of
    the permission is checking manually instead of using django permission permission
    to the  recruitment managers
    """
    form = RecruitmentCreationForm()
    if request.method == "POST":
        form = RecruitmentCreationForm(request.POST)
        if form.is_valid():
            recruitment_obj = form.save()
            messages.success(request, _("Recruitment added."))
            with contextlib.suppress(Exception):
                managers = recruitment_obj.recruitment_managers.select_related(
                    "employee_user_id"
                )
                users = [employee.employee_user_id for employee in managers]
                notify.send(
                    request.user.employee_get,
                    recipient=users,
                    verb="You are chosen as one of recruitment manager",
                    verb_ar="تم اختيارك كأحد مديري التوظيف",
                    verb_de="Sie wurden als einer der Personalvermittler ausgewählt",
                    verb_es="Has sido elegido/a como uno de los gerentes de contratación",
                    verb_fr="Vous êtes choisi(e) comme l'un des responsables du recrutement",
                    icon="people-circle",
                    redirect=reverse("pipeline"),
                )
            response = render(
                request, "recruitment/recruitment_form.html", {"form": form}
            )
            return HttpResponse(
                response.content.decode("utf-8") + "<script>location.reload();</script>"
            )
    return render(request, "recruitment/recruitment_form.html", {"form": form})


@login_required
@permission_required(perm="recruitment.change_recruitment")
@require_http_methods(["POST"])
def remove_recruitment_manager(request, mid, rid):
    """
    This method is used to remove selected manager from the recruitment,
    when remove the manager permissions also removed if the employee is not
    exists in more stage manager or recruitment manager

     Args:
        mid : employee manager_id in the recruitment
        rid : recruitment_id
    """
    recruitment_obj = Recruitment.objects.get(id=rid)
    manager = Employee.objects.get(id=mid)
    recruitment_obj.recruitment_managers.remove(manager)
    messages.success(request, _("Recruitment manager removed successfully."))
    notify.send(
        request.user.employee_get,
        recipient=manager.employee_user_id,
        verb=f"You are removed from recruitment manager from {recruitment_obj}",
        verb_ar=f"تمت إزالتك من وظيفة مدير التوظيف في {recruitment_obj}",
        verb_de=f"Sie wurden als Personalvermittler von {recruitment_obj} entfernt",
        verb_es=f"Has sido eliminado/a como gerente de contratación de {recruitment_obj}",
        verb_fr=f"Vous avez été supprimé(e) en tant que responsable\
                du recrutement de {recruitment_obj}",
        icon="person-remove",
        redirect="",
    )
    recruitment_queryset = Recruitment.objects.all()
    previous_data = request.environ["QUERY_STRING"]
    return render(
        request,
        "recruitment/recruitment_component.html",
        {
            "data": paginator_qry(recruitment_queryset, request.GET.get("page")),
            "pd": previous_data,
        },
    )


@login_required
@permission_required(perm="recruitment.view_recruitment")
def recruitment_view(request):
    """
    This method is used to  render all recruitment to view
    """
    if not request.GET:
        request.GET.copy().update({"is_active": "on"})
    form = RecruitmentCreationForm()
    filter_obj = RecruitmentFilter(request.GET, queryset=Recruitment.objects.all())
    return render(
        request,
        "recruitment/recruitment_view.html",
        {
            "data": paginator_qry(filter_obj.qs, request.GET.get("page")),
            "f": filter_obj,
            "form": form,
        },
    )


@login_required
@permission_required(perm="recruitment.view_recruitment")
def recruitment_search(request):
    """
    This method is used to search recruitment
    """
    filter_obj = RecruitmentFilter(request.GET)
    previous_data = request.environ["QUERY_STRING"]
    recruitment_obj = sortby(request, filter_obj.qs, "orderby")
    return render(
        request,
        "recruitment/recruitment_component.html",
        {
            "data": paginator_qry(recruitment_obj, request.GET.get("page")),
            "pd": previous_data,
        },
    )


@login_required
@permission_required(perm="recruitment.change_recruitment")
@hx_request_required
def recruitment_update(request, rec_id):
    """
    This method is used to update the recruitment, when updating the recruitment,
    any changes in manager is exists then permissions also assigned to the manager
    Args:
        id : recruitment_id
    """
    recruitment_obj = Recruitment.objects.get(id=rec_id)
    form = RecruitmentCreationForm(instance=recruitment_obj)
    if request.method == "POST":
        form = RecruitmentCreationForm(request.POST, instance=recruitment_obj)
        if form.is_valid():
            recruitment_obj = form.save()
            messages.success(request, _("Recruitment Updated."))
            response = render(
                request, "recruitment/recruitment_form.html", {"form": form}
            )
            with contextlib.suppress(Exception):
                managers = recruitment_obj.recruitment_managers.select_related(
                    "employee_user_id"
                )
                users = [employee.employee_user_id for employee in managers]
                notify.send(
                    request.user.employee_get,
                    recipient=users,
                    verb=f"{recruitment_obj} is updated, You are chosen as one of the managers",
                    verb_ar=f"{recruitment_obj} تم تحديثه، تم اختيارك كأحد المديرين",
                    verb_de=f"{recruitment_obj} wurde aktualisiert. Sie wurden als\
                            einer der Manager ausgewählt",
                    verb_es=f"{recruitment_obj} ha sido actualizado/a. Has sido elegido\
                            a como uno de los gerentes",
                    verb_fr=f"{recruitment_obj} a été mis(e) à jour. Vous êtes choisi(e) comme l'un des responsables",
                    icon="people-circle",
                    redirect=reverse("pipeline"),
                )

            return HttpResponse(
                response.content.decode("utf-8") + "<script>location.reload();</script>"
            )
    return render(request, "recruitment/recruitment_update_form.html", {"form": form})


@login_required
@permission_required(perm="recruitment.delete_recruitment")
@require_http_methods(["POST"])
def recruitment_delete(request, rec_id):
    """
    This method is used to permanently delete the recruitment
    Args:
        id : recruitment_id
    """
    recruitment_obj = Recruitment.objects.get(id=rec_id)
    recruitment_mangers = recruitment_obj.recruitment_managers.all()
    all_stage_permissions = Permission.objects.filter(
        content_type__app_label="recruitment", content_type__model="stage"
    )
    all_candidate_permissions = Permission.objects.filter(
        content_type__app_label="recruitment", content_type__model="candidate"
    )
    for manager in recruitment_mangers:
        all_this_manger = manager.recruitment_set.all()
        if len(all_this_manger) == 1:
            for stage_permission in all_candidate_permissions:
                manager.employee_user_id.user_permissions.remove(stage_permission.id)
            for candidate_permission in all_stage_permissions:
                manager.employee_user_id.user_permissions.remove(
                    candidate_permission.id
                )
    try:
        recruitment_obj.delete()
        messages.success(request, _("Recruitment deleted successfully."))
    except Exception as error:
        messages.error(request, error)
        messages.error(request, _("You cannot delete this recruitment"))
    recruitment_obj = Recruitment.objects.all()
    return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))


@login_required
@manager_can_enter(perm="recruitment.view_recruitment")
def recruitment_pipeline(request):
    """
    This method is used to filter out candidate through pipeline structure
    """
    view = request.GET.get("view")
    template = "pipeline/pipeline.html"
    if view == "card":
        template = "pipeline/pipeline_card.html"
    recruitment_form = RecruitmentDropDownForm()
    stage_form = StageDropDownForm()
    candidate_form = CandidateDropDownForm()
    recruitment_obj = Recruitment.objects.filter(is_active=True, closed=False)
    if request.method == "POST":
        if request.POST.get(
            "recruitment_managers"
        ) is not None and request.user.has_perm("add_recruitment"):
            recruitment_form = RecruitmentDropDownForm(request.POST)
            if recruitment_form.is_valid():
                recruitment_obj = recruitment_form.save()
                recruitment_form = RecruitmentDropDownForm()
                messages.success(request, _("Recruitment added."))
                with contextlib.suppress(Exception):
                    managers = recruitment_obj.recruitment_managers.select_related(
                        "employee_user_id"
                    )
                    users = [employee.employee_user_id for employee in managers]
                    notify.send(
                        request.user.employee_get,
                        recipient=users,
                        verb=f"You are chosen as recruitment manager for\
                                the recruitment {recruitment_obj}",
                        verb_ar=f"تم اختيارك كمدير توظيف للتوظيف {recruitment_obj}",
                        verb_de=f"Sie wurden als Personalvermittler für die Rekrutierung\
                                {recruitment_obj} ausgewählt",
                        verb_es=f"Has sido elegido/a como gerente de contratación para la contratación {recruitment_obj}",
                        verb_fr=f"Vous êtes choisi(e) comme responsable du recrutement pour le recrutement {recruitment_obj}",
                        icon="people-circle",
                        redirect=reverse("pipeline"),
                    )

                return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
        elif request.FILES.get("resume") is not None:
            if request.user.has_perm("add_candidate") or is_stagemanager(
                request,
            ):
                candidate_form = CandidateDropDownForm(request.POST, request.FILES)
                if candidate_form.is_valid():
                    candidate_obj = candidate_form.save()
                    candidate_form = CandidateDropDownForm()
                    with contextlib.suppress(Exception):
                        managers = candidate_obj.stage_id.stage_managers.select_related(
                            "employee_user_id"
                        )
                        users = [employee.employee_user_id for employee in managers]
                        notify.send(
                            request.user.employee_get,
                            recipient=users,
                            verb=f"New candidate arrived on stage {candidate_obj.stage_id.stage}",
                            verb_ar=f"وصل مرشح جديد إلى المرحلة {candidate_obj.stage_id.stage}",
                            verb_de=f"Neuer Kandidat ist auf der Stufe {candidate_obj.stage_id.stage} angekommen",
                            verb_es=f"Nuevo candidato llegó a la etapa {candidate_obj.stage_id.stage}",
                            verb_fr=f"Nouveau candidat arrivé à l'étape {candidate_obj.stage_id.stage}",
                            icon="person-add",
                            redirect=reverse("pipeline"),
                        )

                    messages.success(request, _("Candidate added."))
                    return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
        elif request.POST.get("stage_managers") and request.user.has_perm("add_stage"):
            stage_form = StageDropDownForm(request.POST)
            if stage_form.is_valid():
                if recruitment_manages(
                    request, stage_form.instance.recruitment_id
                ) or request.user.has_perm("recruitment.add_stage"):
                    stage_obj = stage_form.save()
                    stage_form = StageDropDownForm()
                    messages.success(request, _("Stage added."))
                    with contextlib.suppress(Exception):
                        managers = stage_obj.stage_managers.select_related(
                            "employee_user_id"
                        )
                        users = [employee.employee_user_id for employee in managers]
                        notify.send(
                            request.user.employee_get,
                            recipient=users,
                            verb=f"You are chosen as a stage manager on the stage {stage_obj.stage} in recruitment {stage_obj.recruitment_id}",
                            verb_ar=f"لقد تم اختيارك كمدير مرحلة في المرحلة {stage_obj.stage} في التوظيف {stage_obj.recruitment_id}",
                            verb_de=f"Sie wurden als Bühnenmanager für die Stufe {stage_obj.stage} in der Rekrutierung {stage_obj.recruitment_id} ausgewählt",
                            verb_es=f"Has sido elegido/a como gerente de etapa en la etapa {stage_obj.stage} en la contratación {stage_obj.recruitment_id}",
                            verb_fr=f"Vous avez été choisi(e) comme responsable de l'étape {stage_obj.stage} dans le recrutement {stage_obj.recruitment_id}",
                            icon="people-circle",
                            redirect=reverse("pipeline"),
                        )

                    return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
                messages.info(request, _("You dont have access"))
    return render(
        request,
        template,
        {
            "recruitment": recruitment_obj,
            "recruitment_form": recruitment_form,
            "stage_form": stage_form,
            "candidate_form": candidate_form,
        },
    )


@login_required
@permission_required(perm="recruitment.view_recruitment")
def recruitment_pipeline_card(request):
    """
    This method is used to render pipeline card structure.
    """
    search = request.GET.get("search")
    search = search if search is not None else ""
    recruitment_obj = Recruitment.objects.all()
    candidates = Candidate.objects.filter(name__icontains=search, is_active=True)
    stages = Stage.objects.all()
    return render(
        request,
        "pipeline/pipeline_components/pipeline_card_view.html",
        {"recruitment": recruitment_obj, "candidates": candidates, "stages": stages},
    )


@login_required
@recruitment_manager_can_enter(perm="recruitment.change_stage")
def stage_update_pipeline(request, stage_id):
    """
    This method is used to update stage from pipeline view
    """
    stage_obj = Stage.objects.get(id=stage_id)
    form = StageCreationForm(instance=stage_obj)
    if request.POST:
        form = StageCreationForm(request.POST, instance=stage_obj)
        if form.is_valid():
            stage_obj = form.save()
            messages.success(request, _("Stage updated."))
            with contextlib.suppress(Exception):
                managers = stage_obj.stage_managers.select_related("employee_user_id")
                users = [employee.employee_user_id for employee in managers]
                notify.send(
                    request.user.employee_get,
                    recipient=users,
                    verb=f"{stage_obj.stage} stage in recruitment {stage_obj.recruitment_id}\
                            is updated, You are chosen as one of the managers",
                    verb_ar=f"تم تحديث مرحلة {stage_obj.stage} في التوظيف {stage_obj.recruitment_id}\
                            ، تم اختيارك كأحد المديرين",
                    verb_de=f"Die Stufe {stage_obj.stage} in der Rekrutierung {stage_obj.recruitment_id}\
                            wurde aktualisiert. Sie wurden als einer der Manager ausgewählt",
                    verb_es=f"Se ha actualizado la etapa {stage_obj.stage} en la contratación {stage_obj.recruitment_id}.\
                            Has sido elegido/a como uno de los gerentes",
                    verb_fr=f"L'étape {stage_obj.stage} dans le recrutement {stage_obj.recruitment_id} a été mise à jour.\
                            Vous avez été choisi(e) comme l'un des responsables",
                    icon="people-circle",
                    redirect=reverse("pipeline"),
                )

            return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))

    return render(request, "pipeline/form/stage_update.html", {"form": form})


@login_required
@recruitment_manager_can_enter(perm="recruitment.change_recruitment")
def recruitment_update_pipeline(request, rec_id):
    """
    This method is used to update recruitment from pipeline view
    """
    recruitment_obj = Recruitment.objects.get(id=rec_id)
    form = RecruitmentCreationForm(instance=recruitment_obj)
    if request.POST:
        form = RecruitmentCreationForm(request.POST, instance=recruitment_obj)
        if form.is_valid():
            recruitment_obj = form.save()
            messages.success(request, _("Recruitment updated."))
            with contextlib.suppress(Exception):
                managers = recruitment_obj.recruitment_managers.select_related(
                    "employee_user_id"
                )
                users = [employee.employee_user_id for employee in managers]
                notify.send(
                    request.user.employee_get,
                    recipient=users,
                    verb=f"{recruitment_obj} is updated, You are chosen as one of the managers",
                    verb_ar=f"تم تحديث {recruitment_obj}، تم اختيارك كأحد المديرين",
                    verb_de=f"{recruitment_obj} wurde aktualisiert. Sie wurden als einer der Manager ausgewählt",
                    verb_es=f"{recruitment_obj} ha sido actualizado/a. Has sido elegido\
                            a como uno de los gerentes",
                    verb_fr=f"{recruitment_obj} a été mis(e) à jour. Vous avez été\
                            choisi(e) comme l'un des responsables",
                    icon="people-circle",
                    redirect=reverse("pipeline"),
                )

            response = render(
                request, "pipeline/form/recruitment_update.html", {"form": form}
            )
            return HttpResponse(
                response.content.decode("utf-8") + "<script>location.reload();</script>"
            )
    return render(request, "pipeline/form/recruitment_update.html", {"form": form})


@login_required
@permission_required(perm="recruitment.delete_recruitment")
@require_http_methods(["POST"])
def recruitment_delete_pipeline(request, rec_id):
    """This method is used to delete the recruitment instance

    Args:
        id: recruitment instance id
    Returns:
        HttpResponseRedirect: Used to refresh the page
    """
    recruitment_obj = Recruitment.objects.get(id=rec_id)
    try:
        recruitment_obj.delete()
        messages.success(request, _("Recruitment deleted."))
    except Exception as error:
        messages.error(request, error)
        messages.error(request, _("Recruitment already in use."))
    return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))


@login_required
@manager_can_enter(perm="recruitment.change_candidate")
def candidate_stage_update(request, cand_id):
    """
    This method is a ajax method used to update candidate stage when drag and drop
    the candidate from one stage to another on the pipeline template
    Args:
        id : candidate_id
    """
    stage_id = request.POST["stageId"]
    candidate_obj = Candidate.objects.get(id=cand_id)
    history_queryset = candidate_obj.candidate_history.all().first()
    stage_obj = Stage.objects.get(id=stage_id)
    previous_stage = history_queryset.stage_id
    if previous_stage == stage_obj:
        return JsonResponse({"type": "info", "message": _("Sequence updated.")})
    # Here set the last updated schedule date on this stage if schedule exists in history
    history_queryset = candidate_obj.candidate_history.filter(stage_id=stage_obj)
    schedule_date = None
    if history_queryset.exists():
        # this condition is executed when a candidate dropped back to any previous
        # stage, if there any scheduled date then set it back
        schedule_date = history_queryset.first().schedule_date
    stage_manager_on_this_recruitment = (
        is_stagemanager(request)[1]
        .filter(recruitment_id=stage_obj.recruitment_id)
        .exists()
    )
    if (
        stage_manager_on_this_recruitment
        or request.user.is_superuser
        or is_recruitmentmanager(rec_id=stage_obj.recruitment_id.id)[0]
    ):
        candidate_obj.stage_id = stage_obj
        candidate_obj.schedule_date = None
        candidate_obj.hired = stage_obj.stage_type == "hired"
        candidate_obj.schedule_date = schedule_date
        candidate_obj.start_onboard = False
        candidate_obj.save()
        with contextlib.suppress(Exception):
            managers = stage_obj.stage_managers.select_related("employee_user_id")
            users = [employee.employee_user_id for employee in managers]
            notify.send(
                request.user.employee_get,
                recipient=users,
                verb=f"New candidate arrived on stage {stage_obj.stage}",
                verb_ar=f"وصل مرشح جديد إلى المرحلة {stage_obj.stage}",
                verb_de=f"Neuer Kandidat ist auf der Stufe {stage_obj.stage} angekommen",
                verb_es=f"Nuevo candidato llegó a la etapa {stage_obj.stage}",
                verb_fr=f"Nouveau candidat arrivé à l'étape {stage_obj.stage}",
                icon="person-add",
                redirect=reverse("pipeline"),
            )

        return JsonResponse(
            {"type": "success", "message": _("Candidate stage updated")}
        )
    return JsonResponse(
        {"type": "danger", "message": _("Something went wrong, Try agian.")}
    )


@login_required
@hx_request_required
@manager_can_enter(perm="recruitment.add_stagenote")
def add_note(request, cand_id=None):
    """
    This method renders template component to add candidate remark
    """
    form = StageNoteForm(initial={"candidate_id": cand_id})
    if request.method == "POST":
        form = StageNoteForm(
            request.POST,
        )
        if form.is_valid():
            note = form.save(commit=False)
            note.stage_id = note.candidate_id.stage_id
            note.updated_by = request.user.employee_get
            note.save()
            messages.success(request, _("Note added successfully.."))
            response = render(
                request, "pipeline/pipeline_components/add_note.html", {"form": form}
            )
            return HttpResponse(
                response.content.decode("utf-8") + "<script>location.reload();</script>"
            )
    return render(
        request,
        "pipeline/pipeline_components/add_note.html",
        {
            "note_form": form,
        },
    )


@login_required
@hx_request_required
@manager_can_enter(perm="recruitment.view_stagenote")
def view_note(request, cand_id):
    """
    This method renders a template components to view candidate remark or note
    Args:
        id : candidate instance id
    """
    candidate_obj = Candidate.objects.get(id=cand_id)
    return render(
        request, "pipeline/pipeline_components/view_note.html", {"cand": candidate_obj}
    )


@login_required
@permission_required(perm="recruitment.change_stagenote")
def note_update(request, note_id):
    """
    This method is used to update the stage not
    Args:
        id : stage note instance id
    """
    note = StageNote.objects.get(id=note_id)
    form = StageNoteForm(instance=note)
    if request.POST:
        form = StageNoteForm(request.POST, instance=note)
        if form.is_valid():
            form.save()
            messages.success(request, _("Note updated successfully..."))
            response = render(
                request, "pipeline/pipeline_components/update_note.html", {"form": form}
            )
            return HttpResponse(
                response.content.decode("utf-8") + "<script>location.reload();</script>"
            )
    return render(
        request, "pipeline/pipeline_components/update_note.html", {"form": form}
    )


@login_required
@permission_required(perm="recruitment.delete_stagenote")
def note_delete(request, note_id):
    """
    This method is used to delete the stage note
    """
    note = StageNote.objects.get(id=note_id)
    candidate_obj = note.candidate_id
    try:
        note.delete()
        messages.success(request, _("Note deleted"))
    except Exception as error:
        messages.error(request, error)
        messages.error(request, _("You cannot delete this note."))
    return render(
        request, "pipeline/pipeline_components/view_note.html", {"cand": candidate_obj}
    )


@login_required
@require_http_methods(["DELETE"])
@permission_required(perm="recruitment.delete_stagenote")
def candidate_remark_delete(request, note_id):
    """
    This method is used to delete the candidate stage note
    Args:
        id : stage note instance id
    """
    stage_note = StageNote.objects.get(id=note_id)
    candidate_obj = stage_note.candidate_note_id.candidate_id
    try:
        stage_note.delete()
        messages.success(request, _("Note deleted"))
    except Exception as error:
        messages.error(request, error)
        messages.error(request, _("You cannot delete this note."))
    return render(
        request,
        "pipeline/pipeline_components/candidate_remark_view.html",
        {"cand": candidate_obj},
    )


@login_required
@permission_required(perm="recruitment.change_candidate")
def candidate_schedule_date_update(request):
    """
    This is a an ajax method to update schedule date for a candidate
    """
    candidate_id = request.POST["candidateId"]
    schedule_date = request.POST["date"]
    candidate_obj = Candidate.objects.get(id=candidate_id)
    candidate_obj.schedule_date = schedule_date
    candidate_obj.save()
    return JsonResponse({"message": "congratulations"})


@login_required
@permission_required(perm="recruitment.add_stage")
def stage(request):
    """
    This method is used to create stages, also several permission assigned to the stage managers
    """
    form = StageCreationForm()
    if request.method == "POST":
        form = StageCreationForm(request.POST)
        if form.is_valid():
            stage_obj = form.save()
            recruitment_obj = stage_obj.recruitment_id
            rec_stages = (
                Stage.objects.filter(recruitment_id=recruitment_obj, is_active=True)
                .order_by("sequence")
                .last()
            )
            if rec_stages.sequence is None:
                stage_obj.sequence = 1
            else:
                stage_obj.sequence = rec_stages.sequence + 1
            stage_obj.save()
            messages.success(request, _("Stage added."))
            with contextlib.suppress(Exception):
                managers = stage_obj.stage_managers.select_related("employee_user_id")
                users = [employee.employee_user_id for employee in managers]
                notify.send(
                    request.user.employee_get,
                    recipient=users,
                    verb=f"Stage {stage_obj} is updated on recruitment {stage_obj.recruitment_id}, You are chosen as one of the managers",
                    verb_ar=f"تم تحديث المرحلة {stage_obj} في التوظيف {stage_obj.recruitment_id}، تم اختيارك كأحد المديرين",
                    verb_de=f"Stufe {stage_obj} wurde in der Rekrutierung {stage_obj.recruitment_id} aktualisiert. Sie wurden als einer der Manager ausgewählt",
                    verb_es=f"La etapa {stage_obj} ha sido actualizada en la contratación {stage_obj.recruitment_id}. Has sido elegido/a como uno de los gerentes",
                    verb_fr=f"L'étape {stage_obj} a été mise à jour dans le recrutement {stage_obj.recruitment_id}. Vous avez été choisi(e) comme l'un des responsables",
                    icon="people-circle",
                    redirect=reverse("pipeline"),
                )

            response = render(request, "stage/stage_form.html", {"form": form})
            return HttpResponse(
                response.content.decode("utf-8") + "<script>location.reload();</script>"
            )
    return render(request, "stage/stage_form.html", {"form": form})


@login_required
@permission_required(perm="recruitment.view_stage")
def stage_view(request):
    """
    This method is used to render all stages to a template
    """
    stages = Stage.objects.filter()
    filter_obj = StageFilter()
    form = StageCreationForm()
    return render(
        request,
        "stage/stage_view.html",
        {
            "data": paginator_qry(stages, request.GET.get("page")),
            "form": form,
            "f": filter_obj,
        },
    )


@login_required
@permission_required(perm="recruitment.view_stage")
def stage_search(request):
    """
    This method is used to search stage
    """
    stages = StageFilter(request.GET).qs
    previous_data = request.environ["QUERY_STRING"]
    stages = sortby(request, stages, "orderby")
    return render(
        request,
        "stage/stage_component.html",
        {"data": paginator_qry(stages, request.GET.get("page")), "pd": previous_data},
    )


@login_required
@permission_required(perm="recruitment.change_stage")
def remove_stage_manager(request, mid, sid):
    """
    This method is used to remove selected stage manager and also removing the  given
    permission if the employee is not exists in more stage manager or recruitment manager
    Args:
        mid : manager_id in the stage
        sid : stage_id
    """
    stage_obj = Stage.objects.get(id=sid)
    manager = Employee.objects.get(id=mid)
    notify.send(
        request.user.employee_get,
        recipient=manager.employee_user_id,
        verb=f"You are removed from stage managers from stage {stage_obj}",
        verb_ar=f"تمت إزالتك من مديري المرحلة من المرحلة {stage_obj}",
        verb_de=f"Sie wurden als Bühnenmanager von der Stufe {stage_obj} entfernt",
        verb_es=f"Has sido eliminado/a de los gerentes de etapa de la etapa {stage_obj}",
        verb_fr=f"Vous avez été supprimé(e) en tant que responsable de l'étape {stage_obj}",
        icon="person-remove",
        redirect="",
    )
    stage_obj.stage_managers.remove(manager)
    messages.success(request, _("Stage manager removed successfully."))
    stages = Stage.objects.all()
    previous_data = request.environ["QUERY_STRING"]
    return render(
        request,
        "stage/stage_component.html",
        {"data": paginator_qry(stages, request.GET.get("page")), "pd": previous_data},
    )


@login_required
@permission_required(perm="recruitment.change_stage")
@hx_request_required
def stage_update(request, stage_id):
    """
    This method is used to update stage, if the managers changed then\
    permission assigned to new managers also
    Args:
        id : stage_id

    """
    stages = Stage.objects.get(id=stage_id)
    form = StageCreationForm(instance=stages)
    if request.method == "POST":
        form = StageCreationForm(request.POST, instance=stages)
        if form.is_valid():
            form.save()
            messages.success(request, _("Stage updated."))
            response = render(
                request, "recruitment/recruitment_form.html", {"form": form}
            )
            return HttpResponse(
                response.content.decode("utf-8") + "<script>location.reload();</script>"
            )
    return render(request, "stage/stage_update_form.html", {"form": form})


@login_required
@require_http_methods(["POST"])
@hx_request_required
def stage_name_update(request, stage_id):
    """
    This method is used to update the name of recruitment stage
    """
    stage_obj = Stage.objects.get(id=stage_id)
    stage_obj.stage = request.POST["stage"]
    stage_obj.save()
    return HttpResponse({"message": "success"})


@login_required
@permission_required(perm="recruitment.delete_stage")
@require_http_methods(["POST", "DELETE"])
def stage_delete(request, stage_id):
    """
    This method is used to delete stage permanently
    Args:
        id : stage_id
    """
    stage_obj = Stage.objects.get(id=stage_id)
    stage_managers = stage_obj.stage_managers.all()
    for manager in stage_managers:
        all_this_manger = manager.stage_set.all()
        if len(all_this_manger) == 1:
            view_recruitment = Permission.objects.get(codename="view_recruitment")
            manager.employee_user_id.user_permissions.remove(view_recruitment.id)
        initial_stage_manager = all_this_manger.filter(stage_type="initial")
        if len(initial_stage_manager) == 1:
            add_candidate = Permission.objects.get(codename="add_candidate")
            change_candidate = Permission.objects.get(codename="change_candidate")
            manager.employee_user_id.user_permissions.remove(add_candidate.id)
            manager.employee_user_id.user_permissions.remove(change_candidate.id)
        stage_obj.stage_managers.remove(manager)
    try:
        stage_obj.delete()
        messages.success(request, _("Stage deleted successfully."))
    except Exception as error:
        messages.error(request, error)
        messages.error(request, _("You cannot delete this stage"))
    return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))


@login_required
@permission_required(perm="recruitment.add_candidate")
def candidate(request):
    """
    This method used to create candidate
    """
    form = CandidateCreationForm()
    open_recruitment = Recruitment.objects.filter(closed=False, is_active=True)
    path = "/recruitment/candidate-view"
    if request.method == "POST":
        form = CandidateCreationForm(request.POST, request.FILES)
        if form.is_valid():
            candidate_obj = form.save(commit=False)
            candidate_obj.start_onboard = False
            if candidate_obj.stage_id is None:
                candidate_obj.stage_id = Stage.objects.filter(
                    recruitment_id=candidate_obj.recruitment_id, stage_type="initial"
                ).first()
            # when creating new candidate from onboarding view
            if request.GET.get("onboarding") == "True":
                candidate_obj.hired = True
                path = "/onboarding/candidates-view"
            candidate_obj.save()
            messages.success(request, _("Candidate added."))
            return redirect(path)

    return render(
        request,
        "candidate/candidate_create_form.html",
        {"form": form, "open_recruitment": open_recruitment},
    )


@login_required
@permission_required(perm="recruitment.add_candidate")
def recruitment_stage_get(_, rec_id):
    """
    This method returns all stages as json
    Args:
        id: recruitment_id
    """
    recruitment_obj = Recruitment.objects.get(id=rec_id)
    all_stages = recruitment_obj.stage_set.all()
    all_stage_json = serializers.serialize("json", all_stages)
    return JsonResponse({"stages": all_stage_json})


@login_required
@permission_required(perm="recruitment.view_candidate")
def candidate_view(request):
    """
    This method render all candidate to the template
    """
    previous_data = request.environ["QUERY_STRING"]
    candidates = Candidate.objects.filter(is_active=True)
    filter_obj = CandidateFilter(queryset=candidates)
    return render(
        request,
        "candidate/candidate_view.html",
        {
            "data": paginator_qry(filter_obj.qs, request.GET.get("page")),
            "pd": previous_data,
            "f": filter_obj,
        },
    )


@login_required
@permission_required(perm="recruitment.view_candidate")
def candidate_filter_view(request):
    """
    This method is used for filter,pagination and search candidate.
    """
    previous_data = request.environ["QUERY_STRING"]
    filter_obj = CandidateFilter(
        request.GET, queryset=Candidate.objects.filter(is_active=True)
    )
    paginator = Paginator(filter_obj.qs, 24)
    page_number = request.GET.get("page")
    page_obj = paginator.get_page(page_number)
    return render(
        request,
        "candidate/candidate_card.html",
        {"data": page_obj, "pd": previous_data},
    )


@login_required
@permission_required(perm="recruitment.view_candidate")
def candidate_search(request):
    """
    This method is used to search candidate model and return matching objects
    """
    previous_data = request.environ["QUERY_STRING"]
    search = request.GET.get("search")
    if search is None:
        search = ""
    candidates = Candidate.objects.filter(name__icontains=search)
    candidates = CandidateFilter(request.GET, queryset=candidates).qs
    template = "candidate/candidate_card.html"
    if request.GET.get("view") == "list":
        template = "candidate/candidate_list.html"
    candidates = sortby(request, candidates, "orderby")
    candidates = paginator_qry(candidates, request.GET.get("page"))
    return render(request, template, {"data": candidates, "pd": previous_data})


@login_required
@permission_required(perm="recruitment.view_candidate")
def candidate_view_list(request):
    """
    This method renders all candidate on candidate_list.html template
    """
    previous_data = request.environ["QUERY_STRING"]
    candidates = Candidate.objects.all()
    if request.GET.get("is_active") is None:
        candidates = candidates.filter(is_active=True)
    candidates = CandidateFilter(request.GET, queryset=candidates).qs
    return render(
        request,
        "candidate/candidate_list.html",
        {
            "data": paginator_qry(candidates, request.GET.get("page")),
            "pd": previous_data,
        },
    )


@login_required
@permission_required(perm="recruitment.view_candidate")
def candidate_view_card(request):
    """
    This method renders all candidate on candidate_card.html template
    """
    previous_data = request.environ["QUERY_STRING"]
    candidates = Candidate.objects.all()
    if request.GET.get("is_active") is None:
        candidates = candidates.filter(is_active=True)
    candidates = CandidateFilter(request.GET, queryset=candidates).qs
    return render(
        request,
        "candidate/candidate_card.html",
        {
            "data": paginator_qry(candidates, request.GET.get("page")),
            "pd": previous_data,
        },
    )


@login_required
@permission_required(perm="recruitment.view_candidate")
def candidate_view_individual(request, cand_id):
    """
    This method is used to view profile of candidate.
    """
    candidate_obj = Candidate.objects.get(id=cand_id)
    return render(request, "candidate/individual.html", {"candidate": candidate_obj})


@login_required
@manager_can_enter(perm="recruitment.change_candidate")
def candidate_update(request, cand_id):
    """
    Used to update or change the candidate
    Args:
        id : candidate_id
    """
    candidate_obj = Candidate.objects.get(id=cand_id)
    form = CandidateCreationForm(instance=candidate_obj)
    path = "/recruitment/candidate-view"
    if request.method == "POST":
        form = CandidateCreationForm(
            request.POST, request.FILES, instance=candidate_obj
        )
        if form.is_valid():
            candidate_obj = form.save()
            if candidate_obj.stage_id is None:
                candidate_obj.stage_id = Stage.objects.filter(
                    recruitment_id=candidate_obj.recruitment_id, stage_type="initial"
                ).first()
            if candidate_obj.stage_id is not None:
                if (
                    candidate_obj.stage_id.recruitment_id
                    != candidate_obj.recruitment_id
                ):
                    candidate_obj.stage_id = (
                        candidate_obj.recruitment_id.stage_set.filter(
                            stage_type="initial"
                        ).first()
                    )
            if request.GET.get("onboarding") == "True":
                candidate_obj.hired = True
                path = "/onboarding/candidates-view"
            candidate_obj.save()
            messages.success(request, _("Candidate Updated Successfully."))
            return redirect(path)
    return render(request, "candidate/candidate_create_form.html", {"form": form})


@login_required
@permission_required(perm="recruitment.delete_candidate")
@require_http_methods(["DELETE", "POST"])
def candidate_delete(request, cand_id):
    """
    This method is used to delete candidate permanently
    Args:
        id : candidate_id
    """
    try:
        Candidate.objects.get(id=cand_id).delete()
        messages.success(request, _("Candidate deleted successfully."))
    except Exception as error:
        messages.error(request, error)
        messages.error(request, _("You cannot delete this candidate"))
    return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))


@login_required
@permission_required(perm="recruitment.delete_candidate")
def candidate_archive(request, cand_id):
    """
    This method is used to archive or un-archive candidates
    """
    candidate_obj = Candidate.objects.get(id=cand_id)
    candidate_obj.is_active = not candidate_obj.is_active
    candidate_obj.save()
    return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))


@login_required
@permission_required(perm="recruitment.delete_candidate")
@require_http_methods(["POST"])
def candidate_bulk_delete(request):
    """
    This method is used to bulk delete candidates
    """
    ids = request.POST["ids"]
    ids = json.loads(ids)
    for cand_id in ids:
        candidate_obj = Candidate.objects.get(id=cand_id)
        try:
            candidate_obj.delete()
            messages.success(
                request, _("%(candidate_obj)s deleted.") % {"candidate": candidate_obj}
            )
        except Exception as error:
            messages.error(
                request,
                _("You cannot delete %(candidate_obj)s") % {"candidate": candidate_obj},
            )
            messages.error(request, error)
    return JsonResponse({"message": "Success"})


@login_required
@permission_required(perm="recruitment.delete_candidate")
@require_http_methods(["POST"])
def candidate_bulk_archive(request):
    """
    This method is used to archive/un-archive bulk candidates
    """
    ids = request.POST["ids"]
    ids = json.loads(ids)
    is_active = True
    message = "un-archived"
    if request.GET.get("is_active") == "False":
        is_active = False
        message = "archived"
    for cand_id in ids:
        candidate_obj = Candidate.objects.get(id=cand_id)
        candidate_obj.is_active = is_active
        candidate_obj.save()
        messages.success(request, f"{candidate_obj} is {message}")
    return JsonResponse({"message": "Success"})


@login_required
@permission_required(perm="recruitment.view_history")
def candidate_history(request, cand_id):
    """
    This method is used to view candidate stage changes
    Args:
        id : candidate_id
    """
    candidate_obj = Candidate.objects.get(id=cand_id)
    candidate_history_queryset = candidate_obj.history.all()
    return render(
        request,
        "candidate/candidate_history.html",
        {"history": candidate_history_queryset},
    )


def application_form(request):
    """
    This method renders candidate form to create candidate
    """
    form = ApplicationForm()
    if request.POST:
        form = ApplicationForm(request.POST, request.FILES)
        if form.is_valid():
            candidate_obj = form.save(commit=False)
            recruitment_obj = candidate_obj.recruitment_id
            stages = recruitment_obj.stage_set.all()
            if stages.filter(stage_type="applied").exists():
                candidate_obj.stage_id = stages.filter(stage_type="applied").first()
            else:
                candidate_obj.stage_id = stages.order_by("sequence").first()
            candidate_obj.save()
            messages.success(request, _("Application saved."))
            return render(request, "candidate/success.html")
        form.fields["job_position_id"].queryset = (
            form.instance.recruitment_id.open_positions.all()
        )
    return render(request, "candidate/application_form.html", {"form": form})


@login_required
@hx_request_required
@manager_can_enter(perm="recruitment.change_candidate")
def form_send_mail(request, cand_id):
    """
    This method is used to render the bootstrap modal content body form
    """
    candidate_obj = Candidate.objects.get(id=cand_id)
    return render(
        request, "pipeline/pipeline_components/send_mail.html", {"cand": candidate_obj}
    )


@login_required
@manager_can_enter(perm="recruitment.change_candidate")
def send_acknowledgement(request):
    """
    This method is used to send acknowledgement mail to the candidate
    """
    with contextlib.suppress(Exception):
        send_to = request.POST.get("to")
        subject = request.POST.get("subject")
        bdy = request.POST.get("body")
        email_backend = ConfiguredEmailBackend()
        display_email_name = email_backend.dynamic_from_email_with_display_name
        if request:
            try:
                display_email_name = f"{request.user.employee_get.get_full_name()} <{request.user.employee_get.email}>"
            except:
                logger.error(Exception)

        res = send_mail(
            subject, bdy, display_email_name, [send_to], fail_silently=False
        )
        if res == 1:
            return HttpResponse(
                """
            <div class="oh-alert-container">
                <div class="oh-alert oh-alert--animated oh-alert--success"> Mail sent.</div>
            </div>
            """
            )
    return HttpResponse(
        """
        <div class="oh-alert-container">
            <div class="oh-alert oh-alert--animated oh-alert--danger">Sorry,\
                Something went wrong.</div>
        </div>
        """
    )


@login_required
@manager_can_enter(perm="recruitment.view_recruitment")
def dashboard(request):
    """
    This method is used to render individual dashboard for recruitment module
    """
    candidates = Candidate.objects.all()
    hired_candidates = candidates.filter(hired=True)
    total_candidates = len(candidates)
    total_hired_candidates = len(hired_candidates)
    hire_ratio = 0
    if total_candidates != 0:
        hire_ratio = f"{((total_hired_candidates / total_candidates) * 100):.1f}"
    return render(
        request,
        "dashboard/dashboard.html",
        {
            "total_candidates": total_candidates,
            "total_hired_candidates": total_hired_candidates,
            "hire_ratio": hire_ratio,
            "onboard_candidates": hired_candidates.filter(start_onboard=True),
        },
    )


def stage_type_candidate_count(rec, stage_type):
    """
    This method is used find the count of candidate in recruitment
    """
    candidates_count = 0
    for stage_obj in rec.stage_set.filter(stage_type=stage_type):
        candidates_count = candidates_count + len(
            stage_obj.candidate_set.filter(is_active=True)
        )
    return candidates_count


@login_required
@manager_can_enter(perm="recruitment.view_recruitment")
def dashboard_pipeline(_):
    """
    This method is used generate recruitment dataset for the dashboard
    """
    recruitment_obj = Recruitment.objects.filter(closed=False)
    data_set = []
    labels = [type[1] for type in Stage.stage_types]
    for rec in recruitment_obj:
        data = [stage_type_candidate_count(rec, type[0]) for type in Stage.stage_types]
        data_set.append(
            {
                "label": (
                    rec.title
                    if rec.title is not None
                    else f"""{rec.job_position_id}
                 {rec.start_date}"""
                ),
                "data": data,
            }
        )
    return JsonResponse({"dataSet": data_set, "labels": labels})


def get_open_position(request):
    """
    This is an ajax method to render the open position to the recruitment

    Returns:
        obj: it returns the list of job positions
    """
    rec_id = request.GET["recId"]
    recruitment_obj = Recruitment.objects.get(id=rec_id)
    queryset = recruitment_obj.open_positions.all()
    job_info = serializers.serialize("json", queryset)
    rec_info = serializers.serialize("json", [recruitment_obj])
    return JsonResponse({"openPositions": job_info, "recruitmentInfo": rec_info})


@login_required
@manager_can_enter("recruitment.change_candidate")
def candidate_sequence_update(request):
    """
    This method is used to update the sequence of candidate
    """
    sequence_data = json.loads(request.POST["sequenceData"])
    for cand_id, seq in sequence_data.items():
        cand = Candidate.objects.get(id=cand_id)
        cand.sequence = seq
        cand.save()

    return JsonResponse({"message": "Sequence updated", "type": "info"})


@login_required
@recruitment_manager_can_enter("recruitment.change_stage")
def stage_sequence_update(request):
    """
    This method is used to update the sequence of the stages
    """
    sequence_data = json.loads(request.POST["sequence"])
    for stage_id, seq in sequence_data.items():
        stage = Stage.objects.get(id=stage_id)
        stage.sequence = seq
        stage.save()
    return JsonResponse({"type": "success", "message": "Stage sequence updated"})
