from decimal import Decimal

from django import forms

from accounts.models import Branch
from .models import (
    WEEKDAY_CHOICES, Employee, Holiday, PayrollSetting, Payslip, SalaryAdvance,
)

MONEY = {"class": "form-control", "step": "0.01", "min": "0"}


def _branch_queryset(company):
    return Branch.objects.filter(company=company, is_active=True) if company is not None else Branch.objects.none()


class EmployeeForm(forms.ModelForm):
    class Meta:
        model = Employee
        fields = [
            "branch", "employee_code", "full_name", "designation", "department",
            "join_date", "status", "left_date",
            "phone", "email", "national_id", "gender", "date_of_birth",
            "address", "emergency_contact",
            "basic_salary", "house_rent", "medical_allowance", "transport_allowance", "other_allowance",
            "payment_method", "account_details", "notes",
        ]
        widgets = {
            "branch": forms.Select(attrs={"class": "form-control"}),
            "employee_code": forms.TextInput(attrs={"class": "form-control", "placeholder": "Auto (EMP-0001)"}),
            "full_name": forms.TextInput(attrs={"class": "form-control"}),
            "designation": forms.TextInput(attrs={"class": "form-control", "list": "designation-list"}),
            "department": forms.TextInput(attrs={"class": "form-control", "list": "department-list"}),
            "join_date": forms.DateInput(attrs={"class": "form-control", "type": "date"}),
            "status": forms.Select(attrs={"class": "form-control"}),
            "left_date": forms.DateInput(attrs={"class": "form-control", "type": "date"}),
            "phone": forms.TextInput(attrs={"class": "form-control"}),
            "email": forms.EmailInput(attrs={"class": "form-control"}),
            "national_id": forms.TextInput(attrs={"class": "form-control"}),
            "gender": forms.Select(attrs={"class": "form-control"}),
            "date_of_birth": forms.DateInput(attrs={"class": "form-control", "type": "date"}),
            "address": forms.Textarea(attrs={"class": "form-control", "rows": 2}),
            "emergency_contact": forms.TextInput(attrs={"class": "form-control"}),
            "basic_salary": forms.NumberInput(attrs=MONEY),
            "house_rent": forms.NumberInput(attrs=MONEY),
            "medical_allowance": forms.NumberInput(attrs=MONEY),
            "transport_allowance": forms.NumberInput(attrs=MONEY),
            "other_allowance": forms.NumberInput(attrs=MONEY),
            "payment_method": forms.Select(attrs={"class": "form-control"}),
            "account_details": forms.TextInput(attrs={"class": "form-control"}),
            "notes": forms.Textarea(attrs={"class": "form-control", "rows": 2}),
        }

    def __init__(self, *args, company=None, default_branch=None, **kwargs):
        super().__init__(*args, **kwargs)
        self.company = company
        self.fields["branch"].queryset = _branch_queryset(company)
        self.fields["branch"].empty_label = None
        if default_branch is not None and not self.instance.pk:
            self.fields["branch"].initial = default_branch.pk
        self.fields["employee_code"].required = False
        self.fields["gender"].choices = [("", "—")] + list(Employee.GENDER_CHOICES)

    def clean_employee_code(self):
        code = (self.cleaned_data.get("employee_code") or "").strip()
        if not code:
            return ""
        clash = Employee.objects.filter(company=self.company, employee_code__iexact=code)
        if self.instance.pk:
            clash = clash.exclude(pk=self.instance.pk)
        if clash.exists():
            raise forms.ValidationError("This employee code is already used.")
        return code

    def clean(self):
        cleaned = super().clean()
        join, left, status = cleaned.get("join_date"), cleaned.get("left_date"), cleaned.get("status")
        if join and left and left < join:
            self.add_error("left_date", "Leaving date cannot be before the joining date.")
        if status in (Employee.STATUS_RESIGNED, Employee.STATUS_TERMINATED) and not left:
            self.add_error("left_date", "Enter the last working day for resigned / terminated staff.")
        return cleaned


class PayslipEditForm(forms.ModelForm):
    class Meta:
        model = Payslip
        fields = ["bonus", "other_deduction", "advance_deduction", "notes"]
        widgets = {
            "bonus": forms.NumberInput(attrs=MONEY),
            "other_deduction": forms.NumberInput(attrs=MONEY),
            "advance_deduction": forms.NumberInput(attrs=MONEY),
            "notes": forms.TextInput(attrs={"class": "form-control"}),
        }

    def __init__(self, *args, outstanding_advance=Decimal("0"), **kwargs):
        super().__init__(*args, **kwargs)
        self.outstanding_advance = outstanding_advance
        self.fields["advance_deduction"].help_text = f"Outstanding advance: {outstanding_advance:.2f}"

    def clean(self):
        cleaned = super().clean()
        for name in ("bonus", "other_deduction", "advance_deduction"):
            if cleaned.get(name) is not None and cleaned[name] < 0:
                self.add_error(name, "Cannot be negative.")
        adv = cleaned.get("advance_deduction")
        if adv is not None and adv > self.outstanding_advance:
            self.add_error("advance_deduction", "More than the employee's outstanding advance.")
        if not self.errors:
            inst = self.instance
            earnings = inst.gross_salary + inst.overtime_amount + (cleaned.get("bonus") or 0)
            deductions = inst.absence_deduction + (cleaned.get("advance_deduction") or 0) + (cleaned.get("other_deduction") or 0)
            if deductions > earnings:
                raise forms.ValidationError("Deductions are more than the earnings - net salary would be negative.")
        return cleaned


class PayForm(forms.Form):
    paid_date = forms.DateField(widget=forms.DateInput(attrs={"class": "form-control", "type": "date"}))
    payment_method = forms.ChoiceField(
        choices=Employee.PAY_METHOD_CHOICES, widget=forms.Select(attrs={"class": "form-select"}),
    )
    reference = forms.CharField(
        required=False, max_length=100,
        widget=forms.TextInput(attrs={"class": "form-control", "placeholder": "Txn / cheque no. (optional)"}),
    )


class AdvanceForm(forms.ModelForm):
    class Meta:
        model = SalaryAdvance
        fields = ["employee", "date", "amount", "monthly_deduction", "reason"]
        widgets = {
            "employee": forms.Select(attrs={"class": "form-control"}),
            "date": forms.DateInput(attrs={"class": "form-control", "type": "date"}),
            "amount": forms.NumberInput(attrs={**MONEY, "min": "0.01"}),
            "monthly_deduction": forms.NumberInput(attrs=MONEY),
            "reason": forms.TextInput(attrs={"class": "form-control"}),
        }

    def __init__(self, *args, employees=None, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["employee"].queryset = employees if employees is not None else Employee.objects.none()

    def clean(self):
        cleaned = super().clean()
        amount, monthly = cleaned.get("amount"), cleaned.get("monthly_deduction")
        if amount is not None and amount <= 0:
            self.add_error("amount", "Enter an amount greater than zero.")
        if monthly is not None and amount is not None and monthly > amount:
            self.add_error("monthly_deduction", "Cannot be more than the advance amount.")
        return cleaned


class HolidayForm(forms.ModelForm):
    class Meta:
        model = Holiday
        fields = ["date", "name", "branch"]
        widgets = {
            "date": forms.DateInput(attrs={"class": "form-control", "type": "date"}),
            "name": forms.TextInput(attrs={"class": "form-control", "placeholder": "e.g. Eid-ul-Fitr"}),
            "branch": forms.Select(attrs={"class": "form-control"}),
        }

    def __init__(self, *args, company=None, **kwargs):
        super().__init__(*args, **kwargs)
        self.company = company
        self.fields["branch"].queryset = _branch_queryset(company)
        self.fields["branch"].required = False
        self.fields["branch"].empty_label = "All branches"

    def clean(self):
        cleaned = super().clean()
        date, branch = cleaned.get("date"), cleaned.get("branch")
        if date:
            clash = Holiday.objects.filter(company=self.company, date=date)
            clash = clash.filter(branch=branch.pk) if branch else clash.filter(branch__isnull=True)
            if clash.exists():
                raise forms.ValidationError("A holiday is already set for this date and branch.")
        return cleaned


class PayrollSettingForm(forms.ModelForm):
    weekly_off = forms.MultipleChoiceField(
        choices=WEEKDAY_CHOICES, required=False, label="Weekly off days",
        widget=forms.CheckboxSelectMultiple,
    )

    class Meta:
        model = PayrollSetting
        fields = ["work_hours_per_day", "overtime_multiplier", "late_days_per_absence", "unmarked_as_absent"]
        widgets = {
            "work_hours_per_day": forms.NumberInput(attrs={"class": "form-control", "step": "0.5", "min": "1", "max": "24"}),
            "overtime_multiplier": forms.NumberInput(attrs={"class": "form-control", "step": "0.25", "min": "1"}),
            "late_days_per_absence": forms.NumberInput(attrs={"class": "form-control", "min": "0"}),
            "unmarked_as_absent": forms.CheckboxInput(attrs={"class": "form-check-input"}),
        }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["weekly_off"].initial = [str(d) for d in sorted(self.instance.weekly_off_set)]

    def save(self, commit=True):
        obj = super().save(commit=False)
        obj.weekly_off_days = ",".join(sorted(self.cleaned_data.get("weekly_off", [])))
        if commit:
            obj.save()
        return obj
