'use strict';

/**
 * test/querys/queries.test.js
 *
 * Tests unitarios para todos los archivos en config/querys/.
 *
 * Qué verifica cada test:
 *   1. La clave existe en el módulo exportado.
 *   2. El valor es un string no vacío.
 *   3. Los placeholders $N son consecutivos sin huecos (ej: $1,$3 sin $2 falla).
 *   4. Para módulos que usan DB_SCHEMA: el schema aparece interpolado en el SQL.
 *   5. Keywords SQL mínimos esperados están presentes (SELECT/UPDATE/etc).
 */

// Forzar DB_NAME antes de cargar cualquier módulo para validar la interpolación
process.env.DB_NAME = 'test_meep';

const path = require('path');
const QUERYS_DIR = path.resolve(__dirname, '../../config/querys');

// ─── Helpers ─────────────────────────────────────────────────────────────────

/**
 * Extrae todos los índices de placeholder únicos de un SQL.
 * Ej: '$1 $2 $1 $3' → [1, 2, 3]
 */
function placeholders(sql) {
    const matches = sql.match(/\$[1-9][0-9]*/g) || [];
    const unique = [...new Set(matches.map(m => parseInt(m.slice(1))))];
    return unique.sort((a, b) => a - b);
}

/**
 * Verifica que los placeholders sean consecutivos desde $1 hasta el máximo.
 * Si el SQL no tiene placeholders, pasa sin error.
 */
function expectSequential(sql, queryName) {
    const nums = placeholders(sql);
    if (nums.length === 0) { return; }
    for (let i = 0; i < nums.length; i++) {
        expect(nums[i]).toBe(
            i + 1,
            `[${queryName}] Placeholder $${i + 1} faltante o fuera de secuencia. Encontrados: ${nums.map(n => '$' + n).join(', ')}`
        );
    }
}

/**
 * Conjunto de assertions comunes para una query.
 */
function expectValidQuery(sql, queryName, { schema = true, keyword = 'SELECT' } = {}) {
    expect(typeof sql).toBe('string');
    expect(sql.trim().length).toBeGreaterThan(0);
    expect(sql.toUpperCase()).toContain(keyword);
    if (schema) {
        expect(sql).toContain('test_meep');
    }
    expectSequential(sql, queryName);
}

// ─── Módulos bajo test ────────────────────────────────────────────────────────

const { querys: walletQ }         = require(`${QUERYS_DIR}/wallet`);
const { querys: addressQ }        = require(`${QUERYS_DIR}/address`);
const { querys: notificationsQ }  = require(`${QUERYS_DIR}/notifications`);
const { querys: orderQ }          = require(`${QUERYS_DIR}/order`);
const { querys: restaurantQ }     = require(`${QUERYS_DIR}/restaurant`);
const { querys: restaurantCpnQ }  = require(`${QUERYS_DIR}/restaurantcoupon`);
const { querys: restaurantRatQ }  = require(`${QUERYS_DIR}/restaurantrating`);
const { querys: riderRatQ }       = require(`${QUERYS_DIR}/riderrating`);
const { querys: reportsQ }        = require(`${QUERYS_DIR}/reports`);
const { querys: riderQ }          = require(`${QUERYS_DIR}/rider`);
const { querys: taxQ }            = require(`${QUERYS_DIR}/tax`);
const { querys: userQ }           = require(`${QUERYS_DIR}/user`);

// ─────────────────────────────────────────────────────────────────────────────
// wallet.js
// ─────────────────────────────────────────────────────────────────────────────

describe('config/querys/wallet.js', () => {

    it('exporta el namespace wallet con todas las claves', () => {
        expect(walletQ.wallet).toBeDefined();
        const keys = ['GET_WALLET_TRANSACTION_BY_DATES', 'GET_WALLET_CALCULATION_FROM_ORDER',
            'UPDATE_WALLET_IN', 'UPDATE_WALLET_OUT', 'UPDATE_WALLET_SET'];
        keys.forEach(k => expect(walletQ.wallet).toHaveProperty(k));
    });

    it('GET_WALLET_TRANSACTION_BY_DATES — SELECT con $1 $2 $3', () => {
        const sql = walletQ.wallet.GET_WALLET_TRANSACTION_BY_DATES;
        expectValidQuery(sql, 'GET_WALLET_TRANSACTION_BY_DATES');
        expect(placeholders(sql)).toEqual([1, 2, 3]);
    });

    it('GET_WALLET_CALCULATION_FROM_ORDER — SELECT con $1', () => {
        const sql = walletQ.wallet.GET_WALLET_CALCULATION_FROM_ORDER;
        expectValidQuery(sql, 'GET_WALLET_CALCULATION_FROM_ORDER');
        expect(placeholders(sql)).toEqual([1]);
    });

    it('UPDATE_WALLET_IN — UPDATE con $1 $2 (ammount + $1)', () => {
        const sql = walletQ.wallet.UPDATE_WALLET_IN;
        expectValidQuery(sql, 'UPDATE_WALLET_IN', { keyword: 'UPDATE' });
        expect(sql).toContain('ammount + $1');
        expect(placeholders(sql)).toEqual([1, 2]);
    });

    it('UPDATE_WALLET_OUT — UPDATE con GREATEST y $1 $2 (no permite saldo negativo)', () => {
        const sql = walletQ.wallet.UPDATE_WALLET_OUT;
        expectValidQuery(sql, 'UPDATE_WALLET_OUT', { keyword: 'UPDATE' });
        expect(sql.toUpperCase()).toContain('GREATEST');
        expect(placeholders(sql)).toEqual([1, 2]);
    });

    it('UPDATE_WALLET_SET — UPDATE con $1 $2 (asignación directa)', () => {
        const sql = walletQ.wallet.UPDATE_WALLET_SET;
        expectValidQuery(sql, 'UPDATE_WALLET_SET', { keyword: 'UPDATE' });
        expect(sql).toContain('ammount = $1');
        expect(placeholders(sql)).toEqual([1, 2]);
    });
});

// ─────────────────────────────────────────────────────────────────────────────
// address.js
// ─────────────────────────────────────────────────────────────────────────────

describe('config/querys/address.js', () => {

    it('exporta el namespace address con GET_ADDRESS_BY_USER_ID', () => {
        expect(addressQ.address).toBeDefined();
        expect(addressQ.address).toHaveProperty('GET_ADDRESS_BY_USER_ID');
    });

    it('GET_ADDRESS_BY_USER_ID — SELECT con $1, ordena por uso', () => {
        const sql = addressQ.address.GET_ADDRESS_BY_USER_ID;
        expectValidQuery(sql, 'GET_ADDRESS_BY_USER_ID');
        expect(sql.toUpperCase()).toContain('ORDER BY');
        expect(placeholders(sql)).toEqual([1]);
    });
});

// ─────────────────────────────────────────────────────────────────────────────
// notifications.js
// ─────────────────────────────────────────────────────────────────────────────

describe('config/querys/notifications.js', () => {

    it('exporta el namespace notifications con todas las claves', () => {
        expect(notificationsQ.notifications).toBeDefined();
        ['SELECT_RESTAURANTS', 'SELECT_RIDERS', 'SELECT_USERS'].forEach(k =>
            expect(notificationsQ.notifications).toHaveProperty(k)
        );
    });

    it('SELECT_RESTAURANTS — SELECT con $1 (tax_id)', () => {
        const sql = notificationsQ.notifications.SELECT_RESTAURANTS;
        expectValidQuery(sql, 'SELECT_RESTAURANTS');
        expect(sql).toContain('device_token');
        expect(placeholders(sql)).toEqual([1]);
    });

    it('SELECT_RIDERS — SELECT con $1 (tax_id)', () => {
        const sql = notificationsQ.notifications.SELECT_RIDERS;
        expectValidQuery(sql, 'SELECT_RIDERS');
        expect(sql).toContain('device_token');
        expect(placeholders(sql)).toEqual([1]);
    });

    it('SELECT_USERS — SELECT con $1 (tax_id)', () => {
        const sql = notificationsQ.notifications.SELECT_USERS;
        expectValidQuery(sql, 'SELECT_USERS');
        expect(sql).toContain('device_token');
        expect(placeholders(sql)).toEqual([1]);
    });
});

// ─────────────────────────────────────────────────────────────────────────────
// order.js
// ─────────────────────────────────────────────────────────────────────────────

describe('config/querys/order.js', () => {

    const ORDER_KEYS = [
        'SELECT_ORDER_FITTED', 'SELECT_CLIENT_RECORDS', 'SELECT_RIDER_ORDERS_BY_DATE',
        'SELECT_CITY_ORDERS_BY_DATE', 'SELECT_RIDER_OPEN_ORDERS', 'SELECT_PENDING_ORDERS',
        'SELECT_RESTAURANT_ORDERS_BY_DATE', 'SELECT_RESTAURANT_ORDERS', 'SELECT_CITY_ORDERS',
        'SELECT_ORDER_MENU_EXTRA_ITEMS', 'SELECT_ORDER_MENU_ITEMS_UPDATED',
        'SELECT_ORDER_MENU_EXTRA_ITEMS_UPDATED', 'SELECT_ORDER_MAPINFO', 'SELECT_RIDER_REJECTIONS',
    ];

    it('exporta el namespace order con todas las claves', () => {
        expect(orderQ.order).toBeDefined();
        ORDER_KEYS.forEach(k => expect(orderQ.order).toHaveProperty(k));
    });

    it('SELECT_ORDER_FITTED — SELECT con $1 (order_id)', () => {
        const sql = orderQ.order.SELECT_ORDER_FITTED;
        expectValidQuery(sql, 'SELECT_ORDER_FITTED');
        expect(placeholders(sql)).toEqual([1]);
    });

    it('SELECT_CLIENT_RECORDS — SELECT con $1 $2 $3 (user_id, offset, limit)', () => {
        const sql = orderQ.order.SELECT_CLIENT_RECORDS;
        expectValidQuery(sql, 'SELECT_CLIENT_RECORDS');
        expect(sql.toUpperCase()).toContain('LIMIT');
        expect(placeholders(sql)).toEqual([1, 2, 3]);
    });

    it('SELECT_RIDER_ORDERS_BY_DATE — SELECT con $1 $2 (rider_id, fecha)', () => {
        const sql = orderQ.order.SELECT_RIDER_ORDERS_BY_DATE;
        expectValidQuery(sql, 'SELECT_RIDER_ORDERS_BY_DATE');
        expect(placeholders(sql)).toEqual([1, 2]);
    });

    it('SELECT_CITY_ORDERS_BY_DATE — SELECT con $1 $2 (tax_id, fecha)', () => {
        const sql = orderQ.order.SELECT_CITY_ORDERS_BY_DATE;
        expectValidQuery(sql, 'SELECT_CITY_ORDERS_BY_DATE');
        expect(placeholders(sql)).toEqual([1, 2]);
    });

    it('SELECT_RIDER_OPEN_ORDERS — SELECT con $1 (rider_id)', () => {
        const sql = orderQ.order.SELECT_RIDER_OPEN_ORDERS;
        expectValidQuery(sql, 'SELECT_RIDER_OPEN_ORDERS');
        expect(placeholders(sql)).toEqual([1]);
    });

    it('SELECT_PENDING_ORDERS — SELECT con $1 (tax_id)', () => {
        const sql = orderQ.order.SELECT_PENDING_ORDERS;
        expectValidQuery(sql, 'SELECT_PENDING_ORDERS');
        expect(placeholders(sql)).toEqual([1]);
    });

    it('SELECT_RESTAURANT_ORDERS_BY_DATE — SELECT con $1 $2 (restaurant_id, fecha)', () => {
        const sql = orderQ.order.SELECT_RESTAURANT_ORDERS_BY_DATE;
        expectValidQuery(sql, 'SELECT_RESTAURANT_ORDERS_BY_DATE');
        expect(placeholders(sql)).toEqual([1, 2]);
    });

    it('SELECT_RESTAURANT_ORDERS — SELECT con $1 (restaurant_id)', () => {
        const sql = orderQ.order.SELECT_RESTAURANT_ORDERS;
        expectValidQuery(sql, 'SELECT_RESTAURANT_ORDERS');
        expect(placeholders(sql)).toEqual([1]);
    });

    it('SELECT_CITY_ORDERS — SELECT con $1 (tax_id), incluye estatus calculado', () => {
        const sql = orderQ.order.SELECT_CITY_ORDERS;
        expectValidQuery(sql, 'SELECT_CITY_ORDERS');
        expect(sql.toUpperCase()).toContain('ORDER BY');
        expect(placeholders(sql)).toEqual([1]);
    });

    it('SELECT_ORDER_MENU_EXTRA_ITEMS — SELECT con $1 (order_id)', () => {
        const sql = orderQ.order.SELECT_ORDER_MENU_EXTRA_ITEMS;
        expectValidQuery(sql, 'SELECT_ORDER_MENU_EXTRA_ITEMS');
        expect(placeholders(sql)).toEqual([1]);
    });

    it('SELECT_ORDER_MENU_ITEMS_UPDATED — SELECT con $1 (order_id)', () => {
        const sql = orderQ.order.SELECT_ORDER_MENU_ITEMS_UPDATED;
        expectValidQuery(sql, 'SELECT_ORDER_MENU_ITEMS_UPDATED');
        expect(placeholders(sql)).toEqual([1]);
    });

    it('SELECT_ORDER_MENU_EXTRA_ITEMS_UPDATED — SELECT con $1 (order_id)', () => {
        const sql = orderQ.order.SELECT_ORDER_MENU_EXTRA_ITEMS_UPDATED;
        expectValidQuery(sql, 'SELECT_ORDER_MENU_EXTRA_ITEMS_UPDATED');
        expect(placeholders(sql)).toEqual([1]);
    });

    it('SELECT_ORDER_MAPINFO — SELECT con $1 (order_id)', () => {
        const sql = orderQ.order.SELECT_ORDER_MAPINFO;
        expectValidQuery(sql, 'SELECT_ORDER_MAPINFO');
        expect(placeholders(sql)).toEqual([1]);
    });

    it('SELECT_RIDER_REJECTIONS — SELECT con $1 (order_id)', () => {
        const sql = orderQ.order.SELECT_RIDER_REJECTIONS;
        expectValidQuery(sql, 'SELECT_RIDER_REJECTIONS');
        expect(sql.toUpperCase()).toContain('ORDER BY');
        expect(placeholders(sql)).toEqual([1]);
    });
});

// ─────────────────────────────────────────────────────────────────────────────
// restaurant.js
// ─────────────────────────────────────────────────────────────────────────────

describe('config/querys/restaurant.js', () => {

    const RESTAURANT_KEYS = [
        'SELECT_RESTAURANT_INFO', 'UPDATE_SWITCH_ON_RESTAURANT', 'UPDATE_SWITCH_ON_RESTAURANT_BY_DAY',
        'UPDATE_SWITCH_OFF_RESTAURANT', 'UPDATE_SWITCH_OFF_RESTAURANT_BY_DAY',
        'SELECT_PROFILE_INDICATORS', 'SELECT_TODAY_INDICATORS', 'SELECT_IS_OWENING_MONEY',
        'SELECT_PAYMENT_SUMMARY', 'SELECT_PAYMENT_SUMMARY_BY_DATE', 'SELECT_INFO_EMAIL_FACTURACION',
        'SELECT_RESTAURANTS_WITH_AGREEMENTS', 'SELECT_RESTAURANTS', 'SELECT_RESTAURANT',
        'SELECT_RESTAURANT_WITH_CATEGORY', 'SELECT_RESTAURANT_MENU_HEADERS',
        'SELECT_RESTAURANT_MENU_ITEM', 'SELECT_RESTAURANT_MENU_ITEM_ALL',
        'SELECT_RESTAURANT_MENU_ITEM_EXTRA', 'SELECT_RESTAURANT_MENU_ITEM_EXTRA_ALL',
        'SELECT_DASHBOARD_RESTAURANT', 'SELECT_DASHBOARD_PUNTOSMEEP',
        'SELECT_RESTAURANT_PENDING_PAYMENT', 'GET_TOP5_MOST_POPULAR',
        'GET_TOP5_MOST_POPULAR_PRODUCT', 'GET_NEWEST_RESTAURANTS', 'SELECT_PRODUCTS_BY_TEXT',
    ];

    it('exporta el namespace restaurant con todas las claves', () => {
        expect(restaurantQ.restaurant).toBeDefined();
        RESTAURANT_KEYS.forEach(k => expect(restaurantQ.restaurant).toHaveProperty(k));
    });

    it('todas las queries son strings no vacíos', () => {
        RESTAURANT_KEYS.forEach(k => {
            const sql = restaurantQ.restaurant[k];
            expect(typeof sql).toBe('string');
            expect(sql.trim().length).toBeGreaterThan(0);
        });
    });

    it('todas las queries que usan DB_SCHEMA contienen el schema interpolado', () => {
        // Solo las queries que efectivamente usan ${DB_SCHEMA} en su FROM/JOIN
        const withSchema = [
            'SELECT_PROFILE_INDICATORS', 'SELECT_TODAY_INDICATORS', 'SELECT_IS_OWENING_MONEY',
            'SELECT_PAYMENT_SUMMARY', 'SELECT_PAYMENT_SUMMARY_BY_DATE', 'SELECT_INFO_EMAIL_FACTURACION',
            'SELECT_RESTAURANT', 'SELECT_RESTAURANT_WITH_CATEGORY', 'SELECT_DASHBOARD_RESTAURANT',
            'SELECT_DASHBOARD_PUNTOSMEEP', 'SELECT_RESTAURANT_PENDING_PAYMENT',
            'GET_TOP5_MOST_POPULAR', 'GET_TOP5_MOST_POPULAR_PRODUCT',
            'GET_NEWEST_RESTAURANTS', 'SELECT_PRODUCTS_BY_TEXT',
        ];
        withSchema.forEach(k => {
            expect(restaurantQ.restaurant[k]).toContain('test_meep');
        });
    });

    it('todas las queries tienen placeholders $N consecutivos sin huecos', () => {
        // SELECT_RESTAURANT_MENU_HEADERS usa $1, $2, $5 (sin $3/$4 por diseño:
        // el servicio pasa 5 valores pero solo necesita posiciones 1, 2 y 5).
        // Se excluye del check genérico y se documenta en su propio test.
        const SKIP = new Set(['SELECT_RESTAURANT_MENU_HEADERS']);
        RESTAURANT_KEYS.filter(k => !SKIP.has(k)).forEach(k => {
            expectSequential(restaurantQ.restaurant[k], k);
        });
    });

    it('[DOC] SELECT_RESTAURANT_MENU_HEADERS — usa $1 $2 $5, sin $3 ni $4 (diseño intencional)', () => {
        const sql = restaurantQ.restaurant.SELECT_RESTAURANT_MENU_HEADERS;
        expect(placeholders(sql)).toEqual([1, 2, 5]);
    });

    it('UPDATE_SWITCH_ON_RESTAURANT — es UPDATE sobre restaurant_timing con $1', () => {
        const sql = restaurantQ.restaurant.UPDATE_SWITCH_ON_RESTAURANT;
        expect(sql.toUpperCase()).toContain('UPDATE');
        expect(sql.toLowerCase()).toContain('restaurant_timing');
        expect(placeholders(sql)).toEqual([1]);
    });

    it('UPDATE_SWITCH_ON_RESTAURANT_BY_DAY — es UPDATE con $1 $2 (restaurant_id, day)', () => {
        const sql = restaurantQ.restaurant.UPDATE_SWITCH_ON_RESTAURANT_BY_DAY;
        expect(sql.toUpperCase()).toContain('UPDATE');
        expect(placeholders(sql)).toEqual([1, 2]);
    });

    it('UPDATE_SWITCH_OFF_RESTAURANT — es UPDATE con $1', () => {
        const sql = restaurantQ.restaurant.UPDATE_SWITCH_OFF_RESTAURANT;
        expect(sql.toUpperCase()).toContain('UPDATE');
        expect(placeholders(sql)).toEqual([1]);
    });

    it('UPDATE_SWITCH_OFF_RESTAURANT_BY_DAY — es UPDATE con $1 $2', () => {
        const sql = restaurantQ.restaurant.UPDATE_SWITCH_OFF_RESTAURANT_BY_DAY;
        expect(sql.toUpperCase()).toContain('UPDATE');
        expect(placeholders(sql)).toEqual([1, 2]);
    });
});

// ─────────────────────────────────────────────────────────────────────────────
// restaurantcoupon.js
// ─────────────────────────────────────────────────────────────────────────────

describe('config/querys/restaurantcoupon.js', () => {

    it('exporta el namespace restaurantcoupon con todas las claves', () => {
        expect(restaurantCpnQ.restaurantcoupon).toBeDefined();
        ['SELECT_ACTIVE_COUPONS', 'SELECT_COUPON_BY_CODE'].forEach(k =>
            expect(restaurantCpnQ.restaurantcoupon).toHaveProperty(k)
        );
    });

    it('SELECT_ACTIVE_COUPONS — SELECT con $1 $2 (tax_id, day)', () => {
        const sql = restaurantCpnQ.restaurantcoupon.SELECT_ACTIVE_COUPONS;
        expect(typeof sql).toBe('string');
        expect(sql.toUpperCase()).toContain('SELECT');
        expect(sql.toUpperCase()).toContain('WHERE');
        expect(sql.toLowerCase()).toContain('coupon');
        expect(placeholders(sql)).toEqual([1, 2]);
    });

    it('SELECT_COUPON_BY_CODE — SELECT con $1 $2 $3 (tax_id, coupon_code, restaurant_id)', () => {
        const sql = restaurantCpnQ.restaurantcoupon.SELECT_COUPON_BY_CODE;
        expect(typeof sql).toBe('string');
        expect(sql.toUpperCase()).toContain('SELECT');
        expect(sql.toLowerCase()).toContain('coupon_code');
        expect(placeholders(sql)).toEqual([1, 2, 3]);
    });
});

// ─────────────────────────────────────────────────────────────────────────────
// restaurantrating.js
// ─────────────────────────────────────────────────────────────────────────────

describe('config/querys/restaurantrating.js', () => {

    it('exporta el namespace rating con SELECT_RESTAURANT_REVIEWS', () => {
        expect(restaurantRatQ.rating).toBeDefined();
        expect(restaurantRatQ.rating).toHaveProperty('SELECT_RESTAURANT_REVIEWS');
    });

    it('SELECT_RESTAURANT_REVIEWS — SELECT con $1 $2 $3 (restaurant_id, offset, limit)', () => {
        const sql = restaurantRatQ.rating.SELECT_RESTAURANT_REVIEWS;
        expect(typeof sql).toBe('string');
        expect(sql.toUpperCase()).toContain('SELECT');
        expect(sql.toLowerCase()).toContain('restaurant_rating');
        expect(sql.toUpperCase()).toContain('ORDER BY');
        expect(sql.toUpperCase()).toContain('LIMIT');
        expect(placeholders(sql)).toEqual([1, 2, 3]);
    });
});

// ─────────────────────────────────────────────────────────────────────────────
// riderrating.js
// ─────────────────────────────────────────────────────────────────────────────

describe('config/querys/riderrating.js', () => {

    it('exporta el namespace rating con SELECT_RIDER_REVIEWS', () => {
        expect(riderRatQ.rating).toBeDefined();
        expect(riderRatQ.rating).toHaveProperty('SELECT_RIDER_REVIEWS');
    });

    it('SELECT_RIDER_REVIEWS — SELECT con $1 $2 $3 (rider_user_id, offset, limit)', () => {
        const sql = riderRatQ.rating.SELECT_RIDER_REVIEWS;
        expect(typeof sql).toBe('string');
        expect(sql.toUpperCase()).toContain('SELECT');
        expect(sql.toLowerCase()).toContain('rider_rating');
        expect(sql.toUpperCase()).toContain('ORDER BY');
        expect(sql.toUpperCase()).toContain('LIMIT');
        expect(placeholders(sql)).toEqual([1, 2, 3]);
    });
});

// ─────────────────────────────────────────────────────────────────────────────
// reports.js
// ─────────────────────────────────────────────────────────────────────────────

describe('config/querys/reports.js', () => {

    const REPORT_KEYS = [
        'RPT_MENSUAL_LICENCIAS', 'RPT_ANUAL_INGRESOS', 'RPT_WEEK_RESUME',
        'RPT_RIDERS_WEEK_RESUME', 'RPT_SERVICE_GROWTH', 'RPT_RIDERS_PERFORMANCE',
        'RPT_GRANJERO', 'RPT_CONVENIOS', 'RPT_DINAMICAS', 'RPT_SERVICIOS',
    ];

    it('exporta el namespace reports con todas las claves', () => {
        expect(reportsQ.reports).toBeDefined();
        REPORT_KEYS.forEach(k => expect(reportsQ.reports).toHaveProperty(k));
    });

    it('todas las queries son strings no vacíos', () => {
        REPORT_KEYS.forEach(k => {
            const sql = reportsQ.reports[k];
            expect(typeof sql).toBe('string');
            expect(sql.trim().length).toBeGreaterThan(0);
        });
    });

    it('todas las queries tienen placeholders $N consecutivos sin huecos', () => {
        REPORT_KEYS.forEach(k => {
            expectSequential(reportsQ.reports[k], k);
        });
    });

    it('RPT_MENSUAL_LICENCIAS — SELECT con $1 $2 $3 (tax_id, mes, año)', () => {
        const sql = reportsQ.reports.RPT_MENSUAL_LICENCIAS;
        expect(sql.toUpperCase()).toContain('SELECT');
        expect(sql.toUpperCase()).toContain('UNION');
        expect(placeholders(sql)).toEqual([1, 2, 3]);
    });

    it('RPT_ANUAL_INGRESOS — SELECT con $1 (año)', () => {
        const sql = reportsQ.reports.RPT_ANUAL_INGRESOS;
        expect(sql.toUpperCase()).toContain('SELECT');
        expect(sql.toUpperCase()).toContain('UNION');
        expect(placeholders(sql)).toEqual([1]);
    });

    it('RPT_WEEK_RESUME — SELECT con $1 $2 $3 (tax_id, fecha_inicio, fecha_fin)', () => {
        expect(placeholders(reportsQ.reports.RPT_WEEK_RESUME)).toEqual([1, 2, 3]);
    });

    it('RPT_RIDERS_WEEK_RESUME — SELECT con $1 $2 $3 (tax_id, fecha_inicio, fecha_fin)', () => {
        expect(placeholders(reportsQ.reports.RPT_RIDERS_WEEK_RESUME)).toEqual([1, 2, 3]);
    });

    it('RPT_SERVICE_GROWTH — SELECT con $1 $2 (tax_id, año)', () => {
        expect(placeholders(reportsQ.reports.RPT_SERVICE_GROWTH)).toEqual([1, 2]);
    });

    it('RPT_RIDERS_PERFORMANCE — SELECT con $1 $2 $3 (tax_id, fecha_inicio, fecha_fin)', () => {
        expect(placeholders(reportsQ.reports.RPT_RIDERS_PERFORMANCE)).toEqual([1, 2, 3, 4, 5]);
    });

    it('RPT_GRANJERO — SELECT con $1 $2 (fecha_inicio, fecha_fin)', () => {
        expect(placeholders(reportsQ.reports.RPT_GRANJERO)).toEqual([1, 2]);
    });

    it('RPT_CONVENIOS — SELECT con $1 $2 $3 (fecha_inicio, fecha_fin, regexp_restaurantes)', () => {
        const sql = reportsQ.reports.RPT_CONVENIOS;
        expect(sql.toUpperCase()).toContain('REGEXP');
        expect(placeholders(sql)).toEqual([1, 2, 3]);
    });

    it('RPT_DINAMICAS — SELECT con $1 $2 $3 (fecha_inicio, fecha_fin, restaurant_id)', () => {
        expect(placeholders(reportsQ.reports.RPT_DINAMICAS)).toEqual([1, 2, 3]);
    });

    it('RPT_SERVICIOS — SELECT con $1 $2 $3 $4 (restaurant_id, fecha_inicio, fecha_fin, tipo_cobro)', () => {
        expect(placeholders(reportsQ.reports.RPT_SERVICIOS)).toEqual([1, 2, 3, 4]);
    });
});

// ─────────────────────────────────────────────────────────────────────────────
// rider.js
// ─────────────────────────────────────────────────────────────────────────────

describe('config/querys/rider.js', () => {

    const RIDER_KEYS = [
        'SELECT_RIDER', 'SELECT_PROFILE_INDICATORS', 'SELECT_TODAY_INDICATORS',
        'SELECT_RATING_REPA_BY_DATE', 'SELECT_RIDERS_ORDERED_BY_DISTANCE_FROM_RESTAURANT',
        'SELECT_DASHBOARD_RIDERS', 'SELECT_FREE_RIDERS', 'SELECT_ACTIVE_RIDERS',
        'SELECT_PAYMENT_SUMMARY', 'SELECT_PAYMENT_SUMMARY_BY_WEEKDAY',
        'GET_STATUS_CHALLENGES', 'SELECT_IS_OWENING_MONEY',
        'SELECT_IS_OWENING_MONEY_LASTWEEK', 'SELECT_IS_BLOCKED', 'GET_FEE_RETENIDO_ACUMULADO',
    ];

    it('exporta el namespace rider con todas las claves', () => {
        expect(riderQ.rider).toBeDefined();
        RIDER_KEYS.forEach(k => expect(riderQ.rider).toHaveProperty(k));
    });

    it('todas las queries son strings no vacíos', () => {
        RIDER_KEYS.forEach(k => {
            const sql = riderQ.rider[k];
            expect(typeof sql).toBe('string');
            expect(sql.trim().length).toBeGreaterThan(0);
        });
    });

    it('todas las queries tienen placeholders $N consecutivos sin huecos', () => {
        RIDER_KEYS.forEach(k => {
            expectSequential(riderQ.rider[k], k);
        });
    });

    it('SELECT_RIDER — SELECT con $1 (user_id)', () => {
        expect(placeholders(riderQ.rider.SELECT_RIDER)).toEqual([1]);
    });

    it('SELECT_PROFILE_INDICATORS — SELECT con $1 (rider_user_id)', () => {
        expect(placeholders(riderQ.rider.SELECT_PROFILE_INDICATORS)).toEqual([1]);
    });

    it('SELECT_TODAY_INDICATORS — SELECT con $1 (rider_user_id)', () => {
        expect(placeholders(riderQ.rider.SELECT_TODAY_INDICATORS)).toEqual([1]);
    });

    it('SELECT_RATING_REPA_BY_DATE — SELECT con $1 $2 (tax_id, fecha)', () => {
        expect(placeholders(riderQ.rider.SELECT_RATING_REPA_BY_DATE)).toEqual([1, 2]);
    });

    it('SELECT_RIDERS_ORDERED_BY_DISTANCE_FROM_RESTAURANT — SELECT con $1..$7 (lat,lon,tax,bbox)', () => {
        const sql = riderQ.rider.SELECT_RIDERS_ORDERED_BY_DISTANCE_FROM_RESTAURANT;
        expect(sql.toUpperCase()).toContain('ST_DISTANCE_SPHERE');
        expect(placeholders(sql)).toEqual([1, 2, 3, 4, 5, 6, 7]);
    });

    it('SELECT_DASHBOARD_RIDERS — SELECT con $1 $2 $3 $4 (tax_id, fecha_ini, fecha_fin, show_all)', () => {
        expect(placeholders(riderQ.rider.SELECT_DASHBOARD_RIDERS)).toEqual([1, 2, 3, 4]);
    });

    it('SELECT_FREE_RIDERS — SELECT con $1 (tax_id)', () => {
        expect(placeholders(riderQ.rider.SELECT_FREE_RIDERS)).toEqual([1]);
    });

    it('SELECT_ACTIVE_RIDERS — SELECT con $1 (tax_id)', () => {
        expect(placeholders(riderQ.rider.SELECT_ACTIVE_RIDERS)).toEqual([1]);
    });

    it('SELECT_PAYMENT_SUMMARY — SELECT con $1 $2 $3 $4 (tax_id, rider_id, fecha_ini, fecha_fin)', () => {
        expect(placeholders(riderQ.rider.SELECT_PAYMENT_SUMMARY)).toEqual([1, 2, 3, 4]);
    });

    it('SELECT_PAYMENT_SUMMARY_BY_WEEKDAY — SELECT con $1..$5 (tax_id, rider_id, fechas, weekday)', () => {
        expect(placeholders(riderQ.rider.SELECT_PAYMENT_SUMMARY_BY_WEEKDAY)).toEqual([1, 2, 3, 4, 5]);
    });

    it('GET_STATUS_CHALLENGES — SELECT con $1 $2 $3 $4 (tax_id, rider_id, fechas)', () => {
        expect(placeholders(riderQ.rider.GET_STATUS_CHALLENGES)).toEqual([1, 2, 3, 4]);
    });

    it('SELECT_IS_OWENING_MONEY — SELECT con $1 $2 (rider_user_id, fecha)', () => {
        expect(placeholders(riderQ.rider.SELECT_IS_OWENING_MONEY)).toEqual([1, 2]);
    });

    it('SELECT_IS_OWENING_MONEY_LASTWEEK — SELECT con $1 $2 $3 (rider_user_id, fechas)', () => {
        expect(placeholders(riderQ.rider.SELECT_IS_OWENING_MONEY_LASTWEEK)).toEqual([1, 2, 3]);
    });

    it('SELECT_IS_BLOCKED — SELECT con $1 (rider_user_id)', () => {
        expect(placeholders(riderQ.rider.SELECT_IS_BLOCKED)).toEqual([1]);
    });

    it('GET_FEE_RETENIDO_ACUMULADO — SELECT con $1 $2 $3 (rider_user_id, fechas)', () => {
        expect(placeholders(riderQ.rider.GET_FEE_RETENIDO_ACUMULADO)).toEqual([1, 2, 3]);
    });
});

// ─────────────────────────────────────────────────────────────────────────────
// tax.js
// ─────────────────────────────────────────────────────────────────────────────

describe('config/querys/tax.js', () => {

    it('exporta el namespace tax con todas las claves', () => {
        expect(taxQ.tax).toBeDefined();
        ['SELECT_NEAREST_CITY', 'SELECT_PAYMENT_SUMMARY'].forEach(k =>
            expect(taxQ.tax).toHaveProperty(k)
        );
    });

    it('SELECT_NEAREST_CITY — SELECT con $1 $2 (lat, lon), incluye fórmula de distancia', () => {
        const sql = taxQ.tax.SELECT_NEAREST_CITY;
        expectValidQuery(sql, 'SELECT_NEAREST_CITY', { schema: false });
        expect(sql.toUpperCase()).toContain('ACOS');
        expect(sql.toUpperCase()).toContain('LIMIT');
        expect(placeholders(sql)).toEqual([1, 2]);
    });

    it('SELECT_PAYMENT_SUMMARY — SELECT con $1 $2 $3 (tax_id, año, mes), incluye UNION', () => {
        const sql = taxQ.tax.SELECT_PAYMENT_SUMMARY;
        expectValidQuery(sql, 'SELECT_PAYMENT_SUMMARY');
        expect(sql.toUpperCase()).toContain('UNION');
        expect(placeholders(sql)).toEqual([1, 2, 3]);
    });
});

// ─────────────────────────────────────────────────────────────────────────────
// user.js
// ─────────────────────────────────────────────────────────────────────────────

describe('config/querys/user.js', () => {

    const USER_KEYS = [
        'RIDER_SELECT_BY_EMAIL', 'RESTAURANT_SELECT_BY_EMAIL', 'PUNTOMEEP_SELECT_BY_EMAIL',
        'USER_SELECT_BY_EMAIL', 'ADMIN_SELECT_BY_EMAIL', 'ADMIN_SELECT_BY_ID',
        'GET_USER', 'GET_USER_RIDER_BY_USERID', 'GET_USER_BY_PHONE',
    ];

    it('exporta el namespace user con todas las claves', () => {
        expect(userQ.user).toBeDefined();
        USER_KEYS.forEach(k => expect(userQ.user).toHaveProperty(k));
    });

    it('todas las queries son strings no vacíos', () => {
        USER_KEYS.forEach(k => {
            const sql = userQ.user[k];
            expect(typeof sql).toBe('string');
            expect(sql.trim().length).toBeGreaterThan(0);
        });
    });

    it('todas las queries usan $1 como primer placeholder', () => {
        USER_KEYS.forEach(k => {
            const nums = placeholders(userQ.user[k]);
            expect(nums[0]).toBe(1);
        });
    });

    it('todas las queries tienen placeholders $N consecutivos sin huecos', () => {
        USER_KEYS.forEach(k => {
            expectSequential(userQ.user[k], k);
        });
    });

    it('RIDER_SELECT_BY_EMAIL — SELECT con $1 (email), role rider', () => {
        const sql = userQ.user.RIDER_SELECT_BY_EMAIL;
        expectValidQuery(sql, 'RIDER_SELECT_BY_EMAIL');
        expect(sql).toContain('\'rider\'');
        expect(placeholders(sql)).toEqual([1]);
    });

    it('RESTAURANT_SELECT_BY_EMAIL — SELECT con $1 (email), role hotel', () => {
        const sql = userQ.user.RESTAURANT_SELECT_BY_EMAIL;
        expectValidQuery(sql, 'RESTAURANT_SELECT_BY_EMAIL');
        expect(sql).toContain('\'hotel\'');
        expect(placeholders(sql)).toEqual([1]);
    });

    it('PUNTOMEEP_SELECT_BY_EMAIL — SELECT con $1 (email), role puntomeep', () => {
        const sql = userQ.user.PUNTOMEEP_SELECT_BY_EMAIL;
        expectValidQuery(sql, 'PUNTOMEEP_SELECT_BY_EMAIL');
        expect(sql).toContain('\'puntomeep\'');
        expect(placeholders(sql)).toEqual([1]);
    });

    it('USER_SELECT_BY_EMAIL — SELECT con $1 (email), role user', () => {
        const sql = userQ.user.USER_SELECT_BY_EMAIL;
        expectValidQuery(sql, 'USER_SELECT_BY_EMAIL');
        expect(sql).toContain('\'user\'');
        expect(placeholders(sql)).toEqual([1]);
    });

    it('ADMIN_SELECT_BY_EMAIL — SELECT con $1 (email)', () => {
        const sql = userQ.user.ADMIN_SELECT_BY_EMAIL;
        expectValidQuery(sql, 'ADMIN_SELECT_BY_EMAIL');
        expect(sql.toLowerCase()).toContain('user_admin');
        expect(placeholders(sql)).toEqual([1]);
    });

    it('ADMIN_SELECT_BY_ID — SELECT con $1 (admin_id)', () => {
        const sql = userQ.user.ADMIN_SELECT_BY_ID;
        expectValidQuery(sql, 'ADMIN_SELECT_BY_ID');
        expect(sql.toLowerCase()).toContain('user_admin');
        expect(placeholders(sql)).toEqual([1]);
    });

    it('GET_USER — SELECT con $1 (user_id), incluye anti-fraude por rechazos', () => {
        const sql = userQ.user.GET_USER;
        expectValidQuery(sql, 'GET_USER');
        expect(sql.toLowerCase()).toContain('rider_rejections');
        expect(placeholders(sql)).toEqual([1]);
    });

    it('GET_USER_RIDER_BY_USERID — SELECT con $1 (user_id)', () => {
        const sql = userQ.user.GET_USER_RIDER_BY_USERID;
        expectValidQuery(sql, 'GET_USER_RIDER_BY_USERID');
        expect(sql).toContain('\'rider\'');
        expect(placeholders(sql)).toEqual([1]);
    });

    it('GET_USER_BY_PHONE — SELECT con $1 (phone), normaliza formato', () => {
        const sql = userQ.user.GET_USER_BY_PHONE;
        expect(typeof sql).toBe('string');
        expect(sql.toUpperCase()).toContain('REPLACE');
        expect(sql.toUpperCase()).toContain('TRIM');
        expect(placeholders(sql)).toEqual([1]);
    });
});
