<?php

namespace App\Services\Sri;

use Carbon\Carbon;

/**
 * Construye la clave de acceso de 49 dígitos que exige el SRI para cada
 * comprobante electrónico, según la ficha técnica vigente:
 *
 *  8   fecha de emisión (ddmmyyyy)
 *  2   código del tipo de documento (01 = factura)
 *  13  RUC del emisor
 *  1   ambiente (1 pruebas, 2 producción)
 *  3   establecimiento
 *  3   punto de emisión
 *  9   secuencial
 *  8   código numérico (aleatorio, distingue documentos idénticos)
 *  1   tipo de emisión (1 = normal)
 *  1   dígito verificador (módulo 11)
 *  --
 *  49
 */
class ClaveAccesoBuilder
{
    public const COD_DOC_FACTURA = '01';

    public static function generar(
        Carbon $fechaEmision,
        string $ruc,
        int $ambiente,
        string $estab,
        string $ptoEmi,
        int $secuencial,
        string $codDoc = self::COD_DOC_FACTURA,
        int $tipoEmision = 1,
        ?string $codigoNumerico = null,
    ): string {
        $fecha = $fechaEmision->format('dmY');
        $ruc = str_pad(preg_replace('/\D/', '', $ruc), 13, '0', STR_PAD_LEFT);
        $serie = str_pad($estab, 3, '0', STR_PAD_LEFT).str_pad($ptoEmi, 3, '0', STR_PAD_LEFT);
        $secuencial = str_pad((string) $secuencial, 9, '0', STR_PAD_LEFT);
        $codigoNumerico = $codigoNumerico ?? str_pad((string) random_int(0, 99999999), 8, '0', STR_PAD_LEFT);

        $clave48 = $fecha.$codDoc.$ruc.$ambiente.$serie.$secuencial.$codigoNumerico.$tipoEmision;

        if (strlen($clave48) !== 48) {
            throw new SriException("La clave de acceso debe tener 48 dígitos antes del verificador, tiene {$clave48} = ".strlen($clave48));
        }

        return $clave48.self::digitoVerificadorModulo11($clave48);
    }

    /**
     * Algoritmo módulo 11 exigido por el SRI: se pondera cada dígito (de derecha
     * a izquierda) por una secuencia cíclica 2,3,4,5,6,7; se suma todo; el
     * dígito verificador es 11 - (suma % 11), con los casos especiales 11→0 y 10→1.
     */
    public static function digitoVerificadorModulo11(string $secuencia): int
    {
        $digitos = array_reverse(str_split($secuencia));
        $pesos = [2, 3, 4, 5, 6, 7];
        $suma = 0;

        foreach ($digitos as $i => $d) {
            $suma += ((int) $d) * $pesos[$i % 6];
        }

        $residuo = 11 - ($suma % 11);

        return match (true) {
            $residuo === 11 => 0,
            $residuo === 10 => 1,
            default => $residuo,
        };
    }
}
