from __future__ import annotations

from dataclasses import dataclass, field
from decimal import Decimal

from .enums import EmploymentTaxMode
from .money import money


@dataclass(frozen=True)
class TaxBracket:
    lower_bound: Decimal
    upper_bound: Decimal | None
    rate: Decimal
    base_tax: Decimal

    def applies_to(self, amount: Decimal) -> bool:
        if amount < self.lower_bound:
            return False
        if self.upper_bound is None:
            return True
        return amount <= self.upper_bound


@dataclass(frozen=True)
class ProgressiveTaxTable:
    brackets: tuple[TaxBracket, ...]

    def compute(self, amount: Decimal) -> Decimal:
        taxable = money(max(amount, Decimal("0")))
        for bracket in self.brackets:
            if bracket.applies_to(taxable):
                return money(bracket.base_tax + ((taxable - bracket.lower_bound) * bracket.rate))
        return money(Decimal("0"))


@dataclass(frozen=True)
class LSTBand:
    lower_exclusive: Decimal
    upper_inclusive: Decimal | None
    amount: Decimal

    def applies_to(self, amount: Decimal) -> bool:
        if amount <= self.lower_exclusive:
            return False
        if self.upper_inclusive is None:
            return True
        return amount <= self.upper_inclusive


@dataclass(frozen=True)
class LocalServiceTaxPolicy:
    mode: str = "quarterly"
    charge_month: int = 7
    quarterly_months: tuple[int, ...] = (7, 8, 9, 10)
    annual_bands: tuple[LSTBand, ...] = field(default_factory=tuple)
    quarterly_bands: tuple[LSTBand, ...] = field(default_factory=tuple)

    def compute(self, *, month: int, monthly_income_after_nssf: Decimal) -> Decimal:
        income = money(monthly_income_after_nssf)
        if self.mode == "none":
            return money(0)
        if self.mode == "single_month":
            if month != self.charge_month:
                return money(0)
            bands = self.annual_bands
        else:
            if month not in self.quarterly_months:
                return money(0)
            bands = self.quarterly_bands

        for band in bands:
            if band.applies_to(income):
                return money(band.amount)
        return money(0)


@dataclass(frozen=True)
class UgandaPayrollPolicy:
    resident_paye: ProgressiveTaxTable
    non_resident_paye: ProgressiveTaxTable
    secondary_rate: Decimal = Decimal("0.30")
    secondary_above_ten_million_rate: Decimal = Decimal("0.40")
    nssf_employee_rate: Decimal = Decimal("0.05")
    nssf_employer_rate: Decimal = Decimal("0.10")
    lst_policy: LocalServiceTaxPolicy = field(default_factory=lambda: default_lst_policy())
    deduct_lst_before_paye: bool = True

    def compute_paye(self, mode: EmploymentTaxMode, chargeable_income: Decimal) -> Decimal:
        taxable = money(max(chargeable_income, Decimal("0")))
        if mode == EmploymentTaxMode.SECONDARY:
            return money(taxable * self.secondary_rate)
        if mode == EmploymentTaxMode.SECONDARY_ABOVE_TEN_MILLION:
            return money(taxable * self.secondary_above_ten_million_rate)
        if mode == EmploymentTaxMode.NON_RESIDENT:
            return self.non_resident_paye.compute(taxable)
        return self.resident_paye.compute(taxable)

    @classmethod
    def uganda_2026(cls, *, lst_mode: str = "quarterly", lst_charge_month: int = 7) -> "UgandaPayrollPolicy":
        return cls(
            resident_paye=default_resident_paye_table(),
            non_resident_paye=default_non_resident_paye_table(),
            lst_policy=default_lst_policy(mode=lst_mode, charge_month=lst_charge_month),
        )


def default_resident_paye_table() -> ProgressiveTaxTable:
    return ProgressiveTaxTable(
        brackets=(
            TaxBracket(Decimal("0"), Decimal("235000"), Decimal("0.00"), Decimal("0")),
            TaxBracket(Decimal("235000"), Decimal("335000"), Decimal("0.10"), Decimal("0")),
            TaxBracket(Decimal("335000"), Decimal("410000"), Decimal("0.20"), Decimal("10000")),
            TaxBracket(Decimal("410000"), Decimal("10000000"), Decimal("0.30"), Decimal("25000")),
            TaxBracket(Decimal("10000000"), None, Decimal("0.40"), Decimal("2902000")),
        )
    )


def default_non_resident_paye_table() -> ProgressiveTaxTable:
    return ProgressiveTaxTable(
        brackets=(
            TaxBracket(Decimal("0"), Decimal("335000"), Decimal("0.10"), Decimal("0")),
            TaxBracket(Decimal("335000"), Decimal("410000"), Decimal("0.20"), Decimal("33500")),
            TaxBracket(Decimal("410000"), Decimal("10000000"), Decimal("0.30"), Decimal("48500")),
            TaxBracket(Decimal("10000000"), None, Decimal("0.40"), Decimal("2925500")),
        )
    )


def default_lst_policy(*, mode: str = "quarterly", charge_month: int = 7) -> LocalServiceTaxPolicy:
    annual_bands = (
        LSTBand(Decimal("100000"), Decimal("200000"), Decimal("5000")),
        LSTBand(Decimal("200000"), Decimal("300000"), Decimal("10000")),
        LSTBand(Decimal("300000"), Decimal("400000"), Decimal("20000")),
        LSTBand(Decimal("400000"), Decimal("500000"), Decimal("30000")),
        LSTBand(Decimal("500000"), Decimal("600000"), Decimal("40000")),
        LSTBand(Decimal("600000"), Decimal("700000"), Decimal("60000")),
        LSTBand(Decimal("700000"), Decimal("800000"), Decimal("70000")),
        LSTBand(Decimal("800000"), Decimal("900000"), Decimal("80000")),
        LSTBand(Decimal("900000"), Decimal("1000000"), Decimal("90000")),
        LSTBand(Decimal("1000000"), None, Decimal("100000")),
    )
    quarterly_bands = (
        LSTBand(Decimal("100000"), Decimal("200000"), Decimal("1250")),
        LSTBand(Decimal("200000"), Decimal("300000"), Decimal("2500")),
        LSTBand(Decimal("300000"), Decimal("400000"), Decimal("5000")),
        LSTBand(Decimal("400000"), Decimal("500000"), Decimal("7500")),
        LSTBand(Decimal("500000"), Decimal("600000"), Decimal("10000")),
        LSTBand(Decimal("600000"), Decimal("700000"), Decimal("15000")),
        LSTBand(Decimal("700000"), Decimal("800000"), Decimal("17500")),
        LSTBand(Decimal("800000"), Decimal("900000"), Decimal("20000")),
        LSTBand(Decimal("900000"), Decimal("1000000"), Decimal("22500")),
        LSTBand(Decimal("1000000"), None, Decimal("25000")),
    )
    return LocalServiceTaxPolicy(
        mode=mode,
        charge_month=charge_month,
        annual_bands=annual_bands,
        quarterly_bands=quarterly_bands,
    )
