"""
payroll/step_upgrade_scheduler.py
===================================
Daily job: check employees due for grade/step progression and create proposals.
Also sends advance notifications at 2 months and 1 month before upgrade date.

Rules:
  - Employee has a payroll profile with grade + step assigned
  - next_step_upgrade_date is today or in the past → create PENDING proposal
  - If employee is at last step of current grade → propose move to Grade+1 Step 1
    (HR must approve, per business rule)
  - Notifications sent 2 months and 1 month before due date to HR group
"""

from datetime import date

from dateutil.relativedelta import relativedelta
from django.contrib.auth.models import Group
from notifications.signals import notify


def check_step_upgrades():
    """Main scheduler entry point — called daily."""
    try:
        from payroll.models.salary_structure import (
            PROGRESSION_AUTO,
            PROGRESSION_PENDING,
            EmployeePayrollProfile,
            GradeLevel,
            GradeProgressionProposal,
            StepLevel,
        )
    except Exception:
        return  # migrations not yet applied

    today = date.today()

    profiles = EmployeePayrollProfile.objects.filter(
        is_active=True,
        grade__isnull=False,
        step__isnull=False,
    ).select_related("employee", "grade", "step", "grade__structure")

    for profile in profiles:
        _check_and_notify(profile, today)


def _check_and_notify(profile, today: date):
    from payroll.models.salary_structure import (
        PROGRESSION_AUTO,
        PROGRESSION_PENDING,
        GradeLevel,
        GradeProgressionProposal,
        StepLevel,
    )

    # Determine reference date for upgrade calculation
    ref_date = profile.last_step_upgrade_date
    if not ref_date:
        # Fall back to employee onboarding date
        try:
            ref_date = profile.employee.employee_work_info.date_joining
        except Exception:
            return  # no reference date, skip

    if not ref_date:
        return

    interval = profile.step_upgrade_interval_months or 12
    next_upgrade = ref_date + relativedelta(months=interval)

    # Update next_step_upgrade_date if changed
    if profile.next_step_upgrade_date != next_upgrade:
        profile.next_step_upgrade_date = next_upgrade
        profile.save(update_fields=["next_step_upgrade_date"])

    # ---- Advance notifications (2 months and 1 month before) ----
    for months_before in [2, 1]:
        notify_date = next_upgrade - relativedelta(months=months_before)
        if today == notify_date:
            _notify_hr(
                profile,
                f"Grade/step upgrade due in {months_before} month(s) for {profile.employee}.",
            )

    # ---- Create proposal when upgrade date reached ----
    if today >= next_upgrade:
        # Check no pending proposal already exists
        existing = GradeProgressionProposal.objects.filter(
            employee=profile.employee,
            status=PROGRESSION_PENDING,
        ).exists()
        if existing:
            return

        # Determine target grade/step
        current_grade = profile.grade
        current_step = profile.step
        max_step = current_grade.max_step

        if current_step.step < (max_step or 0):
            # Move to next step in same grade
            try:
                to_step = StepLevel.objects.get(
                    grade=current_grade,
                    step=current_step.step + 1,
                    is_active=True,
                )
                to_grade = current_grade
            except StepLevel.DoesNotExist:
                return
        else:
            # At last step — propose move to next grade, step 1
            next_grade = (
                GradeLevel.objects.filter(
                    structure=current_grade.structure,
                    level__gt=current_grade.level,
                    is_active=True,
                )
                .order_by("level")
                .first()
            )
            if not next_grade:
                # No higher grade — notify HR but don't create proposal
                _notify_hr(
                    profile,
                    f"{profile.employee} is at the highest grade/step. Manual review required.",
                )
                return
            try:
                to_step = StepLevel.objects.get(grade=next_grade, step=1, is_active=True)
                to_grade = next_grade
            except StepLevel.DoesNotExist:
                return

        GradeProgressionProposal.objects.create(
            employee=profile.employee,
            from_grade=current_grade,
            from_step=current_step,
            to_grade=to_grade,
            to_step=to_step,
            progression_type=PROGRESSION_AUTO,
        )
        _notify_hr(
            profile,
            f"Auto-progression proposal created for {profile.employee}: "
            f"{current_grade.name}/Step {current_step.step} → "
            f"{to_grade.name}/Step {to_step.step}. Please review and approve.",
        )


def _notify_hr(profile, message: str):
    """Send notification to all members of the HR group."""
    try:
        from django.contrib.auth import get_user_model
        User = get_user_model()
        hr_group = Group.objects.get(name="HR")
        for user in hr_group.user_set.filter(is_active=True):
            notify.send(
                profile.employee,
                recipient=user,
                verb="Grade/Step Progression Alert",
                description=message,
            )
    except Group.DoesNotExist:
        # HR group not configured — silently skip
        pass
    except Exception:
        pass
