from django.contrib.auth.decorators import login_required
from pathlib import Path

from django.core.exceptions import PermissionDenied
from django.http import FileResponse
from django.db.models import Q
from django.core.paginator import Paginator
from django.shortcuts import get_object_or_404, redirect, render

from apps.accounts.permissions import (
    ROLE_ACCOUNTS, ROLE_HR_ADMIN, ROLE_MANAGER, ROLE_PAYROLL_OFFICER, ROLE_SUPER_ADMIN, can_access_employee, has_role, require_roles,
    accessible_company_ids, scope_queryset_by_company,
)
from apps.audit.services import log_event
from .forms import CustomFieldDefinitionForm, EmployeeDocumentForm, EmployeeForm, EmployeeLifecycleForm, EmployeePaymentAccountForm
from .models import CustomFieldDefinition, Employee, EmployeeDocument, EmployeePaymentAccount


def _visible_employees(user):
    qs = Employee.objects.select_related("company", "branch", "department", "designation", "shift", "reporting_manager")
    qs = scope_queryset_by_company(user, qs, "company")
    if user.is_superuser or has_role(user, ROLE_SUPER_ADMIN, ROLE_HR_ADMIN):
        return qs
    own = getattr(user, "employee_profile", None)
    if has_role(user, ROLE_MANAGER) and own:
        return qs.filter(Q(pk=own.pk) | Q(reporting_manager=own))
    if own:
        return qs.filter(pk=own.pk)
    return qs.none()


@require_roles(ROLE_SUPER_ADMIN, ROLE_HR_ADMIN)
def company_choice_options(request):
    from django.http import JsonResponse
    from apps.organization.models import Branch, Department, Designation, Shift
    company_id = request.GET.get("company")
    try:
        company_id = int(company_id)
    except (TypeError, ValueError):
        return JsonResponse({"branches": [], "departments": [], "designations": [], "shifts": [], "managers": []})
    if company_id not in accessible_company_ids(request.user):
        raise PermissionDenied("You do not have access to this company.")

    def serialize(qs):
        return [{"id": obj.pk, "label": str(obj)} for obj in qs]

    managers = Employee.objects.filter(company_id=company_id).order_by("employee_code", "full_name")
    return JsonResponse({
        "branches": serialize(Branch.objects.filter(company_id=company_id, is_active=True).order_by("name")),
        "departments": serialize(Department.objects.filter(company_id=company_id, is_active=True).order_by("name")),
        "designations": serialize(Designation.objects.filter(company_id=company_id, is_active=True).order_by("name")),
        "shifts": serialize(Shift.objects.filter(company_id=company_id, is_active=True).order_by("name")),
        "managers": [{"id": obj.pk, "label": f"{obj.employee_code} - {obj.full_name}"} for obj in managers],
    })


@login_required
def employee_list(request):
    qs = _visible_employees(request.user)
    q = request.GET.get("q", "").strip()
    if q:
        qs = qs.filter(Q(employee_code__icontains=q) | Q(full_name__icontains=q) | Q(mobile__icontains=q))
    page_obj = Paginator(qs.order_by("employee_code"), 100).get_page(request.GET.get("page"))
    return render(request, "employees/employee_list.html", {"employees": page_obj.object_list, "page_obj": page_obj, "q": q})


@login_required
def employee_detail(request, pk):
    employee = get_object_or_404(Employee.objects.select_related("company", "branch", "department", "designation", "shift", "reporting_manager"), pk=pk)
    if not can_access_employee(request.user, employee):
        raise PermissionDenied("You do not have permission to view this employee.")
    can_manage_docs = request.user.is_superuser or has_role(request.user, ROLE_SUPER_ADMIN, ROLE_HR_ADMIN)
    can_view_docs = can_manage_docs or employee.user_id == request.user.id
    documents = employee.documents.order_by("document_type", "title") if can_view_docs else EmployeeDocument.objects.none()
    custom_values = employee.custom_values.select_related("definition").filter(definition__is_active=True).order_by("definition__label")
    can_manage_profile = request.user.is_superuser or has_role(request.user, ROLE_SUPER_ADMIN, ROLE_HR_ADMIN)
    can_manage_payment = request.user.is_superuser or has_role(request.user, ROLE_SUPER_ADMIN, ROLE_HR_ADMIN, ROLE_PAYROLL_OFFICER, ROLE_ACCOUNTS)
    payment_account = getattr(employee, "payment_account", None) if can_manage_payment or employee.user_id == request.user.id else None
    return render(request, "employees/employee_detail.html", {"employee": employee, "documents": documents, "custom_values": custom_values, "can_manage_docs": can_manage_docs, "can_view_docs": can_view_docs, "can_manage_profile": can_manage_profile, "can_manage_payment": can_manage_payment, "payment_account": payment_account})


@require_roles(ROLE_SUPER_ADMIN, ROLE_HR_ADMIN)
def employee_create(request):
    from apps.organization.models import Company
    company_qs = Company.objects.filter(pk__in=accessible_company_ids(request.user), is_active=True)
    form = EmployeeForm(request.POST or None, request.FILES or None, company_queryset=company_qs)
    if request.method == "POST" and form.is_valid():
        employee = form.save()
        log_event(user=request.user, action="CREATE", module="employee", object_id=employee.pk, object_repr=str(employee), new_values={"employee_code": employee.employee_code, "full_name": employee.full_name}, request=request)
        return redirect("employees:detail", pk=employee.pk)
    return render(request, "employees/employee_form.html", {"form": form, "page_title": "Add Employee"})


@require_roles(ROLE_SUPER_ADMIN, ROLE_HR_ADMIN)
def employee_edit(request, pk):
    employee = get_object_or_404(scope_queryset_by_company(request.user, Employee.objects.all(), "company"), pk=pk)
    old = {"employee_code": employee.employee_code, "full_name": employee.full_name, "status": employee.status, "department_id": employee.department_id, "designation_id": employee.designation_id}
    from apps.organization.models import Company
    company_qs = Company.objects.filter(pk__in=accessible_company_ids(request.user), is_active=True)
    form = EmployeeForm(request.POST or None, request.FILES or None, instance=employee, company_queryset=company_qs)
    if request.method == "POST" and form.is_valid():
        employee = form.save()
        new = {"employee_code": employee.employee_code, "full_name": employee.full_name, "status": employee.status, "department_id": employee.department_id, "designation_id": employee.designation_id}
        log_event(user=request.user, action="UPDATE", module="employee", object_id=employee.pk, object_repr=str(employee), old_values=old, new_values=new, request=request)
        return redirect("employees:detail", pk=employee.pk)
    return render(request, "employees/employee_form.html", {"form": form, "employee": employee, "page_title": "Edit Employee"})


@require_roles(ROLE_SUPER_ADMIN, ROLE_HR_ADMIN)
def employee_document_upload(request, pk):
    employee = get_object_or_404(scope_queryset_by_company(request.user, Employee.objects.all(), "company"), pk=pk)
    form = EmployeeDocumentForm(request.POST or None, request.FILES or None)
    if request.method == "POST" and form.is_valid():
        document = form.save(commit=False)
        document.employee = employee
        document.save()
        log_event(user=request.user, company=employee.company, action="UPLOAD_DOCUMENT", module="employee_document", object_id=document.pk, object_repr=f"{employee} / {document.title}", new_values={"document_type": document.document_type, "title": document.title}, request=request)
        return redirect("employees:detail", pk=employee.pk)
    return render(request, "employees/document_form.html", {"form": form, "employee": employee})


@login_required
def employee_document_download(request, pk):
    document = get_object_or_404(EmployeeDocument.objects.select_related("employee__company", "employee__user"), pk=pk)
    employee = document.employee
    is_hr = request.user.is_superuser or has_role(request.user, ROLE_SUPER_ADMIN, ROLE_HR_ADMIN)
    is_own = employee.user_id == request.user.id
    if not user_can_access_company_for_document(request.user, employee.company_id) or not (is_hr or is_own):
        raise PermissionDenied("You do not have permission to download this employee document.")
    log_event(user=request.user, company=employee.company, action="DOWNLOAD_DOCUMENT", module="employee_document", object_id=document.pk, object_repr=f"{employee} / {document.title}", request=request)
    return FileResponse(document.file.open("rb"), as_attachment=True, filename=Path(document.file.name).name)


def user_can_access_company_for_document(user, company_id):
    return company_id in accessible_company_ids(user)


@require_roles(ROLE_SUPER_ADMIN, ROLE_HR_ADMIN)
def custom_field_list(request):
    fields = CustomFieldDefinition.objects.filter(company_id__in=accessible_company_ids(request.user)).select_related("company").order_by("company__name", "label", "pk")
    return render(request, "employees/custom_field_list.html", {"fields": fields})


@require_roles(ROLE_SUPER_ADMIN, ROLE_HR_ADMIN)
def custom_field_edit(request, pk=None):
    from apps.organization.models import Company
    company_qs = Company.objects.filter(pk__in=accessible_company_ids(request.user), is_active=True)
    instance = None
    if pk:
        instance = get_object_or_404(CustomFieldDefinition.objects.filter(company_id__in=accessible_company_ids(request.user)), pk=pk)
    form = CustomFieldDefinitionForm(request.POST or None, instance=instance, company_queryset=company_qs)
    if request.method == "POST" and form.is_valid():
        obj = form.save()
        log_event(
            user=request.user, company=obj.company, action="UPDATE_CUSTOM_FIELD" if instance else "CREATE_CUSTOM_FIELD",
            module="employee_custom_field", object_id=obj.pk, object_repr=f"{obj.company.code}/{obj.key}",
            new_values={"label": obj.label, "field_type": obj.field_type, "required": obj.is_required, "active": obj.is_active}, request=request,
        )
        return redirect("employees:custom-fields")
    return render(request, "employees/custom_field_form.html", {"form": form, "title": "Edit Employee Custom Field" if instance else "Add Employee Custom Field"})


@require_roles(ROLE_SUPER_ADMIN, ROLE_HR_ADMIN)
def employee_lifecycle(request, pk):
    employee = get_object_or_404(scope_queryset_by_company(request.user, Employee.objects.all(), "company"), pk=pk)
    if employee.status in {Employee.Status.RESIGNED, Employee.Status.TERMINATED, Employee.Status.RETIRED}:
        raise PermissionDenied("Completed exit statuses cannot be changed from the employee profile. Use the controlled exit workflow.")
    form = EmployeeLifecycleForm(request.POST or None, employee=employee, initial={"effective_date": __import__("django.utils.timezone", fromlist=["localdate"]).localdate()})
    if request.method == "POST" and form.is_valid():
        action = form.cleaned_data["action"]
        effective = form.cleaned_data["effective_date"]
        reason = (form.cleaned_data.get("reason") or "").strip()
        old = {"status": employee.status, "confirmation_date": str(employee.confirmation_date or "")}
        if action == "CONFIRM":
            employee.status = Employee.Status.CONFIRMED
            employee.confirmation_date = effective
        elif action == "SUSPEND":
            employee.status = Employee.Status.SUSPENDED
        elif action == "REACTIVATE":
            employee.status = Employee.Status.CONFIRMED if employee.confirmation_date else Employee.Status.ACTIVE
        employee.save(update_fields=["status", "confirmation_date", "updated_at"])
        log_event(
            user=request.user, company=employee.company, action=f"EMPLOYEE_{action}", module="employee_lifecycle",
            object_id=employee.pk, object_repr=str(employee), old_values=old,
            new_values={"status": employee.status, "confirmation_date": str(employee.confirmation_date or ""), "effective_date": str(effective)},
            reason=reason, request=request,
        )
        return redirect("employees:detail", pk=employee.pk)
    return render(request, "employees/employee_lifecycle.html", {"form": form, "employee": employee})


@require_roles(ROLE_SUPER_ADMIN, ROLE_HR_ADMIN, ROLE_PAYROLL_OFFICER, ROLE_ACCOUNTS)
def employee_payment_account(request, pk):
    employee = get_object_or_404(scope_queryset_by_company(request.user, Employee.objects.select_related("company"), "company"), pk=pk)
    account = EmployeePaymentAccount.objects.filter(employee=employee).first()
    old_masked = account.masked_destination if account else ""
    old_method = account.payment_method if account else ""
    form = EmployeePaymentAccountForm(request.POST or None, instance=account)
    if request.method == "POST" and form.is_valid():
        obj = form.save(commit=False)
        obj.employee = employee
        obj.save()
        log_event(
            user=request.user, company=employee.company, action="UPDATE_PAYMENT_ACCOUNT" if account else "CREATE_PAYMENT_ACCOUNT",
            module="employee_payment", object_id=obj.pk, object_repr=str(employee),
            old_values={"method": old_method, "destination": old_masked},
            new_values={"method": obj.payment_method, "destination": obj.masked_destination, "bank_name": obj.bank_name, "active": obj.is_active},
            reason="Salary payment destination updated", request=request,
        )
        return redirect("employees:detail", pk=employee.pk)
    return render(request, "employees/payment_account_form.html", {"form": form, "employee": employee, "account": account})
