<?php

namespace App\Rules\Invoicing;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Str;

class PhysicalTaxIdentifier implements ValidationRule
{
    /**
     * ISO 3166-1 alpha-3 (e.g. MEX, USA, CAN)
     */
    protected string $country;

    public function __construct(string $country)
    {
        $this->country = Str::upper(trim($country));
    }

    /**
     * Normaliza el valor de entrada:
     * - Recorta espacios
     * - Lleva a mayúsculas
     * - Elimina espacios internos
     * - Conserva guiones SOLO en países que los requieren para el patrón (SSN USA, RIF VEN, RUT CHL, NIT GTM opcional, PAN flexible)
     */
    protected function normalize(string $value): string
    {
        $v = trim($value);

        // Quita espacios y convierte a mayúsculas
        $v = Str::of($v)->upper()->replace(' ', '')->toString();

        // Para la mayoría, quitamos guiones. Los reinsertamos si el patrón lo requiere.
        $keepHyphen = in_array($this->country, ['USA', 'VEN', 'CHL', 'GTM', 'PAN'], true);
        if (!$keepHyphen) {
            $v = str_replace('-', '', $v);
        }

        return $v;
    }

    /**
     * Devuelve el regex (PHP) y un ejemplo/título por país.
     * IMPORTANTE: todas las expresiones están ancladas ^$
     */
    protected function patterns(): array
    {
        return [
            // México (RFC PF: 13)
            'MEX' => [
                'regex' => '/^[A-ZÑ&]{4}(\d{2})(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])[A-Z0-9]{3}$/u',
                'title' => 'RFC persona física (4 letras, fecha AAMMDD, 3 alfanuméricos)',
            ],

            // Estados Unidos (SSN clásico; ITIN requiere reglas diferentes)
            'USA' => [
                'regex' => '/^\d{3}\-\d{2}\-\d{4}$/',
                'title' => 'SSN (formato: 123-45-6789)',
            ],

            // Canadá (SIN 9 dígitos)
            'CAN' => [
                'regex' => '/^\d{9}$/',
                'title' => 'SIN canadiense (9 dígitos; verificación Luhn)',
            ],

            // Francia (Numéro fiscal PF)
            'FRA' => [
                'regex' => '/^\d{13}$/',
                'title' => 'Numéro fiscal (PF) francés (13 dígitos)',
            ],

            // Italia (Codice Fiscale)
            'ITA' => [
                'regex' => '/^[A-Z]{6}[0-9]{2}[A-Z][0-9]{2}[A-Z][0-9]{3}[A-Z]$/',
                'title' => 'Codice Fiscale (PF) - 16 caracteres',
            ],

            // España (NIF/NIE)
            'ESP' => [
                'regex' => '/^(\d{8}[A-HJ-NP-TV-Z]|[XYZ]\d{7}[A-HJ-NP-TV-Z])$/',
                'title' => 'NIF (8 dígitos+letra) o NIE (X|Y|Z + 7 dígitos + letra)',
            ],

            // América Central y Caribe
            'GTM' => ['regex' => '/^\d{7,10}(\-\d)?$/', 'title' => 'NIT GTM (DV opcional)'],
            'BLZ' => ['regex' => '/^[A-Z0-9]{6,15}$/', 'title' => 'ID fiscal (PF) 6-15'],
            'SLV' => ['regex' => '/^\d{14}$/', 'title' => 'NIT El Salvador (14)'],
            'HND' => ['regex' => '/^\d{14}$/', 'title' => 'RTN Honduras (14)'],
            'NIC' => ['regex' => '/^\d{14}$/', 'title' => 'Identidad/RUC Nicaragua (14)'],
            'CRI' => ['regex' => '/^\d{9}$/',  'title' => 'Cédula Costa Rica (9)'],
            'PAN' => ['regex' => '/^[A-Z0-9\-]{8,20}$/', 'title' => 'PF Panamá (8-20, guiones/letras)'],

            'BHS' => ['regex' => '/^[A-Z0-9]{6,15}$/', 'title' => 'Bahamas (6-15)'],
            'CUB' => ['regex' => '/^\d{11}$/', 'title' => 'Carné Cuba (11)'],
            'JAM' => ['regex' => '/^\d{9}$/',  'title' => 'TRN Jamaica (9)'],
            'HTI' => ['regex' => '/^\d{10}$/', 'title' => 'NIF Haití (10)'],
            'DOM' => ['regex' => '/^\d{11}$/', 'title' => 'Cédula Dominicana (11)'],
            'KNA' => ['regex' => '/^[A-Z0-9]{6,15}$/', 'title' => 'St. Kitts & Nevis (6-15)'],
            'ATG' => ['regex' => '/^[A-Z0-9]{6,15}$/', 'title' => 'Antigua y Barbuda (6-15)'],
            'DMA' => ['regex' => '/^[A-Z0-9]{6,15}$/', 'title' => 'Dominica (6-15)'],
            'LCA' => ['regex' => '/^[A-Z0-9]{6,15}$/', 'title' => 'Santa Lucía (6-15)'],
            'VCT' => ['regex' => '/^[A-Z0-9]{6,15}$/', 'title' => 'San Vicente (6-15)'],
            'BRB' => ['regex' => '/^[A-Z0-9]{6,15}$/', 'title' => 'Barbados (6-15)'],
            'TTO' => ['regex' => '/^[A-Z0-9]{6,15}$/', 'title' => 'Trinidad y Tobago (6-15)'],
            'GRD' => ['regex' => '/^[A-Z0-9]{6,15}$/', 'title' => 'Granada (6-15)'],

            // Sudamérica
            'COL' => ['regex' => '/^\d{9,10}$/', 'title' => 'NIT Colombia (9-10; DV fuera del patrón)'],
            'VEN' => ['regex' => '/^[VE]\-\d{8}\-\d$/', 'title' => 'RIF PF Venezuela (V|E-XXXXXXXX-DV)'],
            'GUY' => ['regex' => '/^[A-Z0-9]{6,15}$/', 'title' => 'Guyana (6-15)'],
            'SUR' => ['regex' => '/^[A-Z0-9]{6,15}$/', 'title' => 'Surinam (6-15)'],
            'BRA' => ['regex' => '/^\d{11}$/', 'title' => 'CPF Brasil (11 dígitos)'],
            'ECU' => ['regex' => '/^\d{10}$/', 'title' => 'Cédula Ecuador (10)'],
            'PER' => ['regex' => '/^\d{8}$/',  'title' => 'DNI Perú (8)'],
            'BOL' => ['regex' => '/^\d{7,12}$/', 'title' => 'NIT/CI Bolivia (7-12)'],
            'CHL' => ['regex' => '/^\d{7,8}\-[0-9K]$/i', 'title' => 'RUT Chile (7-8 + DV)'],
            'PRY' => ['regex' => '/^\d{6,8}$/', 'title' => 'Cédula Paraguay (6-8)'],
            'URY' => ['regex' => '/^\d{7,8}$/', 'title' => 'Cédula Uruguay (7-8)'],
            'ARG' => ['regex' => '/^\d{11}$/', 'title' => 'CUIL Argentina (11)'],
        ];
    }

    /**
     * Patrón genérico por si el país no está mapeado
     */
    protected function fallback(): array
    {
        return [
            'regex' => '/^[A-Z0-9\-]{6,20}$/',
            'title' => 'Identificador fiscal (PF) - 6-20 alfanuméricos, guiones opcionales',
        ];
    }

    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        if (!is_string($value) || $value === '') {
            $fail(__('validation.required', ['attribute' => $attribute]));
            return;
        }

        $normalized = $this->normalize($value);
        $patterns = $this->patterns();
        $rule = $patterns[$this->country] ?? $this->fallback();

        if (!preg_match($rule['regex'], $normalized)) {
            $fail(__('validation.custom.tax_identifier_pf'));
            return;
        }

        // Validaciones extra por país (checksums/reglas especiales)
        switch ($this->country) {
            case 'CHL':
                if (!$this->validChileanRut($normalized)) {
                    $fail(__('validation.custom.tax_identifier_chl'));
                }
                break;

            case 'BRA':
                if (!$this->validBrazilCpf($normalized)) {
                    $fail(__('validation.custom.tax_identifier_bra_cpf'));
                }
                break;

            case 'ESP':
                if (!$this->validSpanishNifNie($normalized)) {
                    $fail(__('validation.custom.tax_identifier_esp_nif'));
                }
                break;

            case 'CAN':
                if (!$this->validLuhn($normalized)) {
                    $fail(__('validation.custom.tax_identifier_can_sin'));
                }
                break;

            case 'ECU':
                if (!$this->validEcuadorCedula($normalized)) {
                    $fail(__('validation.custom.tax_identifier_ecu_cedula'));
                }
                break;

            case 'ARG':
                if (!$this->validArgentinaCuil($normalized)) {
                    $fail(__('validation.custom.tax_identifier_arg_cuil'));
                }
                break;

            case 'USA':
                if (!$this->validUsaSsn($normalized)) {
                    $fail(__('validation.custom.tax_identifier_usa_ssn'));
                }
                break;

            // Nota: CO (NIT) DV, VEN (RIF) DV, etc. pueden añadirse aquí si quieres endurecer reglas.
        }
    }

    /**
     * Valida DV del RUT chileno.
     */
    protected function validChileanRut(string $rut): bool
    {
        // Formato: ########-X (X puede ser K)
        [$num, $dv] = explode('-', strtoupper($rut));
        $num = strrev(preg_replace('/\D/', '', $num));
        $suma = 0;
        $multiplo = 2;

        for ($i = 0; $i < strlen($num); $i++) {
            $suma += intval($num[$i]) * $multiplo;
            $multiplo = ($multiplo === 7) ? 2 : $multiplo + 1;
        }

        $resto = $suma % 11;
        $digito = 11 - $resto;
        $dvCalc = match ($digito) {
            10 => 'K',
            11 => '0',
            default => (string)$digito
        };

        return strtoupper($dv) === $dvCalc;
    }

    /**
     * Valida CPF (Brasil) con módulo 11 y descarta secuencias repetidas.
     */
    protected function validBrazilCpf(string $cpf): bool
    {
        $cpf = preg_replace('/\D/', '', $cpf);
        if (strlen($cpf) !== 11) return false;
        if (preg_match('/^(\d)\1{10}$/', $cpf)) return false; // 000... o repetidos

        // Primer dígito verificador
        $sum = 0;
        for ($i = 0, $w = 10; $i < 9; $i++, $w--) {
            $sum += intval($cpf[$i]) * $w;
        }
        $rest = $sum % 11;
        $dv1 = ($rest < 2) ? 0 : 11 - $rest;

        // Segundo dígito verificador
        $sum = 0;
        for ($i = 0, $w = 11; $i < 10; $i++, $w--) {
            $sum += intval($cpf[$i]) * $w;
        }
        $rest = $sum % 11;
        $dv2 = ($rest < 2) ? 0 : 11 - $rest;

        return intval($cpf[9]) === $dv1 && intval($cpf[10]) === $dv2;
    }

    /**
     * Valida NIF/NIE (España):
     * - NIF: 8 dígitos + letra calculada
     * - NIE: X/Y/Z + 7 dígitos + letra calculada (X=0, Y=1, Z=2)
     */
    protected function validSpanishNifNie(string $id): bool
    {
        $id = strtoupper($id);
        $letters = 'TRWAGMYFPDXBNJZSQVHLCKE';

        if (preg_match('/^\d{8}[A-Z]$/', $id)) {
            $num = intval(substr($id, 0, 8));
            $letter = $letters[$num % 23];
            return $id[8] === $letter;
        }

        if (preg_match('/^[XYZ]\d{7}[A-Z]$/', $id)) {
            $map = ['X' => '0', 'Y' => '1', 'Z' => '2'];
            $num = intval($map[$id[0]] . substr($id, 1, 7));
            $letter = $letters[$num % 23];
            return $id[8] === $letter;
        }

        return false;
    }

    /**
     * Luhn genérico (para SIN Canadá, etc.)
     */
    protected function validLuhn(string $digits): bool
    {
        $digits = preg_replace('/\D/', '', $digits);
        $sum = 0;
        $alt = false;

        for ($i = strlen($digits) - 1; $i >= 0; $i--) {
            $n = intval($digits[$i]);
            if ($alt) {
                $n *= 2;
                if ($n > 9) $n -= 9;
            }
            $sum += $n;
            $alt = !$alt;
        }
        return $sum % 10 === 0;
    }

    /**
     * Cédula Ecuador (10):
     * - Provincia 00–24 (incluye 30 exterior en algunos casos; usamos 00–24 estándar)
     * - Dígito 3 < 6 (personas naturales)
     * - Checksum módulo 10 (coeficientes 2,1 alternados)
     */
    protected function validEcuadorCedula(string $id): bool
    {
        $id = preg_replace('/\D/', '', $id);
        if (strlen($id) !== 10) return false;

        $prov = intval(substr($id, 0, 2));
        if ($prov < 0 || $prov > 24) return false;

        $tercero = intval($id[2]);
        if ($tercero >= 6) return false; // PF

        $coef = [2,1,2,1,2,1,2,1,2];
        $sum = 0;
        for ($i = 0; $i < 9; $i++) {
            $prod = intval($id[$i]) * $coef[$i];
            if ($prod >= 10) $prod -= 9;
            $sum += $prod;
        }
        $dv = ($sum % 10 === 0) ? 0 : 10 - ($sum % 10);
        return $dv === intval($id[9]);
    }

    /**
     * CUIL Argentina (11) – módulo 11.
     * Prefijos PF usuales: 20, 23, 24, 27.
     */
    protected function validArgentinaCuil(string $cuil): bool
    {
        $cuil = preg_replace('/\D/', '', $cuil);
        if (strlen($cuil) !== 11) return false;

        $weights = [5,4,3,2,7,6,5,4,3,2];
        $sum = 0;
        for ($i = 0; $i < 10; $i++) {
            $sum += intval($cuil[$i]) * $weights[$i];
        }
        $rest = $sum % 11;
        $dv = 11 - $rest;
        if ($dv === 11) $dv = 0;
        if ($dv === 10) $dv = 9; // regla habitual

        // Prefijo típico PF (opcional endurecer):
        $prefix = intval(substr($cuil, 0, 2));
        if (!in_array($prefix, [20, 23, 24, 27], true)) {
            // Si quieres rechazar estrictamente, descomenta:
            // return false;
        }

        return intval($cuil[10]) === $dv;
    }

    /**
     * SSN USA reglas básicas:
     * - No área "000", "666", ni 900–999
     * - Grupo != "00"
     * - Serie != "0000"
     */
    protected function validUsaSsn(string $ssn): bool
    {
        if (!preg_match('/^\d{3}\-\d{2}\-\d{4}$/', $ssn)) return false;
        [$area, $group, $serial] = explode('-', $ssn);

        $a = intval($area);
        if ($a === 0 || $a === 666 || $a >= 900) return false;
        if (intval($group) === 0) return false;
        if (intval($serial) === 0) return false;

        return true;
    }
}
