from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
from django.core.paginator import Paginator
from django.db.models import Q
from django.shortcuts import get_object_or_404, redirect, render

from apps.accounts.permissions import (
    ROLE_HR_ADMIN,
    ROLE_MANAGER,
    ROLE_SUPER_ADMIN,
    accessible_company_ids,
    has_role,
)
from apps.employees.models import Employee
from .forms import LeaveApplicationForm, ManagedLeaveApplicationForm, LeaveCancellationForm, LeaveYearInitializeForm
from .models import LeaveBalance, LeaveRequest
from .services import cancel_approved_leave, create_leave_request, initialize_leave_year, resubmit_leave_request


def _visible_employees(user):
    """Return only employees within the user's company and team scope."""
    company_ids = accessible_company_ids(user)
    if not company_ids:
        return Employee.objects.none()
    qs = Employee.objects.filter(company_id__in=company_ids)
    if user.is_superuser or has_role(user, ROLE_SUPER_ADMIN, ROLE_HR_ADMIN):
        return qs
    own = getattr(user, "employee_profile", None)
    if not own or own.company_id not in company_ids:
        return qs.none()
    if has_role(user, ROLE_MANAGER):
        return qs.filter(Q(pk=own.pk) | Q(reporting_manager=own))
    return qs.filter(pk=own.pk)


@login_required
def leave_overview(request):
    employees = _visible_employees(request.user)
    request_qs = (
        LeaveRequest.objects.select_related("employee", "leave_type", "employee__company")
        .filter(employee__in=employees)
        .order_by("-created_at")
    )
    balance_qs = (
        LeaveBalance.objects.select_related("employee", "leave_type", "employee__company")
        .filter(employee__in=employees)
        .order_by("employee__employee_code", "leave_type__name")
    )
    request_page = Paginator(request_qs, 100).get_page(request.GET.get("leave_page"))
    balance_page = Paginator(balance_qs, 100).get_page(request.GET.get("balance_page"))
    can_manage = request.user.is_superuser or has_role(request.user, ROLE_SUPER_ADMIN, ROLE_HR_ADMIN)
    return render(request, "leave/leave_overview.html", {
        "leave_requests": request_page.object_list, "leave_page": request_page,
        "balances": balance_page.object_list, "balance_page": balance_page, "can_manage": can_manage,
    })


@login_required
def leave_manage_add(request):
    if not (request.user.is_superuser or has_role(request.user, ROLE_SUPER_ADMIN, ROLE_HR_ADMIN)):
        raise PermissionDenied("HR/Super Admin permission is required.")
    employees = _visible_employees(request.user).filter(status__in=["ACTIVE", "PROBATION", "CONFIRMED"]).order_by("employee_code")
    form = ManagedLeaveApplicationForm(request.POST or None, employee_queryset=employees)
    if request.method == "POST" and form.is_valid():
        employee = form.cleaned_data["employee"]
        create_leave_request(employee=employee, leave_type=form.cleaned_data["leave_type"], start_date=form.cleaned_data["start_date"], end_date=form.cleaned_data["end_date"], days=form.cleaned_data["calculated_days"], reason=form.cleaned_data.get("reason", ""), user=request.user, request=request)
        messages.success(request, f"Leave application submitted for {employee}.")
        return redirect("leave:overview")
    return render(request, "leave/leave_form.html", {"form": form, "page_title": "Add Leave Application"})


@login_required
def leave_apply(request):
    employee = getattr(request.user, "employee_profile", None)
    company_ids = accessible_company_ids(request.user)
    if not employee or employee.company_id not in company_ids:
        messages.error(request, "Leave applications must be submitted from a user account linked to an employee profile. HR/Super Admin may manage leave records but cannot submit a personal leave request without a linked employee profile.")
        return redirect("leave:overview")
    form = LeaveApplicationForm(request.POST or None, employee=employee)
    if request.method == "POST" and form.is_valid():
        create_leave_request(
            employee=employee,
            leave_type=form.cleaned_data["leave_type"],
            start_date=form.cleaned_data["start_date"],
            end_date=form.cleaned_data["end_date"],
            days=form.cleaned_data["calculated_days"],
            reason=form.cleaned_data.get("reason", ""),
            user=request.user,
            request=request,
        )
        messages.success(request, "Leave application submitted.")
        return redirect("leave:overview")
    return render(request, "leave/leave_form.html", {"form": form})


@login_required
def leave_resubmit(request, pk):
    employee = getattr(request.user, "employee_profile", None)
    company_ids = accessible_company_ids(request.user)
    if not employee or employee.company_id not in company_ids:
        raise PermissionDenied("A linked employee profile is required.")
    item = get_object_or_404(LeaveRequest.objects.select_related("leave_type"), pk=pk, employee=employee)
    if item.status != "RETURNED":
        messages.error(request, "Only a returned leave application can be resubmitted.")
        return redirect("leave:overview")
    initial = {"leave_type": item.leave_type, "start_date": item.start_date, "end_date": item.end_date, "reason": item.reason}
    form = LeaveApplicationForm(request.POST or None, employee=employee, initial=initial)
    if request.method == "POST" and form.is_valid():
        try:
            resubmit_leave_request(item, leave_type=form.cleaned_data["leave_type"], start_date=form.cleaned_data["start_date"], end_date=form.cleaned_data["end_date"], days=form.cleaned_data["calculated_days"], reason=form.cleaned_data.get("reason", ""), user=request.user, request=request)
            messages.success(request, "Corrected leave application resubmitted.")
            return redirect("workflow:applications")
        except Exception as exc:
            form.add_error(None, exc)
    return render(request, "leave/leave_form.html", {"form": form, "resubmitting": True})


@login_required
def leave_cancel(request, pk):
    if not (request.user.is_superuser or has_role(request.user, ROLE_SUPER_ADMIN, ROLE_HR_ADMIN)):
        raise PermissionDenied("Only HR/Super Admin may cancel an approved leave.")
    req = LeaveRequest.objects.select_related("employee", "leave_type").filter(employee__company_id__in=accessible_company_ids(request.user), pk=pk).first()
    if not req:
        raise PermissionDenied("Leave request is outside your company scope.")
    form = LeaveCancellationForm(request.POST or None)
    if request.method == "POST" and form.is_valid():
        try:
            cancel_approved_leave(req, user=request.user, reason=form.cleaned_data["reason"])
            messages.success(request, "Approved leave cancelled; balance and attendance were reconciled.")
            return redirect("leave:overview")
        except Exception as exc:
            form.add_error(None, exc)
    return render(request, "leave/leave_cancel.html", {"form": form, "leave_request": req})


@login_required
def leave_year_initialize(request):
    if not (request.user.is_superuser or has_role(request.user, ROLE_SUPER_ADMIN, ROLE_HR_ADMIN)):
        raise PermissionDenied("Only HR/Super Admin may initialize leave year balances.")
    from apps.organization.models import Company
    company_qs = Company.objects.filter(pk__in=accessible_company_ids(request.user), is_active=True)
    form = LeaveYearInitializeForm(request.POST or None, company_queryset=company_qs)
    if request.method == "POST" and form.is_valid():
        try:
            result = initialize_leave_year(company=form.cleaned_data["company"], year=form.cleaned_data["year"], user=request.user)
            messages.success(request, f"Leave year initialized: {result['created']} created, {result['updated']} refreshed before usage.")
            return redirect("leave:overview")
        except Exception as exc:
            form.add_error(None, exc)
    return render(request, "leave/leave_year_initialize.html", {"form": form})
