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', C: 'Cerrado', N: 'Anulado' };
    return map[estado] || estado;
}

var isWeb = process.env.MODO_ERP === 'web';
function aggSql(expr, separator) {
    if (isWeb) return 'GROUP_CONCAT(' + expr + ' SEPARATOR \'' + separator + '\')';
    return 'STRING_AGG(' + expr + ', \'' + separator + '\')';
}

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

        var buscar = req.query.buscar || '';
        var page = req.query.page !== undefined ? parseInt(req.query.page) : 1;
        var limit = req.query.limit !== undefined ? parseInt(req.query.limit) : 50;
        var sinPaginacion = limit <= 0;
        var sort = req.query.sort || 'cp.fecha_pedido';
        var order = req.query.order === 'DESC' ? 'DESC' : 'ASC';
        var ejercicio = parseInt(req.query.ejercicio) || new Date().getFullYear();

        var where = 'WHERE cp.id_empresa = @id_empresa AND cp.ejercicio = @ejercicio';
        var params = {
            id_empresa: { type: db.sql.Int, value: idEmpresa },
            ejercicio: { type: db.sql.Int, value: ejercicio }
        };

        if (req.query.id_sucursal) {
            where += ' AND cp.id_sucursal = @id_sucursal';
            params.id_sucursal = { type: db.sql.Int, value: parseInt(req.query.id_sucursal) };
        }

        if (buscar) {
            where += ' AND (cp.num_ped LIKE @buscar OR p.razon_social 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 cp.fecha_pedido >= @fecha_desde';
            params.fecha_desde = { type: db.sql.Date, value: fechaDesde };
        }
        if (fechaHasta) {
            where += ' AND cp.fecha_pedido <= @fecha_hasta';
            params.fecha_hasta = { type: db.sql.Date, value: fechaHasta };
        }

        var filtros = req.query.filtros ? JSON.parse(req.query.filtros) : null;
        var pedFMap = { num_ped: 'cp.num_ped', proveedor_razon_social: 'p.razon_social', estado: 'cp.estado', tipo: 'cp.tipo' };
        if (filtros) {
            var fIdx = 0;
            for (var col in filtros) {
                var dbCol = pedFMap[col] || col;
                var vals = Array.isArray(filtros[col]) ? filtros[col] : String(filtros[col]).split('\x01');
                var 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 pedJoins = 'LEFT JOIN proveedores p ON cp.id_empresa = p.id_empresa AND cp.id_proveedor = p.id_proveedor';
        if (await dh.tryDistinct(req, res, { db, table: 'cab_pedido', alias: 'cp', joins: pedJoins, where, params, colMap: pedFMap })) return;

        var joins = ' FROM cab_pedido cp ' + pedJoins;
        var sqlCount = 'SELECT COUNT(*) as total' + joins + ' ' + 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 = { num_ped: 'cp.num_ped', fecha_pedido: 'cp.fecha_pedido', proveedor_razon_social: 'p.razon_social', estado: 'cp.estado', tipo: 'cp.tipo', num_ocs: 'num_ocs' };
        var sortCol = allowedSort[sort] || 'cp.fecha_pedido';

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

        var aggSubquery = '(SELECT ' + aggSql('CONCAT(co.ejercicio, \'-\', co.num_oc)', ', ') + ' FROM cab_orden_compra co WHERE co.num_ped = cp.num_ped AND co.ejercicio = cp.ejercicio AND co.id_empresa = cp.id_empresa)';
        var sql = 'SELECT cp.ejercicio, cp.id_sucursal, cp.num_ped, cp.fecha_pedido, cp.tipo, cp.estado, cp.observaciones, ' + aggSubquery + ' AS num_ocs, p.razon_social AS proveedor_razon_social' + joins + ' ' + where +
            ' ORDER BY ' + sortCol + ' ' + order +
            ' OFFSET ' + offset + ' ROWS FETCH NEXT ' + fetchLimit + ' ROWS ONLY';

        var data = await db.query(sql, params);
        var dataMap = data.map(function(r) {
            return { ejercicio: r.ejercicio, num_ped: r.num_ped, fecha_pedido: r.fecha_pedido, tipo: r.tipo, estado: r.estado, estado_texto: mapearEstado(r.estado), observaciones: r.observaciones, num_ocs: r.num_ocs, proveedor_razon_social: r.proveedor_razon_social };
        });

        res.json({ ok: true, data: dataMap, total: total, totalPaginas: totalPaginas });
    } catch (error) {
        console.error('[PEDIDOS] Error listar:', error.message);
        res.status(500).json({ ok: false, mensaje: 'Error al listar pedidos' });
    }
});

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

        var ejercicio = parseInt(req.query.ejercicio) || new Date().getFullYear();
        var cab = await db.query(
            'SELECT cp.*, p.razon_social AS proveedor_razon_social FROM cab_pedido cp LEFT JOIN proveedores p ON cp.id_empresa = p.id_empresa AND cp.id_proveedor = p.id_proveedor WHERE cp.id_empresa = @id_empresa AND cp.ejercicio = @ejercicio AND cp.num_ped = @num_ped',
            {
                id_empresa: { type: db.sql.Int, value: idEmpresa },
                ejercicio: { type: db.sql.Int, value: ejercicio },
                num_ped: { type: db.sql.VarChar(7), value: req.params.num_ped }
            }
        );
        if (cab.length === 0) return res.status(404).json({ ok: false, mensaje: 'Pedido no encontrado' });

        var det = await db.query(
            'SELECT d.ejercicio, d.num_ped, d.id_articulo, d.cantidad, a.cod_articulo, a.nombre FROM det_pedido d INNER JOIN articulos a ON d.id_empresa = a.id_empresa AND d.id_articulo = a.id_articulo WHERE d.id_empresa = @id_empresa AND d.ejercicio = @ejercicio AND d.num_ped = @num_ped',
            {
                id_empresa: { type: db.sql.Int, value: idEmpresa },
                ejercicio: { type: db.sql.Int, value: ejercicio },
                num_ped: { type: db.sql.VarChar(7), value: req.params.num_ped }
            }
        );

        var row = cab[0];
        row.detalle = det;
        res.json({ ok: true, data: row });
    } catch (error) {
        console.error('[PEDIDOS] Error obtener:', error.message);
        res.status(500).json({ ok: false, mensaje: 'Error al obtener pedido' });
    }
});

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

        var numPed = req.params.num_ped;
        var ejercicio = parseInt(req.query.ejercicio) || new Date().getFullYear();
        var cab = await db.query('SELECT estado FROM cab_pedido WHERE id_empresa = @id_empresa AND ejercicio = @ejercicio AND num_ped = @num_ped', {
            id_empresa: { type: db.sql.Int, value: idEmpresa },
            ejercicio: { type: db.sql.Int, value: ejercicio },
            num_ped: { type: db.sql.VarChar(7), value: numPed }
        });
        if (cab.length === 0) return res.status(404).json({ ok: false, mensaje: 'Pedido no encontrado' });

        var items = await db.query(`
            SELECT p.id_articulo, a.cod_articulo, a.nombre, u.cod_sunat AS cod_unidad,
                   p.cantidad AS ped_cantidad,
                   ISNULL(SUM(d.cantidad), 0) AS oc_cantidad,
                   p.cantidad - ISNULL(SUM(d.cantidad), 0) AS pendiente
            FROM det_pedido p
            INNER JOIN articulos a ON p.id_empresa = a.id_empresa AND p.id_articulo = a.id_articulo
            LEFT JOIN unidad_medida u ON a.id_empresa = u.id_empresa AND a.id_unidad = u.id_unidad
            LEFT JOIN cab_orden_compra c ON c.num_ped = p.num_ped AND c.ejercicio = p.ejercicio AND c.id_empresa = p.id_empresa
            LEFT JOIN det_orden_compra d ON d.num_oc = c.num_oc AND d.ejercicio = c.ejercicio AND d.id_articulo = p.id_articulo AND d.id_empresa = c.id_empresa
            WHERE p.id_empresa = @id_empresa AND p.num_ped = @num_ped AND p.ejercicio = @ejercicio
            GROUP BY p.id_articulo, p.cantidad, a.cod_articulo, a.nombre, u.cod_sunat
            HAVING p.cantidad - ISNULL(SUM(d.cantidad), 0) > 0
        `, {
            id_empresa: { type: db.sql.Int, value: idEmpresa },
            num_ped: { type: db.sql.VarChar(7), value: numPed },
            ejercicio: { type: db.sql.Int, value: ejercicio }
        });

        res.json({ ok: true, data: items, estado: cab[0].estado });
    } catch (error) {
        console.error('[PEDIDOS] Error items-restantes:', error.message);
        res.status(500).json({ ok: false, mensaje: 'Error al obtener items restantes' });
    }
});

router.post('/pedidos', 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 ejercicio = body.ejercicio || new Date().getFullYear();

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

        try {
            var nextReq = transaction.request();
            nextReq.input('id_empresa', db.sql.Int, body.id_empresa);
            nextReq.input('ejercicio', db.sql.Int, ejercicio);
            var nextResult = await nextReq.query(
                "SELECT ISNULL(MAX(CAST(num_ped AS INT)), 0) + 1 AS next FROM cab_pedido WITH (UPDLOCK, ROWLOCK) WHERE id_empresa = @id_empresa AND ejercicio = @ejercicio"
            );
            var codigo = String(nextResult.recordset[0].next).padStart(7, '0');

            var cabReq = transaction.request();
            cabReq.input('num_ped', db.sql.VarChar(7), codigo);
            cabReq.input('ejercicio', db.sql.Int, ejercicio);
            cabReq.input('id_empresa', db.sql.Int, body.id_empresa);
            cabReq.input('id_sucursal', db.sql.Int, body.id_sucursal || null);
            cabReq.input('id_proveedor', db.sql.Int, body.id_proveedor || null);
            cabReq.input('fecha_pedido', db.sql.Date, body.fecha_pedido || new Date().toISOString().split('T')[0]);
            cabReq.input('tipo', db.sql.VarChar(1), body.tipo || 'M');
            cabReq.input('estado', db.sql.VarChar(1), body.estado || 'P');
            cabReq.input('observaciones', db.sql.VarChar(200), body.observaciones || null);
            cabReq.input('usuario_crea', db.sql.Int, body.usuario_crea || null);
            await cabReq.query('INSERT INTO cab_pedido (num_ped, ejercicio, id_empresa, id_sucursal, id_proveedor, fecha_pedido, tipo, estado, observaciones, usuario_crea) VALUES (@num_ped, @ejercicio, @id_empresa, @id_sucursal, @id_proveedor, @fecha_pedido, @tipo, @estado, @observaciones, @usuario_crea)');

            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('num_ped', db.sql.VarChar(7), codigo);
                detReq.input('ejercicio', db.sql.Int, ejercicio);
                detReq.input('id_empresa', db.sql.Int, body.id_empresa);
                detReq.input('id_sucursal', db.sql.Int, body.id_sucursal || null);
                detReq.input('id_articulo', db.sql.Int, d.id_articulo);
                detReq.input('cantidad', db.sql.Decimal(8, 2), d.cantidad);
                await detReq.query('INSERT INTO det_pedido (num_ped, ejercicio, id_empresa, id_sucursal, id_articulo, cantidad) VALUES (@num_ped, @ejercicio, @id_empresa, @id_sucursal, @id_articulo, @cantidad)');
            }

            await transaction.commit();
            res.json({ ok: true, mensaje: 'Pedido ' + codigo + ' (' + ejercicio + ') creado', data: { num_ped: codigo, ejercicio: ejercicio } });
        } catch (error) {
            await transaction.rollback();
            throw error;
        }
    } catch (error) {
        console.error('[PEDIDOS] Error crear:', error.message);
        res.status(500).json({ ok: false, mensaje: 'Error al crear pedido: ' + error.message });
    }
});

router.put('/pedidos/:num_ped', async function(req, res) {
    try {
        var body = req.body;
        var numPed = req.params.num_ped;
        var idEmpresa = parseInt(req.query.id_empresa) || body.id_empresa;
        if (!idEmpresa) return res.status(400).json({ ok: false, mensaje: 'Empresa requerida' });

        var ejercicio = parseInt(req.query.ejercicio) || body.ejercicio || new Date().getFullYear();
        if (!body.detalle || body.detalle.length === 0) return res.status(400).json({ ok: false, mensaje: 'Debe agregar al menos un artículo' });

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

        try {
            var cabReq = transaction.request();
            cabReq.input('num_ped', db.sql.VarChar(7), numPed);
            cabReq.input('ejercicio', db.sql.Int, ejercicio);
            cabReq.input('id_empresa', db.sql.Int, idEmpresa);
            cabReq.input('id_proveedor', db.sql.Int, body.id_proveedor || null);
            cabReq.input('fecha_pedido', db.sql.Date, body.fecha_pedido || new Date());
            cabReq.input('tipo', db.sql.VarChar(1), body.tipo || 'M');
            cabReq.input('estado', db.sql.VarChar(1), body.estado || 'P');
            cabReq.input('observaciones', db.sql.VarChar(200), body.observaciones || null);
            await cabReq.query('UPDATE cab_pedido SET id_proveedor=@id_proveedor, fecha_pedido=@fecha_pedido, tipo=@tipo, estado=@estado, observaciones=@observaciones WHERE id_empresa=@id_empresa AND ejercicio=@ejercicio AND num_ped=@num_ped');

            var delReq = transaction.request();
            delReq.input('num_ped', db.sql.VarChar(7), numPed);
            delReq.input('ejercicio', db.sql.Int, ejercicio);
            delReq.input('id_empresa', db.sql.Int, idEmpresa);
            await delReq.query('DELETE FROM det_pedido WHERE id_empresa = @id_empresa AND ejercicio = @ejercicio AND num_ped = @num_ped');

            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('num_ped', db.sql.VarChar(7), numPed);
                detReq.input('ejercicio', db.sql.Int, ejercicio);
                detReq.input('id_empresa', db.sql.Int, idEmpresa);
                detReq.input('id_articulo', db.sql.Int, d.id_articulo);
                detReq.input('cantidad', db.sql.Decimal(8, 2), d.cantidad);
                await detReq.query('INSERT INTO det_pedido (num_ped, ejercicio, id_empresa, id_articulo, cantidad) VALUES (@num_ped, @ejercicio, @id_empresa, @id_articulo, @cantidad)');
            }

            await transaction.commit();
            res.json({ ok: true, mensaje: 'Pedido ' + numPed + ' (' + ejercicio + ') actualizado' });
        } catch (error) {
            await transaction.rollback();
            throw error;
        }
    } catch (error) {
        console.error('[PEDIDOS] Error actualizar:', error.message);
        res.status(500).json({ ok: false, mensaje: 'Error al actualizar pedido: ' + error.message });
    }
});

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

        var ejercicio = parseInt(req.query.ejercicio) || new Date().getFullYear();
        var pool = await db.getPool();
        var transaction = pool.transaction();
        await transaction.begin();
        try {
            var delDet = transaction.request();
            delDet.input('num_ped', db.sql.VarChar(7), req.params.num_ped);
            delDet.input('ejercicio', db.sql.Int, ejercicio);
            delDet.input('id_empresa', db.sql.Int, idEmpresa);
            await delDet.query('DELETE FROM det_pedido WHERE id_empresa = @id_empresa AND ejercicio = @ejercicio AND num_ped = @num_ped');

            var delCab = transaction.request();
            delCab.input('num_ped', db.sql.VarChar(7), req.params.num_ped);
            delCab.input('ejercicio', db.sql.Int, ejercicio);
            delCab.input('id_empresa', db.sql.Int, idEmpresa);
            await delCab.query('DELETE FROM cab_pedido WHERE id_empresa = @id_empresa AND ejercicio = @ejercicio AND num_ped = @num_ped');

            await transaction.commit();
            res.json({ ok: true, mensaje: 'Pedido eliminado' });
        } catch (error) {
            await transaction.rollback();
            throw error;
        }
    } catch (error) {
        console.error('[PEDIDOS] Error eliminar:', error.message);
        res.status(500).json({ ok: false, mensaje: 'Error al eliminar pedido' });
    }
});

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

        var result = await db.query(
            'SELECT num_oc FROM cab_orden_compra WHERE id_empresa = @id_empresa AND num_ped = @num_ped ORDER BY num_oc',
            {
                id_empresa: { type: db.sql.Int, value: idEmpresa },
                num_ped: { type: db.sql.VarChar(7), value: req.params.num_ped }
            }
        );
        res.json({ ok: true, data: result.map(function(r) { return r.num_oc; }) });
    } catch (error) {
        console.error('[PEDIDOS] Error OCs:', error.message);
        res.status(500).json({ ok: false, mensaje: 'Error al obtener OCs del pedido' });
    }
});

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

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

        var numPed = req.params.num_ped;
        var nuevoEstado = req.body.estado;
        var ejercicio = parseInt(req.query.ejercicio) || new Date().getFullYear();
        if (!nuevoEstado || !nuevoEstado.match(/^[PACN]$/)) {
            return res.status(400).json({ ok: false, mensaje: 'Estado invalido' });
        }

        var cab = await db.query('SELECT estado FROM cab_pedido WHERE id_empresa = @id_empresa AND ejercicio = @ejercicio AND num_ped = @num_ped', {
            id_empresa: { type: db.sql.Int, value: idEmpresa },
            ejercicio: { type: db.sql.Int, value: ejercicio },
            num_ped: { type: db.sql.VarChar(7), value: numPed }
        });
        if (cab.length === 0) return res.status(404).json({ ok: false, mensaje: 'Pedido no encontrado' });

        var estadoActual = cab[0].estado;
        var destino = TRANSICIONES[estadoActual];
        if (!destino || !destino[nuevoEstado]) {
            return res.status(400).json({ ok: false, mensaje: 'No se puede cambiar de ' + (estadoActual === 'P' ? 'Pendiente' : estadoActual === 'A' ? 'Aprobado' : estadoActual === 'C' ? 'Cerrado' : 'Anulado') + ' a ' + (nuevoEstado === 'A' ? 'Aprobado' : nuevoEstado === 'C' ? 'Cerrado' : 'Anulado') });
        }

        await db.execute('UPDATE cab_pedido SET estado = @estado WHERE id_empresa = @id_empresa AND ejercicio = @ejercicio AND num_ped = @num_ped', {
            id_empresa: { type: db.sql.Int, value: idEmpresa },
            ejercicio: { type: db.sql.Int, value: ejercicio },
            num_ped: { type: db.sql.VarChar(7), value: numPed },
            estado: { type: db.sql.VarChar(1), value: nuevoEstado }
        });

        res.json({ ok: true, mensaje: 'Pedido ' + numPed + ' (' + ejercicio + ') ' + destino[nuevoEstado].toLowerCase() });
    } catch (error) {
        console.error('[PEDIDOS] Error cambiar estado:', error.message);
        res.status(500).json({ ok: false, mensaje: 'Error al cambiar estado' });
    }
});

module.exports = router;
