<?php
defined('BASEPATH') OR exit('No direct script access allowed');

// Greenter v5 — namespaces correctos
use Greenter\Ws\Services\SunatEndpoints;
use Greenter\See;
use Greenter\Model\Company\Company;
use Greenter\Model\Company\Address;
use Greenter\Model\Sale\Invoice;
use Greenter\Model\Sale\Note;
use Greenter\Model\Sale\SaleDetail;
use Greenter\Model\Sale\Legend;
use Greenter\Model\Client\Client;
use Greenter\XMLSecLibs\Certificate\X509Certificate;
use Greenter\XMLSecLibs\Certificate\X509ContentType;
use Greenter\Model\Sale\FormaPagos\FormaPagoContado;
use Greenter\Model\Sale\FormaPagos\FormaPagoCredito;
use Greenter\Model\Sale\Cuota;
// Guía de Remisión — namespace correcto
use Greenter\Model\Despatch\Despatch;
use Greenter\Model\Despatch\DespatchDetail;
use Greenter\Model\Despatch\Direction;
use Greenter\Model\Despatch\Shipment;
use Greenter\Model\Despatch\Vehicle;
use Greenter\Model\Despatch\Driver;
use Greenter\Model\Despatch\Transportist;

class SunatService {

    private $CI;
    private $empresa;
    private $see;

    public function __construct() {
        $this->CI      =& get_instance();
        $this->empresa = $this->CI->db->get('empresa')->row();
        $this->inicializar();
    }

    // ---------------------------------------------------------------
    // INICIALIZAR SEE
    // ---------------------------------------------------------------
    private function inicializar() {
        $certPath = FCPATH . $this->empresa->cert_path;

        if (!file_exists($certPath)) {
            throw new Exception('Certificado no encontrado: ' . $certPath);
        }

        $password    = $this->empresa->cert_password ?? '';
        $pfx         = file_get_contents($certPath);
        $certificate = new X509Certificate($pfx, $password);
        $certPem     = $certificate->export(X509ContentType::PEM);

        $this->see = new See();
        $this->see->setCertificate($certPem);

        $modo = $this->empresa->sunat_modo ?? 'beta';
        if ($modo === 'produccion') {
            $this->see->setService(SunatEndpoints::FE_PRODUCCION);
        } else {
            $this->see->setService(SunatEndpoints::FE_BETA);
        }

        // v5: setCredentials (no setService con usuario/clave)
        $this->see->setCredentials(
            $this->empresa->ruc . $this->empresa->sol_usuario,
            $this->empresa->sol_clave
        );
    }

    // ---------------------------------------------------------------
    // EMISOR (COMPANY)
    // ---------------------------------------------------------------
    private function getCompany() {
        return (new Company())
            ->setRuc($this->empresa->ruc)
            ->setRazonSocial($this->empresa->nombre)
            ->setNombreComercial($this->empresa->abreviado ?? $this->empresa->nombre)
            ->setAddress((new Address())
                ->setUbigueo('150101')
                ->setDepartamento('LIMA')
                ->setProvincia('LIMA')
                ->setDistrito('LIMA')
                ->setUrbanizacion('-')
                ->setDireccion($this->empresa->direccion_fiscal ?? '')
                ->setCodLocal('0000'));
    }

    // ---------------------------------------------------------------
    // ENVIAR FACTURA / BOLETA
    // ---------------------------------------------------------------
    public function enviarComprobante($comprob_id) {
        // IGV dinámico desde tabla mt_tributo
        $tributo_igv    = $this->CI->db->where('id', 2)->get('mt_tributo')->row();
        $igv_porcentaje = $tributo_igv ? (float)$tributo_igv->valor_porcentual / 100 : 0.18;
        $igv_modo       = $tributo_igv ? (int)$tributo_igv->modo : 1;
        $igv_factor     = 1 + $igv_porcentaje;

        $comprob = $this->CI->db
            ->select('c.*, cl.razon_social as cliente_nombre, cl.nro_documento as cliente_ruc,
                      di.codigo_sunat as tipo_doc_cliente,
                      tc.codigo as tipo_comprobante_codigo,
                      m.codigo as moneda_codigo,
                      fp.codigo as forma_pago_codigo')
            ->from('vt_comprobante c')
            ->join('vt_cliente cl',              'cl.id = c.cliente_id')
            ->join('mt_documento_identidad di',  'di.id = cl.documento_identidad_id', 'left')
            ->join('mt_tipo_comprobante tc',     'tc.id = c.comprobante_id')
            ->join('mt_moneda m',                'm.id  = c.moneda_id', 'left')
            ->join('mt_forma_pago fp',           'fp.id = c.fp_id',     'left')
            ->where('c.id', $comprob_id)
            ->get()->row();

        if (!$comprob) {
            return array('status' => false, 'message' => 'Comprobante no encontrado');
        }

        $detalle = $this->CI->db
            ->select('d.*, p.codigo as producto_codigo, p.nombre as producto_nombre,
                      um.codigo_sunat as um_codigo')
            ->from('vt_comprobante_detalle d')
            ->join('vt_producto p',       'p.id  = d.producto_id')
            ->join('mt_unidad_medida um', 'um.id = p.um_id_venta', 'left')
            ->where('d.comprob_id', $comprob_id)
            ->get()->result();

        // Forma de pago — clases específicas de Greenter v5
        $forma_pago_codigo = strtoupper($comprob->forma_pago_codigo ?? 'CONTADO');
        if ($forma_pago_codigo === 'CONTADO') {
            $formaPago = (new FormaPagoContado())
                ->setMoneda($comprob->moneda_codigo ?? 'PEN');
            $cuotas = array();
        } else {
            $formaPago = (new FormaPagoCredito())
                ->setMoneda($comprob->moneda_codigo ?? 'PEN')
                ->setMonto((float)$comprob->total);

            // Cuotas — van directo en el Invoice con setCuotas()
            $cuotas_bd = $this->CI->db
                ->where('comprob_id', $comprob_id)
                ->order_by('nro_cuota', 'ASC')
                ->get('vt_comprobante_cuota')
                ->result();

            $cuotas = array();
            foreach ($cuotas_bd as $cq) {
                $cuotas[] = (new Cuota())
                    ->setMoneda($comprob->moneda_codigo ?? 'PEN')
                    ->setMonto((float)$cq->importe_cuota)
                    ->setFechaPago(new DateTime($cq->vencto_cuota));
            }
        }

        $invoice = new Invoice();
        $invoice->setUblVersion('2.1')
                ->setTipoOperacion('0101')
                ->setTipoDoc($comprob->tipo_comprobante_codigo)
                ->setSerie($comprob->serie)
                ->setCorrelativo(ltrim($comprob->numero, '0') ?: '1')
                ->setFechaEmision(new DateTime($comprob->fecha))
                ->setFecVencimiento(!empty($comprob->fecvencimiento)
                    ? new DateTime($comprob->fecvencimiento)
                    : new DateTime($comprob->fecha))
                ->setTipoMoneda($comprob->moneda_codigo ?? 'PEN')
                ->setFormaPago($formaPago)
                ->setCompany($this->getCompany())
                ->setClient((new Client())
                    ->setTipoDoc($comprob->tipo_doc_cliente ?? '6')
                    ->setNumDoc($comprob->cliente_ruc)
                    ->setRznSocial($comprob->cliente_nombre))
                ->setMtoOperGravadas((float)$comprob->subtotal)
                ->setMtoIGV((float)$comprob->igv)
                ->setTotalImpuestos((float)$comprob->igv)
                ->setValorVenta((float)$comprob->subtotal)
                ->setSubTotal((float)$comprob->total)
                ->setMtoImpVenta((float)$comprob->total);

        // Agregar cuotas si es crédito
        if (!empty($cuotas)) {
            $invoice->setCuotas($cuotas);
        }

        // Armar ítems con ajuste en el último para cuadrar totales
        $items     = array();
        $suma_base = 0;
        $detalle   = array_values($detalle);

        foreach ($detalle as $key => $d) {
            $cantidad = (float)$d->cantidad;
            $precio   = (float)$d->precio_unitario;
            $es_ultimo = ($key === array_key_last($detalle));

            if ($igv_modo == 1) { // precio con IGV incluido
                $total_item     = round($cantidad * $precio, 2);
                $base_item      = round($total_item / $igv_factor, 2);
                $igv_item       = round($total_item - $base_item, 2);
                $precio_sin_igv = round($precio / $igv_factor, 4);
            } else {
                $base_item      = round($cantidad * $precio, 2);
                $igv_item       = round($base_item * $igv_porcentaje, 2);
                $precio_sin_igv = $precio;
            }

            if ($es_ultimo) {
                $base_item = round((float)$comprob->subtotal - $suma_base, 2);
                $igv_item  = round($base_item * $igv_porcentaje, 2);
            }
            $suma_base += $base_item;

            $item = new SaleDetail();
            $item->setCodProducto($d->producto_codigo)
                 ->setUnidad($d->um_codigo ?? 'NIU')
                 ->setDescripcion($d->descripcion ?? $d->producto_nombre)
                 ->setCantidad($cantidad)
                 ->setMtoValorUnitario($precio_sin_igv)
                 ->setMtoBaseIgv($base_item)
                 ->setPorcentajeIgv($igv_porcentaje * 100)
                 ->setIgv($igv_item)
                 ->setTipAfeIgv('10')
                 ->setTotalImpuestos($igv_item)
                 ->setMtoValorVenta($base_item)
                 ->setMtoPrecioUnitario($precio);
            $items[] = $item;
        }
        $invoice->setDetails($items);

        // Leyenda
        $this->CI->load->helper('numero');
        $legend = new Legend();
        $legend->setCode('1000')
               ->setValue('SON: ' . strtoupper(numero_a_letras((float)$comprob->total, 'SOLES')));
        $invoice->setLegends([$legend]);

        try {
            $result = $this->see->send($invoice);
            return $this->procesarRespuesta($result, $comprob_id, 'comprobante', $comprob->serie . '-' . ltrim($comprob->numero, '0'));
        } catch (Exception $e) {
            return array('status' => false, 'message' => $e->getMessage());
        }
    }

    // ---------------------------------------------------------------
    // ENVIAR NOTA DE CRÉDITO
    // ---------------------------------------------------------------
    public function enviarNotaCredito($nc_id) {
        $nc = $this->CI->db
            ->select('nc.*, cl.razon_social as cliente_nombre, cl.nro_documento as cliente_ruc,
                      di.codigo_sunat as tipo_doc_cliente, tnc.codigo as tipo_nc_codigo,
                      m.codigo as moneda_codigo,
                      c.serie as comprob_serie, c.numero as comprob_numero,
                      tc.codigo as comprob_tipo_codigo')
            ->from('vt_nota_credito nc')
            ->join('vt_cliente cl',             'cl.id  = nc.cliente_id')
            ->join('mt_documento_identidad di', 'di.id  = cl.documento_identidad_id', 'left')
            ->join('mt_tipo_nota_credito tnc',  'tnc.id = nc.tipo_nc_id')
            ->join('mt_moneda m',               'm.id   = nc.moneda_id', 'left')
            ->join('vt_comprobante c',          'c.id   = nc.comprob_id')
            ->join('mt_tipo_comprobante tc',    'tc.id  = c.comprobante_id')
            ->where('nc.id', $nc_id)
            ->get()->row();

        if (!$nc) return array('status' => false, 'message' => 'Nota de crédito no encontrada');

        $detalle = $this->CI->db
            ->select('d.*, p.codigo as producto_codigo, p.nombre as producto_nombre,
                      um.codigo_sunat as um_codigo')
            ->from('vt_nota_credito_det d')
            ->join('vt_producto p',       'p.id  = d.producto_id')
            ->join('mt_unidad_medida um', 'um.id = p.um_id_venta', 'left')
            ->where('d.nc_id', $nc_id)
            ->get()->result();

        $note = (new Note())
            ->setUblVersion('2.1')
            ->setTipoDoc('07')
            ->setSerie($nc->serie)
            ->setCorrelativo(ltrim($nc->numero, '0') ?: '1')
            ->setFechaEmision(new DateTime($nc->fecha))
            ->setTipoMoneda($nc->moneda_codigo ?? 'PEN')
            ->setCompany($this->getCompany())
            ->setClient((new Client())
                ->setTipoDoc($nc->tipo_doc_cliente ?? '6')
                ->setNumDoc($nc->cliente_ruc)
                ->setRznSocial($nc->cliente_nombre))
            ->setCodMotivo($nc->tipo_nc_codigo)
            ->setDesMotivo($nc->sustento ?? '')
            ->setTipDocAfectado($nc->comprob_tipo_codigo)
            ->setNumDocfectado($nc->comprob_serie . '-' . $nc->comprob_numero)
            ->setMtoOperGravadas((float)$nc->subtotal)
            ->setMtoIGV((float)$nc->igv)
            ->setTotalImpuestos((float)$nc->igv)
            ->setValorVenta((float)$nc->subtotal)
            ->setSubTotal((float)$nc->total)
            ->setMtoImpVenta((float)$nc->total);

        $items = array();
        foreach ($detalle as $d) {
            $item = new SaleDetail();
            $item->setCodProducto($d->producto_codigo)
                 ->setUnidad($d->um_codigo ?? 'NIU')
                 ->setDescripcion($d->descripcion ?? $d->producto_nombre)
                 ->setCantidad((float)$d->cantidad)
                 ->setMtoValorUnitario((float)$d->precio_unitario)
                 ->setMtoBaseIgv((float)$d->subtotal)
                 ->setPorcentajeIgv(18.00)
                 ->setIgv(round((float)$d->subtotal * 0.18, 2))
                 ->setTipAfeIgv('10')
                 ->setTotalImpuestos(round((float)$d->subtotal * 0.18, 2))
                 ->setMtoValorVenta((float)$d->subtotal)
                 ->setMtoPrecioUnitario(round((float)$d->precio_unitario * 1.18, 4));
            $items[] = $item;
        }
        $note->setDetails($items);

        try {
            $result = $this->see->send($note);
            return $this->procesarRespuesta($result, $nc_id, 'nota_credito', $nc->serie . '-' . ltrim($nc->numero, '0'));
        } catch (Exception $e) {
            return array('status' => false, 'message' => $e->getMessage());
        }
    }

    // ---------------------------------------------------------------
    // ENVIAR NOTA DE DÉBITO
    // ---------------------------------------------------------------
    public function enviarNotaDebito($nd_id) {
        $nd = $this->CI->db
            ->select('nd.*, cl.razon_social as cliente_nombre, cl.nro_documento as cliente_ruc,
                      di.codigo_sunat as tipo_doc_cliente, tnd.codigo as tipo_nd_codigo,
                      m.codigo as moneda_codigo,
                      c.serie as comprob_serie, c.numero as comprob_numero,
                      tc.codigo as comprob_tipo_codigo')
            ->from('vt_nota_debito nd')
            ->join('vt_cliente cl',             'cl.id  = nd.cliente_id')
            ->join('mt_documento_identidad di', 'di.id  = cl.documento_identidad_id', 'left')
            ->join('mt_tipo_nota_debito tnd',   'tnd.id = nd.tipo_nd_id')
            ->join('mt_moneda m',               'm.id   = nd.moneda_id', 'left')
            ->join('vt_comprobante c',          'c.id   = nd.comprob_id')
            ->join('mt_tipo_comprobante tc',    'tc.id  = c.comprobante_id')
            ->where('nd.id', $nd_id)
            ->get()->row();

        if (!$nd) return array('status' => false, 'message' => 'Nota de débito no encontrada');

        $detalle = $this->CI->db->where('nd_id', $nd_id)->get('vt_nota_debito_det')->result();

        $note = (new Note())
            ->setUblVersion('2.1')
            ->setTipoDoc('08')
            ->setSerie($nd->serie)
            ->setCorrelativo(ltrim($nd->numero, '0') ?: '1')
            ->setFechaEmision(new DateTime($nd->fecha))
            ->setTipoMoneda($nd->moneda_codigo ?? 'PEN')
            ->setCompany($this->getCompany())
            ->setClient((new Client())
                ->setTipoDoc($nd->tipo_doc_cliente ?? '6')
                ->setNumDoc($nd->cliente_ruc)
                ->setRznSocial($nd->cliente_nombre))
            ->setCodMotivo($nd->tipo_nd_codigo)
            ->setDesMotivo($nd->sustento ?? '')
            ->setTipDocAfectado($nd->comprob_tipo_codigo)
            ->setNumDocfectado($nd->comprob_serie . '-' . $nd->comprob_numero)
            ->setMtoOperGravadas((float)$nd->subtotal)
            ->setMtoIGV((float)$nd->igv)
            ->setTotalImpuestos((float)$nd->igv)
            ->setValorVenta((float)$nd->subtotal)
            ->setSubTotal((float)$nd->total)
            ->setMtoImpVenta((float)$nd->total);

        $items = array();
        foreach ($detalle as $d) {
            $item = new SaleDetail();
            $item->setUnidad('NIU')
                 ->setDescripcion($d->descripcion)
                 ->setCantidad(1)
                 ->setMtoValorUnitario((float)$d->subtotal)
                 ->setMtoBaseIgv((float)$d->subtotal)
                 ->setPorcentajeIgv(18.00)
                 ->setIgv((float)$d->igv)
                 ->setTipAfeIgv('10')
                 ->setTotalImpuestos((float)$d->igv)
                 ->setMtoValorVenta((float)$d->subtotal)
                 ->setMtoPrecioUnitario((float)$d->total);
            $items[] = $item;
        }
        $note->setDetails($items);

        try {
            $result = $this->see->send($note);
            return $this->procesarRespuesta($result, $nd_id, 'nota_debito', $nd->serie . '-' . ltrim($nd->numero, '0'));
        } catch (Exception $e) {
            return array('status' => false, 'message' => $e->getMessage());
        }
    }

    // ---------------------------------------------------------------
    // ENVIAR GUÍA DE REMISIÓN
    // ---------------------------------------------------------------
    public function enviarGuiaRemision($gr_id) {

        // Datos completos de la guía
        $guia = $this->CI->db
            ->select('g.*,
                      cl.razon_social as cliente_nombre, cl.nro_documento as cliente_ruc,
                      di.codigo_sunat as tipo_doc_cliente,
                      m.nombre as motivo_nombre, m.codigo_sunat as motivo_codigo,
                      ori.sucursales as origen_direccion, ori.ubigeo as origen_ubigeo,
                      des.direccion as destino_direccion, des.ubigeo_id as destino_ubigeo,
                      v.nro_placa as vehiculo_placa,
                      v.nro_autorizacion as vehiculo_autorizacion,
                      cond.nro_documento as conductor_nro_doc,
                      cond.nombre as conductor_nombre,
                      cond.nro_licencia as conductor_licencia,
                      cdoc.codigo_sunat as conductor_tipo_doc,
                      tr.ruc as transportista_ruc, tr.razon_social as transportista_nombre,
                      tr.nro_registro_mtc as transportista_mtc')
            ->from('al_gr_remitente g')
            ->join('vt_cliente cl',             'cl.id   = g.cliente_id')
            ->join('mt_documento_identidad di', 'di.id   = cl.documento_identidad_id',  'left')
            ->join('mt_motivo_traslado m',      'm.id    = g.motivo_traslado_id')
            ->join('empresa_detalle ori',       'ori.id  = g.origen_id',                'left')
            ->join('vt_cliente_local des',      'des.id  = g.destino_id',               'left')
            ->join('mt_vehiculo v',             'v.id    = g.vehiculo_id',              'left')
            ->join('mt_conductor cond',         'cond.id = g.conductor_id',             'left')
            ->join('mt_documento_identidad cdoc','cdoc.id = cond.documento_identidad_id','left')
            ->join('mt_transportista tr',       'tr.id   = g.transportista_id',         'left')
            ->where('g.id', $gr_id)
            ->get()->row();

        if (!$guia) return array('status' => false, 'message' => 'Guía no encontrada');

        // Detalle
        $detalle = $this->CI->db
            ->select('d.*, p.codigo as producto_codigo, p.nombre as producto_nombre,
                      um.codigo_sunat as um_codigo')
            ->from('al_gr_remitente_detalle d')
            ->join('vt_producto p',       'p.id  = d.producto_id')
            ->join('mt_unidad_medida um', 'um.id = d.um_id', 'left')
            ->where('d.gr_remitente_id', $gr_id)
            ->get()->result();

        // Modo de traslado: 01=Privado, 02=Público
        $mod_traslado = ($guia->tipo_transporte === 'PRIVADO') ? '01' : '02';

        // Shipment (envío)
        $shipment = (new Shipment())
            ->setCodTraslado($guia->motivo_codigo)
            ->setDesTraslado($guia->motivo_nombre)
            ->setModTraslado($mod_traslado)
            ->setFecTraslado(new DateTime($guia->fecha_traslado))
            ->setPesoTotal((float)$guia->peso_bruto_carga)
            ->setUndPesoTotal('KGM')
            ->setPartida(new Direction($guia->origen_ubigeo ?? '150101', $guia->origen_direccion ?? ''))
            ->setLlegada(new Direction($guia->destino_ubigeo ?? '150101', $guia->destino_direccion ?? ''));

        // Vehículo (transporte privado)
        if ($mod_traslado === '01' && !empty($guia->vehiculo_placa)) {
            $vehicle = (new Vehicle())
                ->setPlaca($guia->vehiculo_placa)
                ->setNroAutorizacion($guia->vehiculo_autorizacion ?? '');
            $shipment->setVehiculo($vehicle);
        }

        // Conductor (transporte privado)
        if ($mod_traslado === '01' && !empty($guia->conductor_nro_doc)) {
            $conductor_nombres  = explode(' ', $guia->conductor_nombre ?? '');
            $apellidos = array_splice($conductor_nombres, 0, 2);

            $driver = (new Driver())
                ->setTipo('01') // 01 = Principal
                ->setTipoDoc($guia->conductor_tipo_doc ?? '1')
                ->setNroDoc($guia->conductor_nro_doc)
                ->setNombres(implode(' ', $conductor_nombres) ?: $guia->conductor_nombre)
                ->setApellidos(implode(' ', $apellidos) ?: '')
                ->setLicencia($guia->conductor_licencia ?? '');
            $shipment->setChoferes([$driver]);
        }

        // Transportista (transporte público)
        if ($mod_traslado === '02' && !empty($guia->transportista_ruc)) {
            $transportista = (new Transportist())
                ->setTipoDoc('6')
                ->setNumDoc($guia->transportista_ruc)
                ->setRznSocial($guia->transportista_nombre)
                ->setNroMtc($guia->transportista_mtc ?? '');
            $shipment->setTransportista($transportista);
        }

        // Armar Despatch
        $despatch = (new Despatch())
            ->setVersion('2022')
            ->setTipoDoc('09')
            ->setSerie($guia->serie)
            ->setCorrelativo($guia->numero)
            ->setFechaEmision(new DateTime($guia->fecha_emision))
            ->setObservacion($guia->observaciones ?? '')
            ->setCompany($this->getCompany())
            ->setDestinatario((new Client())
                ->setTipoDoc($guia->tipo_doc_cliente ?? '6')
                ->setNumDoc($guia->cliente_ruc)
                ->setRznSocial($guia->cliente_nombre))
            ->setEnvio($shipment);

        // Detalle de bienes
        $items = array();
        foreach ($detalle as $d) {
            $items[] = (new DespatchDetail())
                ->setCodigo($d->producto_codigo)
                ->setDescripcion($d->producto_nombre)
                ->setUnidad($d->um_codigo ?? 'NIU')
                ->setCantidad((float)$d->cantidad);
        }
        $despatch->setDetails($items);

        // Enviar — See separado con endpoint específico para guías
        try {
            $certPath    = FCPATH . $this->empresa->cert_path;
            $password    = $this->empresa->cert_password ?? '';
            $pfx         = file_get_contents($certPath);
            $certificate = new X509Certificate($pfx, $password);
            $certPem     = $certificate->export(X509ContentType::PEM);

            $modo = $this->empresa->sunat_modo ?? 'beta';
            $endpoint_guia = ($modo === 'produccion')
                ? SunatEndpoints::GUIA_PRODUCCION
                : SunatEndpoints::GUIA_BETA;

            $see_guia = new See();
            $see_guia->setCertificate($certPem);
            $see_guia->setService($endpoint_guia);
            $see_guia->setCredentials(
                $this->empresa->ruc . $this->empresa->sol_usuario,
                $this->empresa->sol_clave
            );

            $result = $see_guia->send($despatch);
            return $this->procesarRespuesta($result, $gr_id, 'guia');
        } catch (Exception $e) {
            return array('status' => false, 'message' => $e->getMessage());
        }
    }

    // ---------------------------------------------------------------
    // PROCESAR RESPUESTA SUNAT
    // ---------------------------------------------------------------
    private function procesarRespuesta($result, $doc_id, $tipo, $nombre_doc = null) {
        // Guardar XML firmado siempre (aunque sea rechazado)
        $xml_path = null;
        $cdr_path = null;

        try {
            if ($nombre_doc) {
                $xml_dir = FCPATH . 'uploads/sunat/xml/';
                $cdr_dir = FCPATH . 'uploads/sunat/cdr/';

                if (!is_dir($xml_dir)) mkdir($xml_dir, 0755, true);
                if (!is_dir($cdr_dir)) mkdir($cdr_dir, 0755, true);

                // Obtener XML — Greenter v5 lo expone desde el See
                $xml_content = null;
                if (method_exists($result, 'getXml')) {
                    $xml_content = $result->getXml();
                } elseif (method_exists($this->see, 'getFactory')) {
                    try { $xml_content = $this->see->getFactory()->getLastXml(); } catch (Exception $ex) {}
                }

                if ($xml_content) {
                    $xml_filename = $nombre_doc . '.xml';
                    file_put_contents($xml_dir . $xml_filename, $xml_content);
                    $xml_path = 'uploads/sunat/xml/' . $xml_filename;
                }
            }
        } catch (Exception $e) {
            // No interrumpir si falla el guardado del XML
        }

        if (!$result->isSuccess()) {
            $error   = $result->getError();
            $mensaje = $error ? $error->getMessage() : 'Error desconocido';
            $this->actualizarEstadoSunat($doc_id, $tipo, 2, $mensaje, $xml_path, null, null, null);
            return array('status' => false, 'message' => $mensaje);
        }

        $cdr = $result->getCdrResponse();
        if (!$cdr) {
            return array('status' => false, 'message' => 'Sin respuesta CDR de SUNAT');
        }

        $codigo      = $cdr->getCode();
        $descripcion = $cdr->getDescription();
        $aceptado    = (int)$codigo === 0;

        // Guardar CDR ZIP
        try {
            if ($nombre_doc && $result->getCdrZip()) {
                $cdr_filename = 'R-' . $nombre_doc . '.zip';
                $cdr_full     = FCPATH . 'uploads/sunat/cdr/' . $cdr_filename;
                file_put_contents($cdr_full, $result->getCdrZip());
                $cdr_path = 'uploads/sunat/cdr/' . $cdr_filename;
            }
        } catch (Exception $e) {
            // No interrumpir si falla el guardado del CDR
        }

        $this->actualizarEstadoSunat($doc_id, $tipo, $aceptado ? 1 : 2, $descripcion, $xml_path, $cdr_path, $codigo, $descripcion);

        return array(
            'status'        => $aceptado,
            'codigo'        => $codigo,
            'message'       => $descripcion,
            'observaciones' => $cdr->getNotes() ?? array(),
        );
    }

    // ---------------------------------------------------------------
    // ACTUALIZAR ESTADO EN BD
    // ---------------------------------------------------------------
    private function actualizarEstadoSunat($doc_id, $tipo, $estado, $mensaje = null, $xml_path = null, $cdr_path = null, $codigo = null, $descripcion = null) {
        $ahora = date('Y-m-d H:i:s');
        switch ($tipo) {
            case 'comprobante':
                $data = array(
                    'comprob_sunat'   => $estado,
                    'updated_at'      => $ahora,
                );
                if ($xml_path)    $data['xml_path']        = $xml_path;
                if ($cdr_path)    $data['cdr_path']        = $cdr_path;
                if ($codigo)      $data['cdr_codigo']      = $codigo;
                if ($descripcion) $data['cdr_descripcion'] = $descripcion;
                $this->CI->db->where('id', $doc_id)->update('vt_comprobante', $data);
                break;
            case 'nota_credito':
                $data = array(
                    'envio_sunat' => $estado,
                    'cdr_sunat'   => $mensaje,
                    'updated_at'  => $ahora,
                );
                if ($xml_path) $data['xml_path'] = $xml_path;
                if ($cdr_path) $data['cdr_path'] = $cdr_path;
                $this->CI->db->where('id', $doc_id)->update('vt_nota_credito', $data);
                break;
            case 'nota_debito':
                $data = array(
                    'envio_sunat' => $estado,
                    'cdr_sunat'   => $mensaje,
                    'updated_at'  => $ahora,
                );
                if ($xml_path) $data['xml_path'] = $xml_path;
                if ($cdr_path) $data['cdr_path'] = $cdr_path;
                $this->CI->db->where('id', $doc_id)->update('vt_nota_debito', $data);
                break;
            case 'guia':
                $this->CI->db->where('id', $doc_id)->update('al_gr_remitente', array(
                    'envio_sunat' => $estado,
                    'updated_at'  => $ahora,
                ));
                break;
        }
    }
}
