const express = require('express');
const router = express.Router();
const db = require('../config/db');
const dh = require('./distinctHelper');


// ...

function mapearEstado(estado) {
    var map = { P: 'Pendiente', A: 'Aprobado', N: 'Anulado' };
    return map[estado] || estado;
}

router.get('/guias-remision', async function(req, res) {
    try {
        if (!req.query.id_empresa) return res.status(400).json({ ok: false, mensaje: 'Empresa requerida' });
        var idEmpresa = parseInt(req.query.id_empresa);

        var buscar = req.query.buscar || '';
        var page = req.query.page ? parseInt(req.query.page) : 1;
        var limit = req.query.limit ? parseInt(req.query.limit) : 50;
        var sinPaginacion = limit <= 0;
        var sort = req.query.sort || 'g.fecha_guia';
        var order = req.query.order === 'DESC' ? 'DESC' : 'ASC';
        var tpoGui = req.query.tpo_gui || 'I';

        var where = 'WHERE g.id_empresa = @id_empresa AND g.tpo_gui = @tpo_gui';
        var params = {
            id_empresa: { type: db.sql.Int, value: idEmpresa },
            tpo_gui: { type: db.sql.Char(1), value: tpoGui }
        };

        var idSucursal = req.query.id_sucursal ? parseInt(req.query.id_sucursal) : null;
        if (idSucursal) {
            where += ' AND g.id_sucursal = @id_sucursal';
            params.id_sucursal = { type: db.sql.Int, value: idSucursal };
        }
        var extraJoins = '';

        if (buscar) {
            where += ' AND (g.cod_guia LIKE @buscar OR p.razon_social LIKE @buscar OR c.razon_social LIKE @buscar OR EXISTS (SELECT 1 FROM guias_remision_det d WHERE d.id_guia = g.id_guia AND d.id_empresa = @id_empresa AND (d.num_oc LIKE @buscar OR CONCAT(d.ejercicio, \'-\', d.num_oc) LIKE @buscar)))';
            params.buscar = { type: db.sql.NVarChar(100), value: '%' + buscar + '%' };
        }

        var fechaDesde = req.query.fecha_desde;
        var fechaHasta = req.query.fecha_hasta;
        if (fechaDesde) {
            where += ' AND g.fecha_guia >= @fecha_desde';
            params.fecha_desde = { type: db.sql.Date, value: fechaDesde };
        }
        if (fechaHasta) {
            where += ' AND g.fecha_guia <= @fecha_hasta';
            params.fecha_hasta = { type: db.sql.Date, value: fechaHasta };
        }

        var filtros = req.query.filtros ? JSON.parse(req.query.filtros) : null;
        var grFMap = { cod_guia: 'g.cod_guia', proveedor_razon_social: 'p.razon_social', cliente_razon_social: 'c.razon_social', estado: 'g.estado', motivo_nombre: 'mg.nombre' };
        if (filtros) {
            var fIdx = 0;
            for (var col in filtros) {
                var vals = Array.isArray(filtros[col]) ? filtros[col] : String(filtros[col]).split('\x01');
                var conds;
                if (col === 'num_ocs') {
                    conds = vals.map(function(v) {
                        var pn = 'f' + fIdx;
                        fIdx++;
                        params[pn] = { type: db.sql.NVarChar(200), value: '%' + v.trim() + '%' };
                        return 'EXISTS (SELECT 1 FROM guias_remision_det d_ref WHERE d_ref.id_guia = g.id_guia AND d_ref.id_empresa = @id_empresa AND CONCAT(d_ref.ejercicio, \'-\', d_ref.num_oc) LIKE @' + pn + ')';
                    });
                } else {
                    var dbCol = grFMap[col] || col;
                    conds = vals.map(function(v) {
                        var pn = 'f' + fIdx;
                        fIdx++;
                        params[pn] = { type: db.sql.NVarChar(200), value: '%' + v.trim() + '%' };
                        return dbCol + ' LIKE @' + pn;
                    });
                }
                where += ' AND (' + conds.join(' OR ') + ')';
            }
        }

        var grJoins = 'LEFT JOIN proveedores p ON g.id_empresa = p.id_empresa AND g.id_proveedor = p.id_proveedor LEFT JOIN clientes c ON g.id_empresa = c.id_empresa AND g.id_cliente = c.id_cliente LEFT JOIN motivos_guia mg ON g.id_empresa = mg.id_empresa AND g.id_motivo = mg.id_motivo';
        if (await dh.tryDistinct(req, res, { db, table: 'guias_remision', alias: 'g', joins: grJoins, where, params, colMap: grFMap })) return;

        var baseJoin = ' FROM guias_remision g ' + grJoins;
        var allJoins = baseJoin + extraJoins;

        var sqlCount = 'SELECT COUNT(*) as total' + allJoins + ' ' + where;
        var countResult = await db.query(sqlCount, params);
        var total = countResult[0].total;
        var totalPaginas = total > 0 ? Math.ceil(total / Math.abs(limit)) : 0;

        var allowedSort = { cod_guia: 'g.cod_guia', fecha_guia: 'g.fecha_guia', proveedor_razon_social: 'p.razon_social', cliente_razon_social: 'c.razon_social', motivo_nombre: 'mg.nombre', estado: 'g.estado', num_ocs: 'num_ocs', total: 'g.total' };
        var sortCol = allowedSort[sort] || 'g.fecha_guia';

        var offset = sinPaginacion ? 0 : (page - 1) * limit;
        var fetchLimit = sinPaginacion ? 10000 : limit;

        var sql = 'SELECT g.id_guia, g.id_sucursal, g.cod_guia, g.serie, g.numero, g.tpo_gui, g.fecha_guia, g.estado, g.total, g.moneda, g.subtotal, g.igv, g.observaciones, g.num_oc AS cab_num_oc, p.razon_social AS proveedor_razon_social, c.razon_social AS cliente_razon_social, mg.nombre AS motivo_nombre, g.id_cliente, g.id_proveedor' + allJoins + ' ' + where +
            ' ORDER BY ' + sortCol + ' ' + order +
            ' OFFSET ' + offset + ' ROWS FETCH NEXT ' + fetchLimit + ' ROWS ONLY';

        var data = await db.query(sql, params);
        var ids = data.map(function(r) { return r.id_guia; });
        var ocMap = {};
        if (ids.length > 0) {
            var ocRows = await db.query(
                'SELECT d.id_guia, CONCAT(d.ejercicio, \'-\', d.num_oc) AS oc_ref FROM guias_remision_det d WHERE d.id_guia IN (' + ids.join(',') + ') AND d.num_oc IS NOT NULL',
                params
            );
            var ocArr = Array.isArray(ocRows) ? ocRows : [];
            for (var oi = 0; oi < ocArr.length; oi++) {
                var r = ocArr[oi];
                if (!ocMap[r.id_guia]) ocMap[r.id_guia] = [];
                ocMap[r.id_guia].push(r.oc_ref);
            }
            for (var g in ocMap) {
                ocMap[g] = ocMap[g].join(', ');
            }
        }
        var dataMap = data.map(function(r) {
            return {
                id_guia: r.id_guia,
                cod_guia: r.cod_guia,
                serie: r.serie,
                numero: r.numero,
                tpo_gui: r.tpo_gui,
                fecha_guia: r.fecha_guia,
                num_ocs: ocMap[r.id_guia] || r.cab_num_oc || '-',
                estado: r.estado,
                estado_texto: mapearEstado(r.estado),
                total: r.total,
                moneda: r.moneda,
                subtotal: r.subtotal,
                igv: r.igv,
                observaciones: r.observaciones,
                proveedor_razon_social: r.proveedor_razon_social,
                cliente_razon_social: r.cliente_razon_social,
                motivo_nombre: r.motivo_nombre,
                id_cliente: r.id_cliente,
                id_proveedor: r.id_proveedor
            };
        });

        res.json({ ok: true, data: dataMap, total: total, totalPaginas: totalPaginas });
    } catch (error) {
        console.error('[GUIAS_REMISION] Error listar:', error.message);
        console.error('[GUIAS_REMISION] Stack:', error.stack);
        res.status(500).json({ ok: false, mensaje: 'Error al listar guías: ' + error.message });
    }
});

router.get('/guias-remision/:id_guia', async function(req, res) {
    try {
        if (!req.query.id_empresa) return res.status(400).json({ ok: false, mensaje: 'Empresa requerida' });
        var idEmpresa = parseInt(req.query.id_empresa);

        var cab = await db.query(
            `SELECT g.*, p.razon_social AS proveedor_razon_social, c.razon_social AS cliente_razon_social, 
                    mg.nombre AS motivo_nombre,
                    t.razon_social AS transportista_razon_social,
                    v.placa AS vehiculo_placa, v.marca AS vehiculo_marca, v.modelo AS vehiculo_modelo,
                    ch.nombre AS conductor_nombre, ch.num_documento AS conductor_dni, ch.licencia_conducir AS conductor_licencia
             FROM guias_remision g 
             LEFT JOIN proveedores p ON g.id_empresa = p.id_empresa AND g.id_proveedor = p.id_proveedor 
             LEFT JOIN clientes c ON g.id_empresa = c.id_empresa AND g.id_cliente = c.id_cliente
             LEFT JOIN motivos_guia mg ON g.id_empresa = mg.id_empresa AND g.id_motivo = mg.id_motivo
             LEFT JOIN transportistas t ON g.id_empresa = t.id_empresa AND g.id_transportista = t.id_transportista
             LEFT JOIN vehiculos v ON g.id_empresa = v.id_empresa AND g.id_vehiculo = v.id_vehiculo
             LEFT JOIN choferes ch ON g.id_empresa = ch.id_empresa AND g.id_chofer = ch.id_chofer
             WHERE g.id_guia = @id_guia AND g.id_empresa = @id_empresa`,
            { id_guia: { type: db.sql.Int, value: parseInt(req.params.id_guia) }, id_empresa: { type: db.sql.Int, value: idEmpresa } }
        );
        if (cab.length === 0) return res.status(404).json({ ok: false, mensaje: 'Guía no encontrada' });

        var det = await db.query(
            `SELECT d.id_guia_det, d.id_guia, d.secuencia, d.id_articulo, d.cantidad, d.costo_unitario, d.total, d.peso, d.unidades, d.num_oc, d.ejercicio, 
                    a.cod_articulo, a.nombre, u.cod_sunat AS cod_unidad, d.des_art
             FROM guias_remision_det d 
             INNER JOIN articulos a ON d.id_empresa = a.id_empresa AND d.id_articulo = a.id_articulo 
             LEFT JOIN unidad_medida u ON a.id_empresa = u.id_empresa AND a.id_unidad = u.id_unidad 
             WHERE d.id_guia = @id_guia AND d.id_empresa = @id_empresa ORDER BY d.secuencia`,
            { id_guia: { type: db.sql.Int, value: parseInt(req.params.id_guia) }, id_empresa: { type: db.sql.Int, value: idEmpresa } }
        );

        var ocRows = await db.query(
            'SELECT CONCAT(d.ejercicio, \'-\', d.num_oc) AS oc_ref FROM guias_remision_det d WHERE d.id_guia = @id_guia AND d.id_empresa = @id_empresa AND d.num_oc IS NOT NULL',
            { id_guia: { type: db.sql.Int, value: parseInt(req.params.id_guia) }, id_empresa: { type: db.sql.Int, value: idEmpresa } }
        );
        var ocArr = Array.isArray(ocRows) ? ocRows : [];
        var numOcs = ocArr.map(function(r) { return r.oc_ref; }).join(', ');

        var row = cab[0];
        row.detalle = det;
        row.num_ocs = numOcs || row.num_oc || '';
        res.json({ ok: true, data: row });
    } catch (error) {
        console.error('[GUIAS_REMISION] Error obtener:', error.message);
        res.status(500).json({ ok: false, mensaje: 'Error al obtener guía' });
    }
});

router.get('/guias-remision/correlativo/:id_empresa', async function(req, res) {
    try {
        var result = await db.query(
            "SELECT ISNULL(MAX(CAST(SUBSTRING(cod_guia, 6, 10) AS INT)), 0) + 1 AS next FROM guias_remision WHERE id_empresa = @id_empresa",
            { id_empresa: { type: db.sql.Int, value: parseInt(req.params.id_empresa) } }
        );
        var codigo = 'GUIR-' + String(result[0].next).padStart(6, '0');
        res.json({ ok: true, data: { cod_guia: codigo } });
    } catch (error) {
        console.error('[GUIAS_REMISION] Error correlativo:', error.message);
        res.status(500).json({ ok: false, mensaje: 'Error al generar correlativo' });
    }
});

router.get('/guias-remision/oc-disponibles/:id_empresa', async function(req, res) {
    try {
        var idEmpresa = parseInt(req.params.id_empresa);
        if (!idEmpresa || isNaN(idEmpresa)) return res.status(400).json({ ok: false, mensaje: 'Empresa requerida' });

        var buscar = req.query.buscar || '';
        var idProveedor = req.query.id_proveedor || '';
        var ejercicio = parseInt(req.query.ejercicio) || new Date().getFullYear();

        var where = 'WHERE co.id_empresa = @id_empresa AND co.estado IN (\'A\', \'P\') AND co.ejercicio = @ejercicio';
        var params = {
            id_empresa: { type: db.sql.Int, value: idEmpresa },
            ejercicio: { type: db.sql.Int, value: ejercicio }
        };

        if (buscar) {
            where += ' AND (co.num_oc LIKE @buscar OR p.razon_social LIKE @buscar)';
            params.buscar = { type: db.sql.NVarChar(100), value: '%' + buscar + '%' };
        }
        if (idProveedor) {
            where += ' AND co.id_proveedor = @id_proveedor';
            params.id_proveedor = { type: db.sql.Int, value: parseInt(idProveedor) };
        }

        var sql = 'SELECT co.ejercicio, co.num_oc, co.fecha_oc, p.razon_social AS proveedor_razon_social, co.id_proveedor, co.moneda, co.tipo_cambio, co.subtotal, co.igv, co.total FROM cab_orden_compra co LEFT JOIN proveedores p ON co.id_empresa = p.id_empresa AND co.id_proveedor = p.id_proveedor ' + where +
            ' ORDER BY co.fecha_oc DESC';

        var data = await db.query(sql, params);

        var result = [];
        for (var i = 0; i < data.length; i++) {
            var oc = data[i];
            var det = await db.query(
                'SELECT d.id_articulo, d.cantidad, d.cantidad_atendida, d.precio, (d.cantidad - d.cantidad_atendida) AS saldo_pendiente, a.cod_articulo, a.nombre, u.cod_sunat AS cod_unidad FROM det_orden_compra d INNER JOIN articulos a ON d.id_empresa = a.id_empresa AND d.id_articulo = a.id_articulo LEFT JOIN unidad_medida u ON a.id_empresa = u.id_empresa AND a.id_unidad = u.id_unidad WHERE d.id_empresa = @id_empresa AND d.num_oc = @num_oc AND d.ejercicio = @ejercicio AND (d.cantidad - d.cantidad_atendida) > 0',
                { id_empresa: { type: db.sql.Int, value: idEmpresa }, num_oc: { type: db.sql.VarChar(7), value: oc.num_oc }, ejercicio: { type: db.sql.Int, value: oc.ejercicio } }
            );
            if (det.length > 0) {
                oc.detalle = det;
                result.push(oc);
            }
        }

        res.json({ ok: true, data: result });
    } catch (error) {
        console.error('[GUIAS_REMISION] Error oc-disponibles:', error.message);
        res.status(500).json({ ok: false, mensaje: 'Error al consultar OC disponibles' });
    }
});

router.get('/guias-remision/op-disponibles/:id_empresa', async function(req, res) {
    try {
        var idEmpresa = parseInt(req.params.id_empresa);
        if (!idEmpresa || isNaN(idEmpresa)) return res.status(400).json({ ok: false, mensaje: 'Empresa requerida' });

        var buscar = req.query.buscar || '';
        var idCliente = req.query.id_cliente || '';

        var where = 'WHERE pe.id_empresa = @id_empresa AND pe.estado IN (\'A\', \'P\')';
        var params = {
            id_empresa: { type: db.sql.Int, value: idEmpresa }
        };

        if (buscar) {
            where += ' AND (pe.num_pedido LIKE @buscar OR c.razon_social LIKE @buscar)';
            params.buscar = { type: db.sql.NVarChar(100), value: '%' + buscar + '%' };
        }
        if (idCliente) {
            where += ' AND pe.id_cliente = @id_cliente';
            params.id_cliente = { type: db.sql.Int, value: parseInt(idCliente) };
        }

        var sql = `SELECT pe.num_pedido, pe.fecha_pedido, c.razon_social AS cliente_razon_social, c.id_cliente
                   FROM cab_pedido pe 
                   LEFT JOIN clientes c ON pe.id_empresa = c.id_empresa AND pe.id_cliente = c.id_cliente 
                   ${where}
                   ORDER BY pe.fecha_pedido DESC`;

        var data = await db.query(sql, params);

        var result = [];
        for (var i = 0; i < data.length; i++) {
            var op = data[i];
            var det = await db.query(
                `SELECT d.id_articulo, d.cantidad, d.cantidad_atendida, d.precio, (d.cantidad - ISNULL(d.cantidad_atendida, 0)) AS saldo_pendiente, 
                        a.cod_articulo, a.nombre, u.cod_sunat AS cod_unidad
                 FROM det_pedido d 
                 INNER JOIN articulos a ON d.id_empresa = a.id_empresa AND d.id_articulo = a.id_articulo 
                 LEFT JOIN unidad_medida u ON a.id_empresa = u.id_empresa AND a.id_unidad = u.id_unidad 
                 WHERE d.id_empresa = @id_empresa AND d.num_pedido = @num_pedido AND (d.cantidad - ISNULL(d.cantidad_atendida, 0)) > 0`,
                { id_empresa: { type: db.sql.Int, value: idEmpresa }, num_pedido: { type: db.sql.VarChar(7), value: op.num_pedido } }
            );
            if (det.length > 0) {
                op.detalle = det;
                result.push(op);
            }
        }

        res.json({ ok: true, data: result });
    } catch (error) {
        console.error('[GUIAS_REMISION] Error op-disponibles:', error.message);
        res.status(500).json({ ok: false, mensaje: 'Error al consultar OP disponibles' });
    }
});

router.post('/guias-remision', async function(req, res) {
    try {
        var body = req.body;
        if (!body.id_empresa) return res.status(400).json({ ok: false, mensaje: 'Empresa requerida' });
        if (!body.detalle || body.detalle.length === 0) return res.status(400).json({ ok: false, mensaje: 'Debe agregar al menos un artículo' });

        var tpoGui = body.tpo_gui || 'I';

        if (tpoGui === 'I') {
            if (!body.id_proveedor) return res.status(400).json({ ok: false, mensaje: 'Proveedor requerido para guía de ingreso' });
        } else {
            if (!body.id_cliente) return res.status(400).json({ ok: false, mensaje: 'Cliente requerido para guía de salida' });
        }

        var pool = await db.getPool();
        var transaction = pool.transaction();
        await transaction.begin();

        try {
            // Validar stock suficiente para guías de salida
            if (tpoGui === 'S') {
                for (var vi = 0; vi < body.detalle.length; vi++) {
                    var dv = body.detalle[vi];
                    if (!dv.id_articulo || !dv.cantidad || dv.cantidad <= 0) continue;
                    var stockReq = transaction.request();
                    stockReq.input('id_empresa', db.sql.Int, body.id_empresa);
                    stockReq.input('id_articulo', db.sql.Int, dv.id_articulo);
                    var stockRes = await stockReq.query('SELECT stock_actual, cod_articulo, nombre FROM articulos WHERE id_empresa = @id_empresa AND id_articulo = @id_articulo');
                    if (stockRes.recordset.length > 0) {
                        var stk = parseFloat(stockRes.recordset[0].stock_actual) || 0;
                        var cantReq = parseFloat(dv.cantidad) || 0;
                        if (stk < cantReq) {
                            await transaction.rollback();
                            return res.status(400).json({ ok: false, mensaje: 'Stock insuficiente para ' + (stockRes.recordset[0].cod_articulo || '') + ' (' + (stockRes.recordset[0].nombre || '') + '): disponible ' + stk.toFixed(2) + ', solicitado ' + cantReq.toFixed(2) });
                        }
                    }
                }
            }

            var nextReq = transaction.request();
            nextReq.input('id_empresa', db.sql.Int, body.id_empresa);
            var nextResult = await nextReq.query(
                "SELECT ISNULL(MAX(CAST(SUBSTRING(cod_guia, 6, 10) AS INT)), 0) + 1 AS next FROM guias_remision WITH (UPDLOCK, ROWLOCK) WHERE id_empresa = @id_empresa"
            );
            var codigo = 'GUIR-' + String(nextResult.recordset[0].next).padStart(6, '0');

            var cabReq = transaction.request();
            cabReq.input('id_empresa', db.sql.Int, body.id_empresa);
            cabReq.input('cod_guia', db.sql.VarChar(15), codigo);
            cabReq.input('serie', db.sql.VarChar(4), body.serie || null);
            cabReq.input('numero', db.sql.VarChar(10), body.numero || null);
            cabReq.input('tpo_gui', db.sql.Char(1), tpoGui);
            cabReq.input('id_proveedor', db.sql.Int, body.id_proveedor || null);
            cabReq.input('id_cliente', db.sql.Int, body.id_cliente || null);
            cabReq.input('id_motivo', db.sql.Int, body.id_motivo || null);
            cabReq.input('num_oc', db.sql.VarChar(7), body.num_oc || null);
            cabReq.input('oc_ejercicio', db.sql.Int, body.oc_ejercicio || null);
            cabReq.input('num_orp', db.sql.VarChar(7), body.num_orp || null);
            cabReq.input('per_orp', db.sql.VarChar(7), body.per_orp || null);
            cabReq.input('fecha_guia', db.sql.Date, body.fecha_guia || new Date().toISOString().split('T')[0]);
            cabReq.input('moneda', db.sql.VarChar(2), body.moneda || 'MN');
            cabReq.input('tipo_cambio', db.sql.Decimal(7,3), parseFloat(body.tipo_cambio) || null);
            cabReq.input('subtotal', db.sql.Decimal(12,2), parseFloat(body.subtotal) || 0);
            cabReq.input('igv', db.sql.Decimal(10,2), parseFloat(body.igv) || 0);
            cabReq.input('total', db.sql.Decimal(12,2), parseFloat(body.total) || 0);
            cabReq.input('observaciones', db.sql.VarChar(500), body.observaciones || null);
            cabReq.input('id_transportista', db.sql.Int, body.id_transportista || null);
            cabReq.input('id_vehiculo', db.sql.Int, body.id_vehiculo || null);
            cabReq.input('id_chofer', db.sql.Int, body.id_chofer || null);
            cabReq.input('direccion_partida', db.sql.VarChar(200), body.direccion_partida || null);
            cabReq.input('direccion_llegada', db.sql.VarChar(200), body.direccion_llegada || null);
            cabReq.input('tip_gui', db.sql.Char(1), body.tip_gui || 'R');
            cabReq.input('est_alm', db.sql.Char(1), body.est_alm || null);
            cabReq.input('txt_gui', db.sql.VarChar(250), body.txt_gui || null);
            cabReq.input('estado', db.sql.VarChar(1), body.estado || 'P');
             cabReq.input('id_sucursal', db.sql.Int, body.id_sucursal || null);
            cabReq.input('usuario_crea', db.sql.Int, body.usuario_crea || null);
            var cabResult = await cabReq.query(
                `INSERT INTO guias_remision (id_empresa, id_sucursal, cod_guia, serie, numero, tpo_gui, id_proveedor, id_cliente, id_motivo, num_oc, oc_ejercicio, num_orp, per_orp, fecha_guia, moneda, tipo_cambio, subtotal, igv, total, observaciones, id_transportista, id_vehiculo, id_chofer, direccion_partida, direccion_llegada, tip_gui, est_alm, txt_gui, estado, usuario_crea) 
                 OUTPUT INSERTED.id_guia 
                 VALUES (@id_empresa, @id_sucursal, @cod_guia, @serie, @numero, @tpo_gui, @id_proveedor, @id_cliente, @id_motivo, @num_oc, @oc_ejercicio, @num_orp, @per_orp, @fecha_guia, @moneda, @tipo_cambio, @subtotal, @igv, @total, @observaciones, @id_transportista, @id_vehiculo, @id_chofer, @direccion_partida, @direccion_llegada, @tip_gui, @est_alm, @txt_gui, @estado, @usuario_crea)`
            );
            var idGuia = cabResult.recordset[0].id_guia;

            for (var i = 0; i < body.detalle.length; i++) {
                var d = body.detalle[i];
                if (!d.id_articulo || !d.cantidad || d.cantidad <= 0) continue;
                var detReq = transaction.request();
                detReq.input('id_guia', db.sql.Int, idGuia);
                detReq.input('id_empresa', db.sql.Int, body.id_empresa);
                detReq.input('secuencia', db.sql.Int, i + 1);
                detReq.input('id_articulo', db.sql.Int, d.id_articulo);
                detReq.input('cantidad', db.sql.Decimal(9,3), parseFloat(d.cantidad) || 0);
                detReq.input('costo_unitario', db.sql.Decimal(10,4), parseFloat(d.costo_unitario) || 0);
                detReq.input('total', db.sql.Decimal(12,2), parseFloat(d.total) || 0);
                detReq.input('peso', db.sql.Decimal(9,3), parseFloat(d.peso) || 0);
                detReq.input('unidades', db.sql.Int, parseInt(d.unidades) || 0);
                detReq.input('num_oc', db.sql.VarChar(7), d.num_oc || null);
                detReq.input('ejercicio_oc', db.sql.Int, d.ejercicio || null);
                detReq.input('des_art', db.sql.VarChar(60), d.des_art || null);
                await detReq.query('INSERT INTO guias_remision_det (id_guia, id_empresa, secuencia, id_articulo, cantidad, costo_unitario, total, peso, unidades, num_oc, ejercicio, des_art) VALUES (@id_guia, @id_empresa, @secuencia, @id_articulo, @cantidad, @costo_unitario, @total, @peso, @unidades, @num_oc, @ejercicio_oc, @des_art)');

                var cantidad = parseFloat(d.cantidad) || 0;
                var costoUnitario = parseFloat(d.costo_unitario) || 0;
                var costoTotal = cantidad * costoUnitario;
                var fecha = body.fecha_guia || new Date().toISOString().split('T')[0];

                if (tpoGui === 'I') {
                    var detNumOc = d.num_oc || body.num_oc;
                    var detEjercicio = d.ejercicio || body.oc_ejercicio;
                    if (detNumOc && detEjercicio) {
                        var updReq = transaction.request();
                        updReq.input('id_empresa', db.sql.Int, body.id_empresa);
                        updReq.input('num_oc', db.sql.VarChar(7), detNumOc);
                        updReq.input('ejercicio', db.sql.Int, detEjercicio);
                        updReq.input('id_articulo', db.sql.Int, d.id_articulo);
                        updReq.input('cantidad', db.sql.Decimal(9,3), cantidad);
                        await updReq.query('UPDATE det_orden_compra SET cantidad_atendida = ISNULL(cantidad_atendida, 0) + @cantidad, flg_gui = 1 WHERE id_empresa = @id_empresa AND ejercicio = @ejercicio AND num_oc = @num_oc AND id_articulo = @id_articulo');
                    }
                }

                var artSel = transaction.request();
                artSel.input('id_empresa', db.sql.Int, body.id_empresa);
                artSel.input('id_articulo', db.sql.Int, d.id_articulo);
                var artData = await artSel.query(
                    'SELECT stock_actual, costo_promedio FROM articulos WITH (UPDLOCK, ROWLOCK) WHERE id_empresa = @id_empresa AND id_articulo = @id_articulo'
                );
                var stockActual = (artData.recordset.length > 0 && artData.recordset[0].stock_actual != null) ? parseFloat(artData.recordset[0].stock_actual) : 0;
                var costoActual = (artData.recordset.length > 0 && artData.recordset[0].costo_promedio) ? parseFloat(artData.recordset[0].costo_promedio) : 0;

                var nuevoStock, nuevoCostoPromedio;
                if (tpoGui === 'I') {
                    nuevoStock = stockActual + cantidad;
                    nuevoCostoPromedio = nuevoStock > 0 ? ((stockActual * costoActual) + costoTotal) / nuevoStock : costoUnitario;
                } else {
                    nuevoStock = Math.max(0, stockActual - cantidad);
                    nuevoCostoPromedio = nuevoStock > 0 ? ((stockActual * costoActual) - costoTotal) / nuevoStock : 0;
                }

                var tipoMov = tpoGui === 'I' ? 'E' : 'S';

                var movReq = transaction.request();
                movReq.input('id_empresa', db.sql.Int, body.id_empresa);
                movReq.input('id_articulo', db.sql.Int, d.id_articulo);
                movReq.input('tipo', db.sql.VarChar(1), tipoMov);
                movReq.input('documento', db.sql.VarChar(30), codigo);
                movReq.input('id_documento', db.sql.Int, idGuia);
                movReq.input('cantidad', db.sql.Decimal(9,3), cantidad);
                movReq.input('costo_unitario', db.sql.Decimal(10,4), costoUnitario);
                movReq.input('costo_total', db.sql.Decimal(12,2), costoTotal);
                movReq.input('fecha_movimiento', db.sql.Date, fecha);
                movReq.input('usuario_crea', db.sql.Int, body.usuario_crea || null);
                var movResult = await movReq.query(
                    'INSERT INTO movimiento_almacen (id_empresa, id_articulo, tipo, documento, id_documento, cantidad, costo_unitario, costo_total, fecha_movimiento, usuario_crea) OUTPUT INSERTED.id_movimiento VALUES (@id_empresa, @id_articulo, @tipo, @documento, @id_documento, @cantidad, @costo_unitario, @costo_total, @fecha_movimiento, @usuario_crea)'
                );
                var idMovimiento = movResult.recordset[0].id_movimiento;

                var costReq = transaction.request();
                costReq.input('id_empresa', db.sql.Int, body.id_empresa);
                costReq.input('id_articulo', db.sql.Int, d.id_articulo);
                costReq.input('tipo_costo', db.sql.VarChar(1), 'P');
                costReq.input('costo_unitario', db.sql.Decimal(10,4), nuevoCostoPromedio);
                costReq.input('cantidad', db.sql.Decimal(9,3), cantidad);
                costReq.input('costo_total', db.sql.Decimal(12,2), costoTotal);
                costReq.input('fecha_movimiento', db.sql.Date, fecha);
                costReq.input('id_movimiento', db.sql.Int, idMovimiento);
                costReq.input('usuario_crea', db.sql.Int, body.usuario_crea || null);
                await costReq.query(
                    'INSERT INTO costo (id_empresa, id_articulo, tipo_costo, costo_unitario, cantidad, costo_total, fecha_movimiento, id_movimiento, usuario_crea) VALUES (@id_empresa, @id_articulo, @tipo_costo, @costo_unitario, @cantidad, @costo_total, @fecha_movimiento, @id_movimiento, @usuario_crea)'
                );

                var artReq = transaction.request();
                artReq.input('id_empresa', db.sql.Int, body.id_empresa);
                artReq.input('id_articulo', db.sql.Int, d.id_articulo);
                artReq.input('stock', db.sql.Decimal(9,3), nuevoStock);
                artReq.input('costo_promedio', db.sql.Decimal(10,4), nuevoCostoPromedio);
                await artReq.query('UPDATE articulos SET stock_actual = @stock, costo_promedio = @costo_promedio WHERE id_empresa = @id_empresa AND id_articulo = @id_articulo');
            }

            if (tpoGui === 'I') {
                var ocsMap = {};
                for (var j = 0; j < body.detalle.length; j++) {
                    var det = body.detalle[j];
                    if (!det.id_articulo || !det.cantidad || det.cantidad <= 0) continue;
                    var detNumOc = det.num_oc || body.num_oc;
                    var detEj = det.ejercicio || body.oc_ejercicio;
                    if (detNumOc && detEj) {
                        ocsMap[detEj + '|' + detNumOc] = { num_oc: detNumOc, ejercicio: detEj };
                    }
                }
                var ocKeys = Object.keys(ocsMap);
                for (var k = 0; k < ocKeys.length; k++) {
                    var ocInfo = ocsMap[ocKeys[k]];
                    var pendReq = transaction.request();
                    pendReq.input('id_empresa', db.sql.Int, body.id_empresa);
                    pendReq.input('num_oc', db.sql.VarChar(7), ocInfo.num_oc);
                    pendReq.input('ejercicio', db.sql.Int, ocInfo.ejercicio);
                    var pendientes = await pendReq.query(
                        'SELECT COUNT(*) AS pend FROM det_orden_compra WHERE id_empresa = @id_empresa AND ejercicio = @ejercicio AND num_oc = @num_oc AND (cantidad - ISNULL(cantidad_atendida, 0)) > 0'
                    );
                    if (parseInt(pendientes.recordset[0].pend) === 0) {
                        var ocUpdReq = transaction.request();
                        ocUpdReq.input('id_empresa', db.sql.Int, body.id_empresa);
                        ocUpdReq.input('num_oc', db.sql.VarChar(7), ocInfo.num_oc);
                        ocUpdReq.input('ejercicio', db.sql.Int, ocInfo.ejercicio);
                        ocUpdReq.input('estado', db.sql.VarChar(1), 'C');
                        await ocUpdReq.query('UPDATE cab_orden_compra SET estado = @estado WHERE id_empresa = @id_empresa AND ejercicio = @ejercicio AND num_oc = @num_oc');
                    }
                }
            }

            await transaction.commit();
            res.json({ ok: true, mensaje: 'Guía ' + codigo + ' creada', data: { id_guia: idGuia, cod_guia: codigo } });
        } catch (error) {
            await transaction.rollback();
            throw error;
        }
    } catch (error) {
        console.error('[GUIAS_REMISION] Error crear:', error.message);
        res.status(500).json({ ok: false, mensaje: 'Error al crear guía: ' + error.message });
    }
});

router.put('/guias-remision/:id_guia', async function(req, res) {
    try {
        if (!req.query.id_empresa) return res.status(400).json({ ok: false, mensaje: 'Empresa requerida' });
        var idEmpresa = parseInt(req.query.id_empresa);

        var body = req.body;
        var idGuia = parseInt(req.params.id_guia);

        var existing = await db.query('SELECT cod_guia, estado, tpo_gui FROM guias_remision WHERE id_guia = @id_guia AND id_empresa = @id_empresa', {
            id_guia: { type: db.sql.Int, value: idGuia },
            id_empresa: { type: db.sql.Int, value: idEmpresa }
        });
        if (existing.length === 0) return res.status(404).json({ ok: false, mensaje: 'Guía no encontrada' });
        if (existing[0].estado !== 'P') return res.status(400).json({ ok: false, mensaje: 'Solo se puede editar guías pendientes' });

        var tpoGui = existing[0].tpo_gui || body.tpo_gui || 'I';

        var pool = await db.getPool();
        var transaction = pool.transaction();
        await transaction.begin();

        try {
            var oldDetReq = transaction.request();
            oldDetReq.input('id_guia', db.sql.Int, idGuia);
            oldDetReq.input('id_empresa', db.sql.Int, idEmpresa);
            var oldDetResult = await oldDetReq.query('SELECT id_articulo, cantidad, costo_unitario, total, num_oc, ejercicio FROM guias_remision_det WHERE id_guia = @id_guia AND id_empresa = @id_empresa');
            var oldDetalle = oldDetResult.recordset || [];

            var oldOcsMap = {};
            for (var oi = 0; oi < oldDetalle.length; oi++) {
                var od = oldDetalle[oi];
                if (od.num_oc && od.ejercicio) {
                    var key = od.ejercicio + '|' + od.num_oc;
                    if (!oldOcsMap[key]) oldOcsMap[key] = { num_oc: od.num_oc, ejercicio: od.ejercicio };
                }
            }
            for (var oKey in oldOcsMap) {
                var oldOc = oldOcsMap[oKey];
                for (var ri = 0; ri < oldDetalle.length; ri++) {
                    var rd = oldDetalle[ri];
                    if (rd.ejercicio === oldOc.ejercicio && rd.num_oc === oldOc.num_oc && rd.id_articulo && rd.cantidad) {
                        var revUpd = transaction.request();
                        revUpd.input('id_empresa', db.sql.Int, idEmpresa);
                        revUpd.input('num_oc', db.sql.VarChar(7), oldOc.num_oc);
                        revUpd.input('ejercicio', db.sql.Int, oldOc.ejercicio);
                        revUpd.input('id_articulo', db.sql.Int, rd.id_articulo);
                        revUpd.input('cantidad', db.sql.Decimal(9,3), parseFloat(rd.cantidad) || 0);
                        await revUpd.query('UPDATE det_orden_compra SET cantidad_atendida = ISNULL(cantidad_atendida, 0) - @cantidad, flg_gui = 0 WHERE id_empresa = @id_empresa AND ejercicio = @ejercicio AND num_oc = @num_oc AND id_articulo = @id_articulo');
                    }
                }
            }

            for (var oi = 0; oi < oldDetalle.length; oi++) {
                var od = oldDetalle[oi];
                if (!od.id_articulo || !od.cantidad) continue;
                var oldCant = parseFloat(od.cantidad) || 0;
                var oldCostoTot = parseFloat(od.total) || 0;

                var revArtSel = transaction.request();
                revArtSel.input('id_empresa', db.sql.Int, idEmpresa);
                revArtSel.input('id_articulo', db.sql.Int, od.id_articulo);
                var revArtData = await revArtSel.query(
                    'SELECT stock_actual, costo_promedio FROM articulos WITH (UPDLOCK, ROWLOCK) WHERE id_empresa = @id_empresa AND id_articulo = @id_articulo'
                );
                var revStock = (revArtData.recordset.length > 0 && revArtData.recordset[0].stock_actual != null) ? parseFloat(revArtData.recordset[0].stock_actual) : 0;
                var revCosto = (revArtData.recordset.length > 0 && revArtData.recordset[0].costo_promedio) ? parseFloat(revArtData.recordset[0].costo_promedio) : 0;

                var nuevoStockRev, nuevoCostoRev;
                if (tpoGui === 'I') {
                    nuevoStockRev = Math.max(0, revStock - oldCant);
                    nuevoCostoRev = nuevoStockRev > 0 ? ((revStock * revCosto) - oldCostoTot) / nuevoStockRev : 0;
                } else {
                    nuevoStockRev = revStock + oldCant;
                    nuevoCostoRev = nuevoStockRev > 0 ? ((revStock * revCosto) + oldCostoTot) / nuevoStockRev : revCosto;
                }

                var revArtUpd = transaction.request();
                revArtUpd.input('id_empresa', db.sql.Int, idEmpresa);
                revArtUpd.input('id_articulo', db.sql.Int, od.id_articulo);
                revArtUpd.input('stock', db.sql.Decimal(9,3), nuevoStockRev);
                revArtUpd.input('costo_promedio', db.sql.Decimal(10,4), nuevoCostoRev);
                await revArtUpd.query('UPDATE articulos SET stock_actual = @stock, costo_promedio = @costo_promedio WHERE id_empresa = @id_empresa AND id_articulo = @id_articulo');

                var delMov = transaction.request();
                delMov.input('id_documento', db.sql.Int, idGuia);
                delMov.input('id_articulo', db.sql.Int, od.id_articulo);
                delMov.input('id_empresa', db.sql.Int, idEmpresa);
                await delMov.query("DELETE FROM costo WHERE id_movimiento IN (SELECT id_movimiento FROM movimiento_almacen WHERE id_documento = @id_documento AND id_articulo = @id_articulo AND id_empresa = @id_empresa)");
                await delMov.query('DELETE FROM movimiento_almacen WHERE id_documento = @id_documento AND id_articulo = @id_articulo AND id_empresa = @id_empresa');
            }

            var cabReq = transaction.request();
            cabReq.input('id_guia', db.sql.Int, idGuia);
            cabReq.input('id_empresa', db.sql.Int, idEmpresa);
            cabReq.input('serie', db.sql.VarChar(4), body.serie || null);
            cabReq.input('numero', db.sql.VarChar(10), body.numero || null);
            cabReq.input('tpo_gui', db.sql.Char(1), tpoGui);
            cabReq.input('id_proveedor', db.sql.Int, body.id_proveedor || null);
            cabReq.input('id_cliente', db.sql.Int, body.id_cliente || null);
            cabReq.input('id_motivo', db.sql.Int, body.id_motivo || null);
            cabReq.input('num_oc', db.sql.VarChar(7), body.num_oc || null);
            cabReq.input('oc_ejercicio', db.sql.Int, body.oc_ejercicio || null);
            cabReq.input('num_orp', db.sql.VarChar(7), body.num_orp || null);
            cabReq.input('per_orp', db.sql.VarChar(7), body.per_orp || null);
            cabReq.input('fecha_guia', db.sql.Date, body.fecha_guia || new Date().toISOString().split('T')[0]);
            cabReq.input('moneda', db.sql.VarChar(2), body.moneda || 'MN');
            cabReq.input('tipo_cambio', db.sql.Decimal(7,3), parseFloat(body.tipo_cambio) || null);
            cabReq.input('subtotal', db.sql.Decimal(12,2), parseFloat(body.subtotal) || 0);
            cabReq.input('igv', db.sql.Decimal(10,2), parseFloat(body.igv) || 0);
            cabReq.input('total', db.sql.Decimal(12,2), parseFloat(body.total) || 0);
            cabReq.input('observaciones', db.sql.VarChar(500), body.observaciones || null);
            cabReq.input('id_transportista', db.sql.Int, body.id_transportista || null);
            cabReq.input('id_vehiculo', db.sql.Int, body.id_vehiculo || null);
            cabReq.input('id_chofer', db.sql.Int, body.id_chofer || null);
            cabReq.input('direccion_partida', db.sql.VarChar(200), body.direccion_partida || null);
            cabReq.input('direccion_llegada', db.sql.VarChar(200), body.direccion_llegada || null);
            cabReq.input('tip_gui', db.sql.Char(1), body.tip_gui || 'R');
            cabReq.input('est_alm', db.sql.Char(1), body.est_alm || null);
            cabReq.input('txt_gui', db.sql.VarChar(250), body.txt_gui || null);
            cabReq.input('usuario_mod', db.sql.Int, body.usuario_crea || null);
            await cabReq.query(`UPDATE guias_remision SET serie=@serie, numero=@numero, tpo_gui=@tpo_gui, id_proveedor=@id_proveedor, id_cliente=@id_cliente, id_motivo=@id_motivo, num_oc=@num_oc, oc_ejercicio=@oc_ejercicio, num_orp=@num_orp, per_orp=@per_orp, fecha_guia=@fecha_guia, moneda=@moneda, tipo_cambio=@tipo_cambio, subtotal=@subtotal, igv=@igv, total=@total, observaciones=@observaciones, id_transportista=@id_transportista, id_vehiculo=@id_vehiculo, id_chofer=@id_chofer, direccion_partida=@direccion_partida, direccion_llegada=@direccion_llegada, tip_gui=@tip_gui, est_alm=@est_alm, txt_gui=@txt_gui, usuario_mod=@usuario_mod, fecha_mod=GETDATE() WHERE id_guia=@id_guia AND id_empresa=@id_empresa`);

            var delDet = transaction.request();
            delDet.input('id_guia', db.sql.Int, idGuia);
            delDet.input('id_empresa', db.sql.Int, idEmpresa);
            await delDet.query('DELETE FROM guias_remision_det WHERE id_guia = @id_guia AND id_empresa = @id_empresa');

            var codGuia = existing[0].cod_guia;
            var fecha = body.fecha_guia || new Date().toISOString().split('T')[0];

            for (var i = 0; i < body.detalle.length; i++) {
                var d = body.detalle[i];
                if (!d.id_articulo || !d.cantidad || d.cantidad <= 0) continue;
                var detReq = transaction.request();
                detReq.input('id_guia', db.sql.Int, idGuia);
                detReq.input('id_empresa', db.sql.Int, idEmpresa);
                detReq.input('secuencia', db.sql.Int, i + 1);
                detReq.input('id_articulo', db.sql.Int, d.id_articulo);
                detReq.input('cantidad', db.sql.Decimal(9,3), parseFloat(d.cantidad) || 0);
                detReq.input('costo_unitario', db.sql.Decimal(10,4), parseFloat(d.costo_unitario) || 0);
                detReq.input('total', db.sql.Decimal(12,2), parseFloat(d.total) || 0);
                detReq.input('peso', db.sql.Decimal(9,3), parseFloat(d.peso) || 0);
                detReq.input('unidades', db.sql.Int, parseInt(d.unidades) || 0);
                detReq.input('num_oc', db.sql.VarChar(7), d.num_oc || null);
                detReq.input('ejercicio_oc', db.sql.Int, d.ejercicio || null);
                detReq.input('des_art', db.sql.VarChar(60), d.des_art || null);
                await detReq.query('INSERT INTO guias_remision_det (id_guia, id_empresa, secuencia, id_articulo, cantidad, costo_unitario, total, peso, unidades, num_oc, ejercicio, des_art) VALUES (@id_guia, @id_empresa, @secuencia, @id_articulo, @cantidad, @costo_unitario, @total, @peso, @unidades, @num_oc, @ejercicio_oc, @des_art)');

                var cantidad = parseFloat(d.cantidad) || 0;
                var costoUnitario = parseFloat(d.costo_unitario) || 0;
                var costoTotal = parseFloat(d.total) || (cantidad * costoUnitario);

                if (tpoGui === 'I') {
                    var detNumOc = d.num_oc || body.num_oc;
                    var detEj = d.ejercicio || body.oc_ejercicio;
                    if (detNumOc && detEj) {
                        var updOcReq = transaction.request();
                        updOcReq.input('id_empresa', db.sql.Int, idEmpresa);
                        updOcReq.input('num_oc', db.sql.VarChar(7), detNumOc);
                        updOcReq.input('ejercicio', db.sql.Int, detEj);
                        updOcReq.input('id_articulo', db.sql.Int, d.id_articulo);
                        updOcReq.input('cantidad', db.sql.Decimal(9,3), cantidad);
                        await updOcReq.query('UPDATE det_orden_compra SET cantidad_atendida = ISNULL(cantidad_atendida, 0) + @cantidad, flg_gui = 1 WHERE id_empresa = @id_empresa AND ejercicio = @ejercicio AND num_oc = @num_oc AND id_articulo = @id_articulo');
                    }
                }

                var artSel2 = transaction.request();
                artSel2.input('id_empresa', db.sql.Int, idEmpresa);
                artSel2.input('id_articulo', db.sql.Int, d.id_articulo);
                var artData2 = await artSel2.query(
                    'SELECT stock_actual, costo_promedio FROM articulos WITH (UPDLOCK, ROWLOCK) WHERE id_empresa = @id_empresa AND id_articulo = @id_articulo'
                );
                var stockActual2 = (artData2.recordset.length > 0 && artData2.recordset[0].stock_actual != null) ? parseFloat(artData2.recordset[0].stock_actual) : 0;
                var costoActual2 = (artData2.recordset.length > 0 && artData2.recordset[0].costo_promedio) ? parseFloat(artData2.recordset[0].costo_promedio) : 0;

                var nuevoStock2, nuevoCostoPromedio2;
                if (tpoGui === 'I') {
                    nuevoStock2 = stockActual2 + cantidad;
                    nuevoCostoPromedio2 = nuevoStock2 > 0 ? ((stockActual2 * costoActual2) + costoTotal) / nuevoStock2 : costoUnitario;
                } else {
                    nuevoStock2 = Math.max(0, stockActual2 - cantidad);
                    nuevoCostoPromedio2 = nuevoStock2 > 0 ? ((stockActual2 * costoActual2) - costoTotal) / nuevoStock2 : 0;
                }

                var tipoMov2 = tpoGui === 'I' ? 'E' : 'S';

                var movReq2 = transaction.request();
                movReq2.input('id_empresa', db.sql.Int, idEmpresa);
                movReq2.input('id_articulo', db.sql.Int, d.id_articulo);
                movReq2.input('tipo', db.sql.VarChar(1), tipoMov2);
                movReq2.input('documento', db.sql.VarChar(30), codGuia);
                movReq2.input('id_documento', db.sql.Int, idGuia);
                movReq2.input('cantidad', db.sql.Decimal(9,3), cantidad);
                movReq2.input('costo_unitario', db.sql.Decimal(10,4), costoUnitario);
                movReq2.input('costo_total', db.sql.Decimal(12,2), costoTotal);
                movReq2.input('fecha_movimiento', db.sql.Date, fecha);
                movReq2.input('usuario_crea', db.sql.Int, body.usuario_crea || null);
                var movResult2 = await movReq2.query(
                    'INSERT INTO movimiento_almacen (id_empresa, id_articulo, tipo, documento, id_documento, cantidad, costo_unitario, costo_total, fecha_movimiento, usuario_crea) OUTPUT INSERTED.id_movimiento VALUES (@id_empresa, @id_articulo, @tipo, @documento, @id_documento, @cantidad, @costo_unitario, @costo_total, @fecha_movimiento, @usuario_crea)'
                );
                var idMovimiento2 = movResult2.recordset[0].id_movimiento;

                var costReq2 = transaction.request();
                costReq2.input('id_empresa', db.sql.Int, idEmpresa);
                costReq2.input('id_articulo', db.sql.Int, d.id_articulo);
                costReq2.input('tipo_costo', db.sql.VarChar(1), 'P');
                costReq2.input('costo_unitario', db.sql.Decimal(10,4), nuevoCostoPromedio2);
                costReq2.input('cantidad', db.sql.Decimal(9,3), cantidad);
                costReq2.input('costo_total', db.sql.Decimal(12,2), costoTotal);
                costReq2.input('fecha_movimiento', db.sql.Date, fecha);
                costReq2.input('id_movimiento', db.sql.Int, idMovimiento2);
                costReq2.input('usuario_crea', db.sql.Int, body.usuario_crea || null);
                await costReq2.query(
                    'INSERT INTO costo (id_empresa, id_articulo, tipo_costo, costo_unitario, cantidad, costo_total, fecha_movimiento, id_movimiento, usuario_crea) VALUES (@id_empresa, @id_articulo, @tipo_costo, @costo_unitario, @cantidad, @costo_total, @fecha_movimiento, @id_movimiento, @usuario_crea)'
                );

                var artUpd2 = transaction.request();
                artUpd2.input('id_empresa', db.sql.Int, idEmpresa);
                artUpd2.input('id_articulo', db.sql.Int, d.id_articulo);
                artUpd2.input('stock', db.sql.Decimal(9,3), nuevoStock2);
                artUpd2.input('costo_promedio', db.sql.Decimal(10,4), nuevoCostoPromedio2);
                await artUpd2.query('UPDATE articulos SET stock_actual = @stock, costo_promedio = @costo_promedio WHERE id_empresa = @id_empresa AND id_articulo = @id_articulo');
            }

            if (tpoGui === 'I') {
                var newOcsMap = {};
                for (var n = 0; n < body.detalle.length; n++) {
                    var nd = body.detalle[n];
                    var ndNumOc = nd.num_oc || body.num_oc;
                    var ndEj = nd.ejercicio || body.oc_ejercicio;
                    if (ndNumOc && ndEj) {
                        newOcsMap[ndEj + '|' + ndNumOc] = { num_oc: ndNumOc, ejercicio: ndEj };
                    }
                }
                var newOcKeys = Object.keys(newOcsMap);
                for (var nk = 0; nk < newOcKeys.length; nk++) {
                    var nOc = newOcsMap[newOcKeys[nk]];
                    var pendReq2 = transaction.request();
                    pendReq2.input('id_empresa', db.sql.Int, idEmpresa);
                    pendReq2.input('num_oc', db.sql.VarChar(7), nOc.num_oc);
                    pendReq2.input('ejercicio', db.sql.Int, nOc.ejercicio);
                    var pendientes2 = await pendReq2.query(
                        'SELECT COUNT(*) AS pend FROM det_orden_compra WHERE id_empresa = @id_empresa AND ejercicio = @ejercicio AND num_oc = @num_oc AND (cantidad - ISNULL(cantidad_atendida, 0)) > 0'
                    );
                    if (parseInt(pendientes2.recordset[0].pend) === 0) {
                        var ocUpdReq2 = transaction.request();
                        ocUpdReq2.input('id_empresa', db.sql.Int, idEmpresa);
                        ocUpdReq2.input('num_oc', db.sql.VarChar(7), nOc.num_oc);
                        ocUpdReq2.input('ejercicio', db.sql.Int, nOc.ejercicio);
                        ocUpdReq2.input('estado', db.sql.VarChar(1), 'C');
                        await ocUpdReq2.query('UPDATE cab_orden_compra SET estado = @estado WHERE id_empresa = @id_empresa AND ejercicio = @ejercicio AND num_oc = @num_oc');
                    }
                }
            }

            await transaction.commit();
            res.json({ ok: true, mensaje: 'Guía actualizada' });
        } catch (error) {
            await transaction.rollback();
            throw error;
        }
    } catch (error) {
        console.error('[GUIAS_REMISION] Error actualizar:', error.message);
        res.status(500).json({ ok: false, mensaje: 'Error al actualizar guía: ' + error.message });
    }
});

router.delete('/guias-remision/:id_guia', async function(req, res) {
    try {
        if (!req.query.id_empresa) return res.status(400).json({ ok: false, mensaje: 'Empresa requerida' });
        var idEmpresa = parseInt(req.query.id_empresa);

        var idGuia = parseInt(req.params.id_guia);
        var existing = await db.query('SELECT cod_guia, estado, tpo_gui FROM guias_remision WHERE id_guia = @id_guia AND id_empresa = @id_empresa', {
            id_guia: { type: db.sql.Int, value: idGuia },
            id_empresa: { type: db.sql.Int, value: idEmpresa }
        });
        if (existing.length === 0) return res.status(404).json({ ok: false, mensaje: 'Guía no encontrada' });
        if (existing[0].estado !== 'P') return res.status(400).json({ ok: false, mensaje: 'Solo se puede eliminar guías pendientes' });

        var tpoGui = existing[0].tpo_gui || 'I';

        var pool = await db.getPool();
        var transaction = pool.transaction();
        await transaction.begin();
        try {
            var oldDetReq = transaction.request();
            oldDetReq.input('id_guia', db.sql.Int, idGuia);
            oldDetReq.input('id_empresa', db.sql.Int, idEmpresa);
            var oldDetResult = await oldDetReq.query('SELECT id_articulo, cantidad, total, num_oc, ejercicio FROM guias_remision_det WHERE id_guia = @id_guia AND id_empresa = @id_empresa');
            var oldDetalle = oldDetResult.recordset || [];

            for (var oi = 0; oi < oldDetalle.length; oi++) {
                var od = oldDetalle[oi];
                if (!od.id_articulo || !od.cantidad) continue;
                var oldCant = parseFloat(od.cantidad) || 0;
                var oldCostoTot = parseFloat(od.total) || 0;

                if (tpoGui === 'I' && od.num_oc && od.ejercicio) {
                    var revOcReq = transaction.request();
                    revOcReq.input('id_empresa', db.sql.Int, idEmpresa);
                    revOcReq.input('num_oc', db.sql.VarChar(7), od.num_oc);
                    revOcReq.input('ejercicio', db.sql.Int, od.ejercicio);
                    revOcReq.input('id_articulo', db.sql.Int, od.id_articulo);
                    revOcReq.input('cantidad', db.sql.Decimal(9,3), oldCant);
                    await revOcReq.query('UPDATE det_orden_compra SET cantidad_atendida = ISNULL(cantidad_atendida, 0) - @cantidad, flg_gui = 0 WHERE id_empresa = @id_empresa AND ejercicio = @ejercicio AND num_oc = @num_oc AND id_articulo = @id_articulo');
                }

                var revArtSel = transaction.request();
                revArtSel.input('id_empresa', db.sql.Int, idEmpresa);
                revArtSel.input('id_articulo', db.sql.Int, od.id_articulo);
                var revArtData = await revArtSel.query(
                    'SELECT stock_actual, costo_promedio FROM articulos WITH (UPDLOCK, ROWLOCK) WHERE id_empresa = @id_empresa AND id_articulo = @id_articulo'
                );
                var revStock = (revArtData.recordset.length > 0 && revArtData.recordset[0].stock_actual != null) ? parseFloat(revArtData.recordset[0].stock_actual) : 0;
                var revCosto = (revArtData.recordset.length > 0 && revArtData.recordset[0].costo_promedio) ? parseFloat(revArtData.recordset[0].costo_promedio) : 0;

                var nuevoStockRev, nuevoCostoRev;
                if (tpoGui === 'I') {
                    nuevoStockRev = Math.max(0, revStock - oldCant);
                    nuevoCostoRev = nuevoStockRev > 0 ? ((revStock * revCosto) - oldCostoTot) / nuevoStockRev : 0;
                } else {
                    nuevoStockRev = revStock + oldCant;
                    nuevoCostoRev = nuevoStockRev > 0 ? ((revStock * revCosto) + oldCostoTot) / nuevoStockRev : revCosto;
                }

                var revArtUpd = transaction.request();
                revArtUpd.input('id_empresa', db.sql.Int, idEmpresa);
                revArtUpd.input('id_articulo', db.sql.Int, od.id_articulo);
                revArtUpd.input('stock', db.sql.Decimal(9,3), nuevoStockRev);
                revArtUpd.input('costo_promedio', db.sql.Decimal(10,4), nuevoCostoRev);
                await revArtUpd.query('UPDATE articulos SET stock_actual = @stock, costo_promedio = @costo_promedio WHERE id_empresa = @id_empresa AND id_articulo = @id_articulo');

                var delMov = transaction.request();
                delMov.input('id_documento', db.sql.Int, idGuia);
                delMov.input('id_articulo', db.sql.Int, od.id_articulo);
                delMov.input('id_empresa', db.sql.Int, idEmpresa);
                await delMov.query("DELETE FROM costo WHERE id_movimiento IN (SELECT id_movimiento FROM movimiento_almacen WHERE id_documento = @id_documento AND id_articulo = @id_articulo AND id_empresa = @id_empresa)");
                await delMov.query('DELETE FROM movimiento_almacen WHERE id_documento = @id_documento AND id_articulo = @id_articulo AND id_empresa = @id_empresa');
            }

            var delDet = transaction.request();
            delDet.input('id_guia', db.sql.Int, idGuia);
            delDet.input('id_empresa', db.sql.Int, idEmpresa);
            await delDet.query('DELETE FROM guias_remision_det WHERE id_guia = @id_guia AND id_empresa = @id_empresa');

            var delCab = transaction.request();
            delCab.input('id_guia', db.sql.Int, idGuia);
            delCab.input('id_empresa', db.sql.Int, idEmpresa);
            await delCab.query('DELETE FROM guias_remision WHERE id_guia = @id_guia AND id_empresa = @id_empresa');

            await transaction.commit();
            res.json({ ok: true, mensaje: 'Guía eliminada' });
        } catch (error) {
            await transaction.rollback();
            throw error;
        }
    } catch (error) {
        console.error('[GUIAS_REMISION] Error eliminar:', error.message);
        res.status(500).json({ ok: false, mensaje: 'Error al eliminar guía' });
    }
});

var TRANSICIONES = {
    P: { A: 'Aprobado', N: 'Anulado' },
    A: { N: 'Anulado' },
    N: {}
};

router.patch('/guias-remision/:id_guia/estado', async function(req, res) {
    try {
        if (!req.query.id_empresa) return res.status(400).json({ ok: false, mensaje: 'Empresa requerida' });
        var idEmpresa = parseInt(req.query.id_empresa);

        var idGuia = parseInt(req.params.id_guia);
        var nuevoEstado = req.body.estado;
        if (!nuevoEstado || !nuevoEstado.match(/^[PAN]$/)) {
            return res.status(400).json({ ok: false, mensaje: 'Estado inválido' });
        }

        var cab = await db.query('SELECT estado, cod_guia, tpo_gui FROM guias_remision WHERE id_guia = @id_guia AND id_empresa = @id_empresa', {
            id_guia: { type: db.sql.Int, value: idGuia },
            id_empresa: { type: db.sql.Int, value: idEmpresa }
        });
        if (cab.length === 0) return res.status(404).json({ ok: false, mensaje: 'Guía no encontrada' });

        var estadoActual = cab[0].estado;
        var tpoGui = cab[0].tpo_gui || 'I';
        var destino = TRANSICIONES[estadoActual];
        if (!destino || !destino[nuevoEstado]) {
            return res.status(400).json({ ok: false, mensaje: 'No se puede cambiar de ' + mapearEstado(estadoActual) + ' a ' + mapearEstado(nuevoEstado) });
        }

        if (nuevoEstado === 'N') {
            var pool = await db.getPool();
            var transaction = pool.transaction();
            await transaction.begin();
            try {
                var oldDetReq = transaction.request();
                oldDetReq.input('id_guia', db.sql.Int, idGuia);
                oldDetReq.input('id_empresa', db.sql.Int, idEmpresa);
                var oldDetResult = await oldDetReq.query('SELECT id_articulo, cantidad, total, num_oc, ejercicio FROM guias_remision_det WHERE id_guia = @id_guia AND id_empresa = @id_empresa');
                var oldDetalle = oldDetResult.recordset || [];

                for (var oi = 0; oi < oldDetalle.length; oi++) {
                    var od = oldDetalle[oi];
                    if (!od.id_articulo || !od.cantidad) continue;
                    var oldCant = parseFloat(od.cantidad) || 0;
                    var oldCostoTot = parseFloat(od.total) || 0;

                    if (tpoGui === 'I' && od.num_oc && od.ejercicio) {
                        var revOcReq = transaction.request();
                        revOcReq.input('id_empresa', db.sql.Int, idEmpresa);
                        revOcReq.input('num_oc', db.sql.VarChar(7), od.num_oc);
                        revOcReq.input('ejercicio', db.sql.Int, od.ejercicio);
                        revOcReq.input('id_articulo', db.sql.Int, od.id_articulo);
                        revOcReq.input('cantidad', db.sql.Decimal(9,3), oldCant);
                        await revOcReq.query('UPDATE det_orden_compra SET cantidad_atendida = ISNULL(cantidad_atendida, 0) - @cantidad, flg_gui = 0 WHERE id_empresa = @id_empresa AND ejercicio = @ejercicio AND num_oc = @num_oc AND id_articulo = @id_articulo');
                    }

                    var revArtSel = transaction.request();
                    revArtSel.input('id_empresa', db.sql.Int, idEmpresa);
                    revArtSel.input('id_articulo', db.sql.Int, od.id_articulo);
                    var revArtData = await revArtSel.query(
                        'SELECT stock_actual, costo_promedio FROM articulos WITH (UPDLOCK, ROWLOCK) WHERE id_empresa = @id_empresa AND id_articulo = @id_articulo'
                    );
                var revStock = (revArtData.recordset.length > 0 && revArtData.recordset[0].stock_actual != null) ? parseFloat(revArtData.recordset[0].stock_actual) : 0;
                var revCosto = (revArtData.recordset.length > 0 && revArtData.recordset[0].costo_promedio) ? parseFloat(revArtData.recordset[0].costo_promedio) : 0;

                    var nuevoStockRev, nuevoCostoRev;
                    if (tpoGui === 'I') {
                        nuevoStockRev = Math.max(0, revStock - oldCant);
                        nuevoCostoRev = nuevoStockRev > 0 ? ((revStock * revCosto) - oldCostoTot) / nuevoStockRev : 0;
                    } else {
                        nuevoStockRev = revStock + oldCant;
                        nuevoCostoRev = nuevoStockRev > 0 ? ((revStock * revCosto) + oldCostoTot) / nuevoStockRev : revCosto;
                    }

                    var revArtUpd = transaction.request();
                    revArtUpd.input('id_empresa', db.sql.Int, idEmpresa);
                    revArtUpd.input('id_articulo', db.sql.Int, od.id_articulo);
                    revArtUpd.input('stock', db.sql.Decimal(9,3), nuevoStockRev);
                    revArtUpd.input('costo_promedio', db.sql.Decimal(10,4), nuevoCostoRev);
                    await revArtUpd.query('UPDATE articulos SET stock_actual = @stock, costo_promedio = @costo_promedio WHERE id_empresa = @id_empresa AND id_articulo = @id_articulo');

                    var delMov = transaction.request();
                    delMov.input('id_documento', db.sql.Int, idGuia);
                    delMov.input('id_articulo', db.sql.Int, od.id_articulo);
                    delMov.input('id_empresa', db.sql.Int, idEmpresa);
                    await delMov.query("DELETE FROM costo WHERE id_movimiento IN (SELECT id_movimiento FROM movimiento_almacen WHERE id_documento = @id_documento AND id_articulo = @id_articulo AND id_empresa = @id_empresa)");
                    await delMov.query('DELETE FROM movimiento_almacen WHERE id_documento = @id_documento AND id_articulo = @id_articulo AND id_empresa = @id_empresa');
                }

                await transaction.commit();
            } catch (error) {
                await transaction.rollback();
                throw error;
            }
        }

        await db.execute('UPDATE guias_remision SET estado = @estado WHERE id_guia = @id_guia AND id_empresa = @id_empresa', {
            id_guia: { type: db.sql.Int, value: idGuia },
            id_empresa: { type: db.sql.Int, value: idEmpresa },
            estado: { type: db.sql.VarChar(1), value: nuevoEstado }
        });

        res.json({ ok: true, mensaje: 'Guía ' + cab[0].cod_guia + ' ' + destino[nuevoEstado].toLowerCase() });
    } catch (error) {
        console.error('[GUIAS_REMISION] Error cambiar estado:', error.message);
        res.status(500).json({ ok: false, mensaje: 'Error al cambiar estado' });
    }
});

module.exports = router;
