<?php
/**
 * vencimientos.php — Vencimientos tributarios de Bolivia, AÑO POR AÑO.
 *
 * Reproduce los Calendarios Tributarios oficiales del SIN (2013–2026):
 *   - Obligaciones MENSUALES (IVA, IT, RC-IVA agentes): vencen el mes SIGUIENTE
 *     al período fiscal, en el día base según la TERMINACIÓN del NIT
 *       dígito 0→13, 1→14, 2→15, 3→16, 4→17, 5→18, 6→19, 7→20, 8→21, 9→22
 *     corriéndose al SIGUIENTE DÍA HÁBIL si cae sábado, domingo o feriado
 *     nacional. Por eso cada año difiere (fines de semana y feriados móviles).
 *   - IUE: vence 120 días después del cierre de gestión, corrido a día hábil.
 *     Cierres: comercial 31/12, industrial 31/03, agropecuaria 30/06,
 *     minera 30/09. Se contemplan PRÓRROGAS puntuales (ej. gestión 2019).
 *
 * Los feriados nacionales se calculan por año (fijos + móviles derivados de la
 * Pascua), de modo que el dataset es exacto para cualquier año. Las EXCEPCIONES
 * (prórrogas por RND) se guardan en las tablas $OVERRIDE_*.
 */

class Vencimientos {

    /** Feriados nacionales con suspensión de plazos. mm-dd y año de inicio. */
    private static $FIJOS = [
        ['01-01', 0],     // Año Nuevo
        ['01-22', 2010],  // Día del Estado Plurinacional
        ['05-01', 0],     // Día del Trabajo
        ['06-21', 2010],  // Año Nuevo Andino Amazónico
        ['08-06', 0],     // Día de la Patria
        ['11-02', 0],     // Día de los Difuntos
        ['12-25', 0],     // Navidad
    ];

    /** Prórrogas/excepciones de vencimiento MENSUAL: 'IMP|YYYY-MM|nit' => 'Y-m-d' */
    public static $OVERRIDE_MENSUAL = [
        // (se completan si se detectan prórrogas en los calendarios)
    ];

    /** Vencimiento IUE por gestión y tipo de cierre: 'tipo|YYYY' => 'Y-m-d'
     *  tipo: comercial|industrial|agropecuaria|minera. Sobrescribe la regla +120. */
    public static $OVERRIDE_IUE = [
        'comercial|2019' => '2020-07-31', // prórroga (ratificada por Vista de Cargo)
    ];

    /** Domingo de Pascua (algoritmo de Gauss/Anonymous Gregorian). */
    static function pascua($y) {
        $a=$y%19; $b=intdiv($y,100); $c=$y%100; $d=intdiv($b,4); $e=$b%4;
        $f=intdiv($b+8,25); $g=intdiv($b-$f+1,3); $h=(19*$a+$b-$d-$g+15)%30;
        $i=intdiv($c,4); $k=$c%4; $l=(32+2*$e+2*$i-$h-$k)%7;
        $m=intdiv($a+11*$h+22*$l,451); $mes=intdiv($h+$l-7*$m+114,31);
        $dia=(($h+$l-7*$m+114)%31)+1;
        return mktime(0,0,0,$mes,$dia,$y);
    }

    /** Set de feriados nacionales del año como 'Y-m-d' => true. */
    static function feriados($y) {
        $set = [];
        foreach (self::$FIJOS as $f) {
            if ($y >= $f[1]) $set[$y.'-'.$f[0]] = true;
        }
        $p = self::pascua($y);
        $set[date('Y-m-d', strtotime('-48 days', $p))] = true; // Lunes Carnaval
        $set[date('Y-m-d', strtotime('-47 days', $p))] = true; // Martes Carnaval
        $set[date('Y-m-d', strtotime('-2 days',  $p))] = true; // Viernes Santo
        $set[date('Y-m-d', strtotime('+60 days', $p))] = true; // Corpus Christi
        return $set;
    }

    static function esHabil($ts, $fer) {
        $dow = (int)date('N', $ts);            // 6=sáb, 7=dom
        if ($dow >= 6) return false;
        return !isset($fer[date('Y-m-d', $ts)]);
    }

    /** Avanza al siguiente día hábil (incluye el mismo si ya es hábil). */
    static function siguienteHabil($ts) {
        $fer = self::feriados((int)date('Y', $ts));
        $guard = 0;
        while (!self::esHabil($ts, $fer)) {
            $ts = strtotime('+1 day', $ts);
            // si cruzamos de año, recargar feriados
            $fer = self::feriados((int)date('Y', $ts));
            if (++$guard > 30) break;
        }
        return $ts;
    }

    /** Vencimiento de obligación mensual (IVA/IT/RC-IVA) por período y NIT. */
    static function mensual($periodo, $nit) {
        if (!preg_match('/^(\d{4})-(\d{2})$/', $periodo, $mm)) return null;
        if ($nit === null || $nit === '' || $nit < 0 || $nit > 9) return null;
        $nit = (int)$nit;
        // mes siguiente al período
        $y = (int)$mm[1]; $m = (int)$mm[2];
        $m++; if ($m > 12) { $m = 1; $y++; }
        $diaBase = 13 + $nit;
        $ts = self::siguienteHabil(mktime(0,0,0,$m,$diaBase,$y));
        return date('Y-m-d', $ts);
    }

    /** Tipo de cierre IUE según mes del período. */
    static function tipoIUE($mes) {
        return ['03'=>'industrial','06'=>'agropecuaria','09'=>'minera','12'=>'comercial'][$mes] ?? 'comercial';
    }

    /** Vencimiento IUE por período (el período indica el mes/año de cierre). */
    static function iue($periodo) {
        if (!preg_match('/^(\d{4})-(\d{2})$/', $periodo, $mm)) return null;
        $y=(int)$mm[1]; $mesStr=$mm[2];
        $tipo = self::tipoIUE($mesStr);
        $ov = self::$OVERRIDE_IUE["$tipo|$y"] ?? null;
        if ($ov) return $ov;
        // último día del mes de cierre
        $cierre = strtotime(date('Y-m-t', mktime(0,0,0,(int)$mesStr,1,$y)));
        $venc = strtotime('+120 days', $cierre);
        return date('Y-m-d', self::siguienteHabil($venc));
    }

    /** API principal. */
    static function sugerir($periodo, $impuesto, $nit) {
        $imp = strtoupper(trim($impuesto));
        if ($imp === 'IUE') return self::iue($periodo);
        if (in_array($imp, ['IVA','IT','RC-IVA','RC IVA','RCIVA'])) {
            $ov = self::$OVERRIDE_MENSUAL["$imp|$periodo|$nit"] ?? null;
            return $ov ?: self::mensual($periodo, $nit);
        }
        return null; // 'Otro' u otros: sin sugerencia
    }
}
