<?php
/**
 * PayrollTax — Tanzania statutory tax + contribution rates.
 *
 * Constants below are the rates effective 2025-07 onwards (post-Finance-Act-2025).
 * When TRA changes them, update here and note the effective date. All amounts
 * are TZS.
 *
 * Source references:
 *   * PAYE brackets — Income Tax Act (Amended by Finance Act 2020) Sch. 1
 *   * NSSF — Social Security Regulatory Authority (SSRA) 20% split 10ee/10er
 *   * SDL — Vocational Education & Training Act, 3.5% (reduced from 4% in 2019)
 *   * WCF — Workers Compensation Fund Act, employers pay 0.5–1% (using 0.5%
 *           for private-sector fleet operators)
 */
class PayrollTax
{
    /** PAYE monthly brackets. Format: [ceiling, fixed_base, marginal_pct]. */
    private const PAYE_BRACKETS = [
        // upper-bound TZS · fixed base · marginal rate on excess
        [ 270000,       0,     0.00],
        [ 520000,       0,     0.08],   // 8% on excess over 270,000
        [ 760000,   20000,     0.20],   // 20% on excess over 520,000
        [1000000,   68000,     0.25],   // 25% on excess over 760,000
        [PHP_INT_MAX, 128000,  0.30],   // 30% on excess over 1,000,000
    ];

    /** Default employee NSSF % (drivers table stores per-driver override). */
    public const DEFAULT_NSSF_EE_PCT = 10.00;

    /** Employer NSSF %, matched to employee. */
    public const NSSF_ER_PCT = 10.00;

    /** SDL — employer only. */
    public const SDL_PCT = 3.50;

    /** WCF — employer only. Private sector rate. */
    public const WCF_PCT = 0.50;

    /**
     * PAYE on monthly taxable income. Progressive with the bracket table
     * above. Returns TZS rounded to 2 dp.
     */
    public static function paye(float $taxable): float
    {
        if ($taxable <= 0) return 0.0;
        $lower = 0;
        foreach (self::PAYE_BRACKETS as $i => $b) {
            [$ceiling, $base, $rate] = $b;
            if ($taxable <= $ceiling) {
                $lowerBound = $i === 0 ? 0 : self::PAYE_BRACKETS[$i - 1][0];
                return round($base + ($taxable - $lowerBound) * $rate, 2);
            }
        }
        return 0.0; // unreachable
    }

    public static function nssfEmployee(float $gross, float $pct): float
    {
        return round($gross * max(0, min(100, $pct)) / 100, 2);
    }

    public static function nssfEmployer(float $gross): float
    {
        return round($gross * self::NSSF_ER_PCT / 100, 2);
    }

    public static function sdl(float $gross): float
    {
        return round($gross * self::SDL_PCT / 100, 2);
    }

    public static function wcf(float $gross): float
    {
        return round($gross * self::WCF_PCT / 100, 2);
    }

    /**
     * Full statutory calculation from a driver's gross pay.
     * $config = ['nssf_ee_pct' => X, 'health_ins' => Y]
     * Returns keyed array: paye, nssf_ee, nssf_er, sdl, wcf, health_ins,
     *                     net_pay (before manual deductions).
     */
    public static function computeAll(float $gross, array $config = []): array
    {
        $nssfEePct = (float)($config['nssf_ee_pct'] ?? self::DEFAULT_NSSF_EE_PCT);
        $healthIns = (float)($config['health_ins'] ?? 0);

        // NSSF employee reduces the PAYE-able amount per TRA rules (statutory contribution).
        $nssfEe  = self::nssfEmployee($gross, $nssfEePct);
        $taxable = max(0, $gross - $nssfEe);   // NSSF deductible before PAYE
        $paye    = self::paye($taxable);

        return [
            'paye'       => $paye,
            'nssf_ee'    => $nssfEe,
            'nssf_er'    => self::nssfEmployer($gross),
            'sdl'        => self::sdl($gross),
            'wcf'        => self::wcf($gross),
            'health_ins' => round($healthIns, 2),
            // net BEFORE manual "other_deductions" — caller subtracts those.
            'net_before_other' => round($gross - $paye - $nssfEe - $healthIns, 2),
        ];
    }
}
