"""
payroll/views/salary_structure_views.py
=========================================
Module 5 — FBV views still wired to salary_urls.py.

List/Nav/Form pages are handled by CBVs in payroll/cbv/salary_structure.py.
This file contains only the FBVs that remain active:
  - salary_structure_clone
  - grade_step_matrix + grade_step_matrix_save
  - ajax_grades_for_structure + ajax_steps_for_grade
  - progression_decision + progression_bulk_decision + progression_export
"""

import io
import json

import openpyxl
from django.contrib import messages
from django.http import HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.utils import timezone
from django.utils.translation import gettext_lazy as _

from horilla.decorators import login_required, permission_required

from payroll.forms.salary_structure_forms import (
    CloneSalaryStructureForm,
    GradeProgressionDecisionForm,
)
from payroll.models.salary_structure import (
    PROGRESSION_PENDING,
    PROGRESSION_REJECTED,
    GradeLevel,
    GradeProgressionProposal,
    GradeStepComponent,
    IncomeComponent,
    SalaryStructure,
    StepLevel,
)


# ── Clone salary structure ────────────────────────────────────────────────────

@login_required
@permission_required("payroll.add_salarystructure")
def salary_structure_clone(request, pk):
    source = get_object_or_404(SalaryStructure, pk=pk)
    form = CloneSalaryStructureForm(request.POST or None)
    if request.method == "POST" and form.is_valid():
        clone = source.clone(form.cleaned_data["new_name"])
        messages.success(request, _(f"Structure cloned as '{clone.name}'."))
        return redirect("salary-structure-view")
    return render(request, "payroll/salary_structure/clone_form.html", {"form": form, "source": source})


# ── Grade-Step Value Matrix ───────────────────────────────────────────────────

@login_required
@permission_required("payroll.view_gradestepcomponent")
def grade_step_matrix(request, grade_pk):
    """Matrix view: rows=steps, cols=components, cells=amounts."""
    grade = get_object_or_404(GradeLevel, pk=grade_pk)
    steps = grade.steps.filter(is_active=True).order_by("step")
    components = IncomeComponent.objects.filter(is_active=True).order_by("order")

    values = {
        (gsc.step_id, gsc.component_id): gsc.amount
        for gsc in GradeStepComponent.objects.filter(grade=grade)
    }
    matrix_values_json = json.dumps({
        f"amount_{step_id}_{comp_id}": str(amount)
        for (step_id, comp_id), amount in values.items()
    })

    return render(request, "payroll/salary_structure/grade_step_matrix.html", {
        "grade": grade,
        "steps": steps,
        "components": components,
        "values": values,
        "matrix_values_json": matrix_values_json,
    })


@login_required
@permission_required("payroll.change_gradestepcomponent")
def grade_step_matrix_save(request, grade_pk):
    """Bulk-save the matrix via POST."""
    grade = get_object_or_404(GradeLevel, pk=grade_pk)
    if request.method != "POST":
        return redirect("grade-step-matrix", grade_pk=grade_pk)

    steps = grade.steps.filter(is_active=True)
    components = IncomeComponent.objects.filter(is_active=True)

    for step in steps:
        for component in components:
            key = f"amount_{step.pk}_{component.pk}"
            val = request.POST.get(key, "0").strip() or "0"
            try:
                amount = float(val)
            except ValueError:
                amount = 0
            GradeStepComponent.objects.update_or_create(
                grade=grade, step=step, component=component,
                defaults={"amount": amount},
            )

    messages.success(request, _("Grade-step values saved."))
    return redirect("grade-step-matrix", grade_pk=grade_pk)


# ── AJAX cascade helpers ──────────────────────────────────────────────────────

@login_required
def ajax_grades_for_structure(request):
    structure_id = request.GET.get("structure_id")
    grades = []
    if structure_id:
        grades = list(
            GradeLevel.objects.filter(structure_id=structure_id, is_active=True)
            .order_by("level")
            .values("id", "level", "name")
        )
    return JsonResponse({"grades": grades})


@login_required
def ajax_steps_for_grade(request):
    grade_id = request.GET.get("grade_id")
    steps = []
    if grade_id:
        steps = list(
            StepLevel.objects.filter(grade_id=grade_id, is_active=True)
            .order_by("step")
            .values("id", "step", "description")
        )
    return JsonResponse({"steps": steps})


# ── Grade Progression decisions ───────────────────────────────────────────────

@login_required
@permission_required("payroll.change_gradeprogressionproposal")
def progression_decision(request, pk):
    """HR approves or rejects a single proposal."""
    proposal = get_object_or_404(GradeProgressionProposal, pk=pk, status=PROGRESSION_PENDING)
    form = GradeProgressionDecisionForm(request.POST or None)
    if request.method == "POST" and form.is_valid():
        decision = form.cleaned_data["decision"]
        notes = form.cleaned_data.get("notes", "")
        if decision == GradeProgressionDecisionForm.APPROVE:
            proposal.apply(approved_by_user=request.user)
            messages.success(request, _(f"Progression approved for {proposal.employee}."))
        else:
            proposal.status = PROGRESSION_REJECTED
            proposal.approved_by = request.user
            proposal.decision_date = timezone.now().date()
            proposal.notes = notes
            proposal.save(update_fields=["status", "approved_by", "decision_date", "notes"])
            messages.info(request, _(f"Progression rejected for {proposal.employee}."))
        return redirect("grade-progression-view")
    return render(request, "payroll/salary_structure/progression_decision.html",
                  {"proposal": proposal, "form": form})


@login_required
@permission_required("payroll.change_gradeprogressionproposal")
def progression_bulk_decision(request):
    """Bulk approve or reject selected proposals."""
    if request.method != "POST":
        return redirect("grade-progression-view")

    pks = request.POST.getlist("proposal_ids")
    action = request.POST.get("action")
    notes = request.POST.get("notes", "")
    count = 0

    for pk in pks:
        try:
            proposal = GradeProgressionProposal.objects.get(pk=pk, status=PROGRESSION_PENDING)
            if action == "approve":
                proposal.apply(approved_by_user=request.user)
            elif action == "reject":
                proposal.status = PROGRESSION_REJECTED
                proposal.approved_by = request.user
                proposal.decision_date = timezone.now().date()
                proposal.notes = notes
                proposal.save(update_fields=["status", "approved_by", "decision_date", "notes"])
            count += 1
        except GradeProgressionProposal.DoesNotExist:
            pass

    messages.success(request, _(f"{count} proposal(s) processed."))
    return redirect("grade-progression-view")


@login_required
@permission_required("payroll.view_gradeprogressionproposal")
def progression_export(request):
    """Export progressions as Excel."""
    wb = openpyxl.Workbook()
    ws = wb.active
    ws.title = "Grade Progressions"
    ws.append(["Employee", "Employee No.", "Department", "From Grade",
               "From Step", "To Grade", "To Step", "Type", "Proposed Date", "Status"])

    proposals = GradeProgressionProposal.objects.select_related(
        "employee__employee_work_info__department_id",
        "from_grade", "from_step", "to_grade", "to_step",
    ).order_by("employee__employee_first_name")

    status_filter = request.GET.get("status")
    if status_filter:
        proposals = proposals.filter(status=status_filter)

    for p in proposals:
        dept = ""
        try:
            dept = str(p.employee.employee_work_info.department_id)
        except Exception:
            pass
        ws.append([
            str(p.employee),
            getattr(getattr(p.employee, "employee_work_info", None), "employee_id", ""),
            dept,
            str(p.from_grade), f"Step {p.from_step.step}",
            str(p.to_grade), f"Step {p.to_step.step}",
            p.get_progression_type_display(),
            str(p.proposed_date),
            p.get_status_display(),
        ])

    buf = io.BytesIO()
    wb.save(buf)
    buf.seek(0)
    response = HttpResponse(
        buf,
        content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
    )
    response["Content-Disposition"] = 'attachment; filename="grade_progressions.xlsx"'
    return response
