<?php
// actualizado al 28-10-21 
set_time_limit (1800); 

require 'config.php';
require 'libs/Session.php';
require 'libs/Database.php';
require_once 'controllers/PseController.php';

Session::set('rol', '2');

$db = new Database(DB_TYPE, DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_CHARSET);
$dsPendientesEnvio = $db->prepare("SELECT id_venta, id_tipo_doc, serie_doc, nro_doc, fecha_venta, total, comision_delivery, descuento_monto FROM tm_venta WHERE estado <> 'i' AND id_tipo_doc IN ('1','2') AND (enviado_sunat IS NULL OR enviado_sunat = '' OR enviado_sunat = '0') ORDER BY id_venta DESC");
$dsPendientesEnvio->execute();
$dataPendientesEnvio = $dsPendientesEnvio->fetchAll(PDO::FETCH_ASSOC);

$dsPendientesBaja = $db->prepare("SELECT id_venta, id_tipo_doc, serie_doc, nro_doc, fecha_venta, total, comision_delivery, descuento_monto, enviado_sunat FROM tm_venta WHERE estado = 'i' AND id_tipo_doc IN ('1','2') AND enviado_sunat IN ('1','2') AND DATE(fecha_venta) >= DATE_SUB(CURDATE(), INTERVAL 7 DAY) AND (descripcion_sunat_cdr IS NULL OR descripcion_sunat_cdr = '' OR descripcion_sunat_cdr NOT LIKE '%ANULAD%') ORDER BY id_venta DESC");
$dsPendientesBaja->execute();
$dataPendientesBaja = $dsPendientesBaja->fetchAll(PDO::FETCH_ASSOC);

$empresa = $db->query("SELECT ruc, client_id, client_secret FROM tm_empresa LIMIT 1")->fetch(PDO::FETCH_ASSOC);
$stmMarcarAceptado = $db->prepare("UPDATE tm_venta SET enviado_sunat = 1, code_respuesta_sunat = '0', descripcion_sunat_cdr = 'ACEPTADO EN SUNAT (cron validador)', estado = 'a' WHERE id_venta = ?");
$stmMarcarAnulado = $db->prepare("UPDATE tm_venta SET enviado_sunat = 1, code_respuesta_sunat = '0', descripcion_sunat_cdr = 'ANULADO EN SUNAT (cron validador)', estado = 'i' WHERE id_venta = ?");

$obtenerTokenValidador = function ($empresaData) {
    if (!function_exists('curl_init')) {
        return ['ok' => false, 'message' => 'cURL no disponible'];
    }
    if (empty($empresaData['client_id']) || empty($empresaData['client_secret'])) {
        return ['ok' => false, 'message' => 'client_id/client_secret no configurados'];
    }

    $form_params = [
        'grant_type' => 'client_credentials',
        'scope' => 'https://api.sunat.gob.pe/v1/contribuyente/contribuyentes',
        'client_id' => $empresaData['client_id'],
        'client_secret' => $empresaData['client_secret'],
    ];

    $curl = curl_init();
    curl_setopt_array($curl, [
        CURLOPT_URL => "https://api-seguridad.sunat.gob.pe/v1/clientesextranet/" . $empresaData['client_id'] . "/oauth2/token",
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => http_build_query($form_params),
        CURLOPT_HTTPHEADER => ['Content-Type: application/x-www-form-urlencoded'],
        CURLOPT_TIMEOUT => 20,
    ]);
    $response = curl_exec($curl);
    $httpCode = (int) curl_getinfo($curl, CURLINFO_HTTP_CODE);
    $error = curl_error($curl);
    curl_close($curl);

    if ($response === false) {
        return ['ok' => false, 'message' => 'Error token SUNAT: ' . $error];
    }
    $data = json_decode($response, true);
    if ($httpCode >= 400 || !is_array($data) || empty($data['access_token'])) {
        return ['ok' => false, 'message' => 'No se pudo obtener token SUNAT'];
    }

    return ['ok' => true, 'token' => $data['access_token']];
};

$consultarEstadoSunat = function ($empresaData, $token, $venta) {
    if (!function_exists('curl_init')) {
        return ['ok' => false, 'message' => 'cURL no disponible'];
    }
    $codComp = ((int) $venta['id_tipo_doc'] === 1) ? '03' : '01';
    $monto = (float) $venta['total'] + (float) $venta['comision_delivery'] - (float) $venta['descuento_monto'];
    $form_params = [
        'numRuc' => $empresaData['ruc'],
        'codComp' => $codComp,
        'numeroSerie' => $venta['serie_doc'],
        'numero' => $venta['nro_doc'],
        'fechaEmision' => date('d/m/Y', strtotime($venta['fecha_venta'])),
        'monto' => number_format($monto, 2, '.', '')
    ];

    $curl = curl_init();
    curl_setopt_array($curl, [
        CURLOPT_URL => "https://api.sunat.gob.pe/v1/contribuyente/contribuyentes/" . $empresaData['ruc'] . "/validarcomprobante",
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => json_encode($form_params),
        CURLOPT_HTTPHEADER => [
            "Authorization: Bearer " . $token,
            'Content-Type: application/json'
        ],
        CURLOPT_TIMEOUT => 20,
    ]);
    $response = curl_exec($curl);
    $httpCode = (int) curl_getinfo($curl, CURLINFO_HTTP_CODE);
    $error = curl_error($curl);
    curl_close($curl);

    if ($response === false) {
        return ['ok' => false, 'message' => 'Error consulta SUNAT: ' . $error];
    }

    $res = json_decode($response, true);
    if ($httpCode >= 400 || !is_array($res)) {
        return ['ok' => false, 'message' => 'Respuesta no valida del validador SUNAT'];
    }

    if (!empty($res['success'])) {
        return ['ok' => true, 'estadoCp' => (string) ($res['data']['estadoCp'] ?? '')];
    }

    return ['ok' => false, 'message' => $res['message'] ?? 'SUNAT no pudo validar el comprobante'];
};

$resultado = array(
    'ok' => true,
    'total_pendientes' => count($dataPendientesEnvio) + count($dataPendientesBaja),
    'total_pendientes_envio' => count($dataPendientesEnvio),
    'total_pendientes_baja' => count($dataPendientesBaja),
    'procesados' => 0,
    'aceptados_validador' => 0,
    'anulados_validador' => 0,
    'reintentos_envio' => 0,
    'reintentos_baja' => 0,
    'enviados_ok' => 0,
    'bajas_ok' => 0,
    'bajas_ticket' => 0,
    'errores' => 0,
    'detalle' => array()
);

if ((count($dataPendientesEnvio) + count($dataPendientesBaja)) > 0) {
    $pse = new PseController();
    $tokenValidador = null;
    if (!empty($empresa['ruc'])) {
        $tokenResp = $obtenerTokenValidador($empresa);
        if (!empty($tokenResp['ok'])) {
            $tokenValidador = $tokenResp['token'];
        } else {
            $resultado['detalle'][] = array('mensaje' => 'Validador SUNAT no disponible: ' . $tokenResp['message']);
        }
    }

    foreach ($dataPendientesEnvio as $row) {
        $id_venta = (int) $row['id_venta'];
        $id_tipo_doc = (int) $row['id_tipo_doc'];
        $resultado['procesados']++;

        try {
            // 1) Validar en SUNAT antes de reenviar.
            if (!empty($tokenValidador)) {
                $validacion = $consultarEstadoSunat($empresa, $tokenValidador, $row);
                if (!empty($validacion['ok']) && (string) $validacion['estadoCp'] === '1') {
                    $stmMarcarAceptado->execute([$id_venta]);
                    $resultado['aceptados_validador']++;
                    $resultado['detalle'][] = array(
                        'id_venta' => $id_venta,
                        'id_tipo_doc' => $id_tipo_doc,
                        'ok' => 1,
                        'accion' => 'marcado_aceptado',
                        'mensaje' => 'SUNAT reporta ACEPTADO'
                    );
                    continue;
                }
            }

            // 2) Si no esta aceptado en SUNAT, reintentar envio.
            $resultado['reintentos_envio']++;
            if ($id_tipo_doc === 1) {
                // Boleta -> flujo PSE de resumen/boleta.
                $resp = $pse->resumenBoleta($id_venta, 1);
            } else {
                // Factura -> flujo PSE factura.
                $resp = $pse->enviarFactura($id_venta);
            }

            if (is_array($resp) && !empty($resp['ok'])) {
                $resultado['enviados_ok']++;
            } else {
                $resultado['errores']++;
            }

            $resultado['detalle'][] = array(
                'id_venta' => $id_venta,
                'id_tipo_doc' => $id_tipo_doc,
                'ok' => (is_array($resp) && !empty($resp['ok'])) ? 1 : 0,
                'accion' => 'reintento_envio',
                'mensaje' => is_array($resp) ? ($resp['mensaje'] ?? $resp['message'] ?? $resp['error'] ?? null) : 'Respuesta invalida'
            );
        } catch (Throwable $e) {
            $resultado['errores']++;
            $resultado['detalle'][] = array(
                'id_venta' => $id_venta,
                'id_tipo_doc' => $id_tipo_doc,
                'ok' => 0,
                'mensaje' => $e->getMessage()
            );
        }
    }

    foreach ($dataPendientesBaja as $row) {
        $id_venta = (int) $row['id_venta'];
        $id_tipo_doc = (int) $row['id_tipo_doc'];
        $resultado['procesados']++;

        try {
            // 1) Validar si en SUNAT ya figura ANULADO.
            if (!empty($tokenValidador)) {
                $validacion = $consultarEstadoSunat($empresa, $tokenValidador, $row);
                if (!empty($validacion['ok']) && (string) $validacion['estadoCp'] === '2') {
                    $stmMarcarAnulado->execute([$id_venta]);
                    $resultado['anulados_validador']++;
                    $resultado['detalle'][] = array(
                        'id_venta' => $id_venta,
                        'id_tipo_doc' => $id_tipo_doc,
                        'ok' => 1,
                        'accion' => 'marcado_anulado',
                        'mensaje' => 'SUNAT reporta ANULADO'
                    );
                    continue;
                }
            }

            // 2) Si no figura anulado en SUNAT, reintentar baja.
            $resultado['reintentos_baja']++;
            if ($id_tipo_doc === 2) {
                // Factura -> comunicación de baja.
                $resp = $pse->comunicacionBaja($id_venta);
            } else {
                // Boleta -> resumen/comunicación de baja (acción 3).
                $resp = $pse->resumenBoleta($id_venta, 3);
            }

            if (is_array($resp) && !empty($resp['ok'])) {
                $esFinal = array_key_exists('final', $resp) ? ((bool) $resp['final']) : true;
                if ($esFinal) {
                    // Estandariza el estado final para no reprocesar el mismo anulado.
                    $stmMarcarAnulado->execute([$id_venta]);
                    $resultado['bajas_ok']++;
                } else {
                    // Quedó ticket pendiente; en siguiente corrida se validará estado en SUNAT.
                    $resultado['bajas_ticket']++;
                }
            } else {
                $resultado['errores']++;
            }

            $resultado['detalle'][] = array(
                'id_venta' => $id_venta,
                'id_tipo_doc' => $id_tipo_doc,
                'ok' => (is_array($resp) && !empty($resp['ok'])) ? 1 : 0,
                'accion' => 'reintento_baja',
                'final' => is_array($resp) ? ($resp['final'] ?? null) : null,
                'mensaje' => is_array($resp) ? ($resp['mensaje'] ?? $resp['message'] ?? $resp['error'] ?? null) : 'Respuesta invalida'
            );
        } catch (Throwable $e) {
            $resultado['errores']++;
            $resultado['detalle'][] = array(
                'id_venta' => $id_venta,
                'id_tipo_doc' => $id_tipo_doc,
                'ok' => 0,
                'accion' => 'reintento_baja',
                'mensaje' => $e->getMessage()
            );
        }
    }
} else {
    $resultado['detalle'][] = array(
        'mensaje' => 'No hay comprobantes pendientes por enviar/anular'
    );
}

header('Content-Type: application/json; charset=utf-8');
echo json_encode($resultado, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
