"""Build payroll formula settings from company deduction catalog."""
from __future__ import annotations

import json
from decimal import Decimal

from human_resource.models import Deduction, HrPayrollSettings
from system_administration.models import CompanyProfile

DEFAULT_PAYE_CONFIG = {
    "personal_relief": "2400",
    "insurance_relief_rate": "0.15",
    "insurance_relief_cap": "5000",
    "housing_levy_rate": "0.015",
    "include_housing_levy_in_paye": True,
    "paye_band1_limit": "24000",
    "paye_band1_rate": "0.10",
    "paye_band2_limit": "8333",
    "paye_band2_rate": "0.25",
    "paye_band3_limit": "467667",
    "paye_band3_rate": "0.30",
    "paye_band4_limit": "300000",
    "paye_band4_rate": "0.325",
    "paye_band5_threshold": "800000",
    "paye_band5_rate": "0.35",
}

DEFAULT_NSSF_CONFIG = {
    "rate": "0.06",
    "tier1_limit": "8000",
    "tier2_limit": "72000",
}


def _decimal(value) -> Decimal:
    return Decimal(str(value or 0).replace(",", ""))


def _parse_config(raw) -> dict:
    if not raw:
        return {}
    if isinstance(raw, dict):
        return raw
    try:
        parsed = json.loads(str(raw))
        return parsed if isinstance(parsed, dict) else {}
    except (TypeError, ValueError, json.JSONDecodeError):
        return {}


def _infer_statutory_formula(deduction: Deduction) -> str:
    if deduction.statutory_formula:
        return str(deduction.statutory_formula)
    title = str(deduction.deduction_title or "").strip().upper()
    if title == "NSSF":
        return "nssf_tiered"
    if title in ("SHA", "SHIF"):
        return "sha_percentage"
    if title == "PAYE":
        return "paye_progressive"
    if "HOUSING" in title and "LEVY" in title:
        return "housing_levy"
    return ""


def default_payroll_settings_map() -> dict:
    return payroll_settings_to_map(HrPayrollSettings(company_profile_id=0))


def get_or_create_payroll_settings(company_profile: CompanyProfile) -> HrPayrollSettings:
    settings_obj, _created = HrPayrollSettings.objects.get_or_create(
        company_profile=company_profile,
    )
    return settings_obj


def payroll_settings_to_map(settings_obj: HrPayrollSettings) -> dict:
    """Legacy shape used by payroll_calculations — values come from deductions now."""
    return {
        "nssf_contribution_rate": str(settings_obj.nssf_contribution_rate),
        "nssf_tier1_limit": str(settings_obj.nssf_tier1_limit),
        "nssf_tier2_limit": str(settings_obj.nssf_tier2_limit),
        "shif_contribution_rate": str(settings_obj.shif_contribution_rate),
        "normalize_shif_to_sha": "true",
        "housing_levy_rate": str(settings_obj.housing_levy_rate),
        "include_housing_levy_in_paye": "true" if settings_obj.include_housing_levy_in_paye else "false",
        "paye_personal_relief": str(settings_obj.paye_personal_relief),
        "paye_insurance_relief_rate": str(settings_obj.paye_insurance_relief_rate),
        "paye_insurance_relief_cap": str(settings_obj.paye_insurance_relief_cap),
        "paye_band1_limit": str(settings_obj.paye_band1_limit),
        "paye_band1_rate": str(settings_obj.paye_band1_rate),
        "paye_band2_limit": str(settings_obj.paye_band2_limit),
        "paye_band2_rate": str(settings_obj.paye_band2_rate),
        "paye_band3_limit": str(settings_obj.paye_band3_limit),
        "paye_band3_rate": str(settings_obj.paye_band3_rate),
        "paye_band4_limit": str(settings_obj.paye_band4_limit),
        "paye_band4_rate": str(settings_obj.paye_band4_rate),
        "paye_band5_threshold": str(settings_obj.paye_band5_threshold),
        "paye_band5_rate": str(settings_obj.paye_band5_rate),
    }


def build_settings_from_deductions(deductions) -> dict:
    settings = default_payroll_settings_map()
    housing_from_catalog = False

    for deduction in deductions or []:
        formula = _infer_statutory_formula(deduction)
        cfg = _parse_config(deduction.formula_config)
        title = str(deduction.deduction_title or "").strip().upper()

        if formula == "nssf_tiered" or title == "NSSF":
            merged = {**DEFAULT_NSSF_CONFIG, **cfg}
            settings["nssf_contribution_rate"] = str(merged.get("rate", "0.06"))
            settings["nssf_tier1_limit"] = str(merged.get("tier1_limit", "8000"))
            settings["nssf_tier2_limit"] = str(merged.get("tier2_limit", "72000"))
            continue

        if formula == "sha_percentage" or title in ("SHA", "SHIF"):
            if str(deduction.deduction_module) == "percentage":
                settings["shif_contribution_rate"] = str(_decimal(deduction.deduction_value) / Decimal("100"))
            elif cfg.get("rate") is not None:
                settings["shif_contribution_rate"] = str(cfg.get("rate"))
            continue

        if formula == "housing_levy" or ("HOUSING" in title and "LEVY" in title):
            if str(deduction.deduction_module) == "percentage":
                settings["housing_levy_rate"] = str(_decimal(deduction.deduction_value) / Decimal("100"))
            elif cfg.get("rate") is not None:
                settings["housing_levy_rate"] = str(cfg.get("rate"))
            settings["include_housing_levy_in_paye"] = "true" if cfg.get("include_in_paye", True) else "false"
            housing_from_catalog = True
            continue

        if formula == "paye_progressive" or title == "PAYE":
            merged = {**DEFAULT_PAYE_CONFIG, **cfg}
            settings["paye_personal_relief"] = str(merged.get("personal_relief", "2400"))
            settings["paye_insurance_relief_rate"] = str(merged.get("insurance_relief_rate", "0.15"))
            settings["paye_insurance_relief_cap"] = str(merged.get("insurance_relief_cap", "5000"))
            if not housing_from_catalog:
                settings["housing_levy_rate"] = str(merged.get("housing_levy_rate", "0.015"))
                settings["include_housing_levy_in_paye"] = (
                    "true" if merged.get("include_housing_levy_in_paye", True) else "false"
                )
            for key in (
                "paye_band1_limit", "paye_band1_rate",
                "paye_band2_limit", "paye_band2_rate",
                "paye_band3_limit", "paye_band3_rate",
                "paye_band4_limit", "paye_band4_rate",
                "paye_band5_threshold", "paye_band5_rate",
            ):
                if key in merged:
                    settings[key] = str(merged[key])

    return settings


def resolve_payroll_settings(company_profile: CompanyProfile | None = None) -> dict:
    if company_profile is None:
        return default_payroll_settings_map()
    deductions = company_profile.company_deductions.filter(recycle_bin=False).order_by("-id")
    return build_settings_from_deductions(deductions)


def deduction_row_map(deduction: Deduction) -> dict:
    return {
        "deduction_id": str(deduction.id),
        "deduction_title": deduction.deduction_title,
        "deduction_description": deduction.deduction_description,
        "deduction_type": deduction.deduction_type,
        "deduction_module": deduction.deduction_module,
        "deduction_value": deduction.deduction_value,
        "statutory_formula": deduction.statutory_formula or "",
        "formula_config": deduction.formula_config or "",
        "date_effective_from": deduction.date_effective_from,
        "date_effective_to": deduction.date_effective_to,
    }
