<?php

namespace App\Helpers\Support;

class SegmentInvoiceTaxesHelper
{
  //* Constants
    private const TAX_ISR  = '001';
    private const TAX_IVA  = '002';
    private const TAX_IEPS = '003';


  //* Base
    /**
     * Clasifica los Impuestos federales que sean otorgados, con base a los 3 impuestos Federales base de México: IVA, IEPS e ISR
     *
     * @param array $items  Arreglo de Impuestos Federales a ordenar, se espera una estructura:
     *      - tax (Clave del Impuesto Federal | 001, 002 o 003),
     *      - tax_application ('tr'|'rt' => transferred'|'withheld),
     *      - rate_quote (0.xx),
     *      - charges (importe)
     *
     * @return array Totales y desglose útiles.
     */
    public static function aggregateFederalTaxes(array $items): array {
        $totals = [
            'iva_transferred' => 0.0,
            'iva_withheld'    => 0.0,
            'ieps_transferred'=> 0.0,
            'isr_withheld'    => 0.0,
        ];

        foreach ($items as $item) {
            $conceptTaxes = $item['taxes'] ?? [];
            if (!$conceptTaxes) continue;

            foreach ($conceptTaxes as $t) {
                $taxCode = (string)($t['tax'] ?? '');
                $charge  = self::parseFloat($t['charges'] ?? 0);
                $appRaw  = (string)($t['tax_application'] ?? 'tr');

                // Normaliza 'tr'/'rt' -> 'transferred'/'withheld'
                $app = self::normalizeApplication($appRaw);

                // Regla dura por impuesto
                switch ($taxCode) {
                    case self::TAX_IVA:
                        // IVA respeta 'tr'/'rt'
                        if ($app === 'withheld') {
                            $totals['iva_withheld'] = self::sumAsFloat($totals['iva_withheld'], $charge);
                        } else {
                            $totals['iva_transferred'] = self::sumAsFloat($totals['iva_transferred'], $charge);
                        }
                        break;

                    case self::TAX_IEPS:
                        // IEPS siempre trasladado
                        $totals['ieps_transferred'] = self::sumAsFloat($totals['ieps_transferred'], $charge);
                        break;

                    case self::TAX_ISR:
                        // ISR siempre retenido
                        $totals['isr_withheld'] = self::sumAsFloat($totals['isr_withheld'], $charge);
                        break;

                    default:
                        // Si algún día agregas otro impuesto federal, por defecto respeta la app normalizada:
                        // if ($app === 'withheld') {
                            // Podrías crear llaves dinámicas por código si lo necesitas.
                        // } else {}
                        break;
                }
            }
        }

        // Totales consolidados
        $transferred = self::sumAsFloat($totals['iva_transferred'], $totals['ieps_transferred']); // + otros trasladados si agregas más
        $withheld    = self::sumAsFloat($totals['iva_withheld'],    $totals['isr_withheld']);     // + otros retenidos si agregas más
        $taxes_total = round($transferred - $withheld, 2);

        return [
            // Desglose por impuesto
            'iva_transferred'     => $totals['iva_transferred'],
            'iva_withheld'        => $totals['iva_withheld'],
            'ieps_transferred'    => $totals['ieps_transferred'],
            'isr_withheld'        => $totals['isr_withheld'],

            // Totales consolidados
            'transferred_total'   => $transferred,
            'withheld_total'      => $withheld,
            'taxes_total'         => $taxes_total,
        ];
    }

    /**
     * Agrupa los Impuestos Locales que sean otorgados
     *
     * @param array $items  Arreglo de Impuestos Locales a agrupar, se espera una estructura:
     *      - name (Impuesto),
     *      - tax_application ('tr'|'rt' => transferred'|'withheld),
     *      - rate_quote (0.xx),
     *      - charges (importe)
     *
     * @return array Totales y desglose útiles.
     */
    public static function aggregateLocalTaxes(array $items): array
    {
        $byKey = [
            'transferred' => [],
            'withheld'    => [],
        ];

        foreach ($items as $item) {
            $locals = $item['local_taxes'] ?? [];
            if (!$locals) continue;

            foreach ($locals as $lt) {
                $app   = self::normalizeApplication((string)($lt['tax_application'] ?? 'tr'));
                $name  = (string)($lt['name'] ?? 'LOCAL');
                $rate  = self::parseFloat($lt['rate_quote'] ?? 0.0, 6);
                $chg   = self::parseFloat($lt['charges'] ?? 0.0);

                $bucket = ($app === 'withheld') ? 'withheld' : 'transferred';
                $key    = $name . '|' . ($rate === null ? 'null' : number_format((float)$rate, 6, '.', ''));

                if (!isset($byKey[$bucket][$key])) {
                    $byKey[$bucket][$key] = [
                        'name'   => $name,
                        'rate'   => $rate,
                        'charge' => 0.0,
                    ];
                }
                $byKey[$bucket][$key]['charge'] = self::sumAsFloat($byKey[$bucket][$key]['charge'], $chg);
            }
        }

        // Arreglo final y totales
        $transferred = array_values($byKey['transferred']);
        $withheld    = array_values($byKey['withheld']);

        foreach ([$transferred, $withheld] as &$arr) {
            foreach ($arr as &$row) {
                if ($row['rate'] !== null) $row['rate'] = (float)$row['rate'];
                $row['charge'] = round($row['charge'], 2);
            }
        }
        unset($arr, $row);

        $transferred_total = round(array_sum(array_column($transferred, 'charge')), 2);
        $withheld_total    = round(array_sum(array_column($withheld, 'charge')), 2);
        $locals_total      = round($transferred_total - $withheld_total, 2);

        return [
            'local_taxes' => [
                'transferred' => $transferred,
                'withheld'    => $withheld,
            ],
            'transferred_total' => $transferred_total,
            'withheld_total'    => $withheld_total,
            'local_taxes_total' => $locals_total,
        ];
    }



  //* Support Function
    public static function normalizeApplication(string $app): string
    {
        $app = strtolower(trim($app));
        if ($app === 'rt' || $app === 'withheld')    return 'withheld';
        if ($app === 'tr' || $app === 'transferred') return 'transferred';

        return 'transferred';
    }

    /**
     * Convierte un string numérico a su respectivo valor flotante
     *
     * @param string $value     Valor a trasformar
     * @param int    $decimals  Cantidad de Decimales permitidos
     *
     * @return float Valor formateado
     */
    public static function parseFloat($value, $decimals = 2): float {
        return round((float)$value ?? 0.0, $decimals);
    }

    /**
     * Se encarga de sumar dos valores, asegurando en el proceso que se preserve cierta cantidad de decimales
     *
     * @param string $a         Valor A base
     * @param string $b         Valor B a sumar
     * @param int    $decimals  Cantidad de Decimales permitidos
     *
     * @return float Resultado de la Suma
    */
    public static function sumAsFloat(float $a, float $b, int $decimals = 2): float {
        return round($a + $b, $decimals);
    }

}
