import assert from 'node:assert/strict';
import {fileURLToPath} from 'node:url';
import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({
    executablePath: process.env.PUPPETEER_EXECUTABLE_PATH || '/usr/bin/chromium-browser',
    args: [
        '--disable-dev-shm-usage',
        '--disable-gpu',
        '--no-sandbox',
    ],
    headless: true,
});

const collectionMarkup = (id, positions) => `
    <div id="${id}"
         class="product-collection-item-collection"
         data-allow-add="0"
         data-allow-remove="0"
         data-name-prefix="product_collection[items]">
        ${positions.map((position, index) => `
            <div id="${id}_${index}" data-product="produit-${index}">
                <input class="position-form"
                       name="product_collection[items][${index}][position]"
                       value="${position}">
                <a href="#" class="collection-up">Monter</a>
                <a href="#" class="collection-down">Descendre</a>
            </div>
        `).join('')}
    </div>
`;

const readState = async (page, selector) => page.$$eval(
    `${selector} > div`,
    (elements) => elements.map((element) => ({
        position: Number(element.querySelector('.position-form').value),
        product: element.dataset.product,
    })),
);

const changePosition = async (page, collection, product, position) => page.$eval(
    `#${collection} > [data-product="${product}"] .position-form`,
    (input, value) => {
        input.value = value;
        input.dispatchEvent(new Event('change', {bubbles: true}));
    },
    String(position),
);

try {
    const page = await browser.newPage();
    const browserErrors = [];

    page.on('pageerror', (error) => browserErrors.push(error.message));

    await page.setContent(`
        ${collectionMarkup('duplicate_positions', [0, 1, 1])}
        ${collectionMarkup('manual_position', [0, 1, 2])}
        ${collectionMarkup('multiple_positions', [0, 1, 2, 3])}
        ${collectionMarkup('bounded_positions', [0, 1, 2])}
    `);
    await page.addScriptTag({
        path: fileURLToPath(new URL(
            '../../src/Bridge/Symfony/Bundle/Resources/public/js/admin/lib/jquery.min.js',
            import.meta.url,
        )),
    });
    await page.addScriptTag({
        path: fileURLToPath(new URL(
            '../../src/Bridge/Symfony/Bundle/Resources/public/js/admin/lib/jquery.collection.js',
            import.meta.url,
        )),
    });
    await page.addScriptTag({
        path: fileURLToPath(new URL(
            '../../src/Bridge/Symfony/Bundle/Resources/public/js/admin/collection.js',
            import.meta.url,
        )),
    });
    await page.waitForFunction(() => (
        window.jQuery('#duplicate_positions').data('collection-settings')
        && window.jQuery('#manual_position').data('collection-settings')
        && window.jQuery('#multiple_positions').data('collection-settings')
        && window.jQuery('#bounded_positions').data('collection-settings')
    ));

    assert.deepEqual(
        await readState(page, '#duplicate_positions'),
        [
            {position: 0, product: 'produit-0'},
            {position: 1, product: 'produit-1'},
            {position: 2, product: 'produit-2'},
        ],
        'Une collection chargée avec deux positions identiques doit être renumérotée.',
    );

    await page.click('#duplicate_positions > div:last-child .collection-up');

    assert.deepEqual(
        await readState(page, '#duplicate_positions'),
        [
            {position: 0, product: 'produit-0'},
            {position: 1, product: 'produit-2'},
            {position: 2, product: 'produit-1'},
        ],
        'Le dernier produit doit pouvoir remonter après la réparation d’un doublon existant.',
    );

    await changePosition(page, 'manual_position', 'produit-2', 1);

    assert.deepEqual(
        await readState(page, '#manual_position'),
        [
            {position: 0, product: 'produit-0'},
            {position: 1, product: 'produit-2'},
            {position: 2, product: 'produit-1'},
        ],
        'Modifier une position doit déplacer le produit et conserver des positions uniques.',
    );

    await changePosition(page, 'multiple_positions', 'produit-3', 0);

    assert.deepEqual(
        await readState(page, '#multiple_positions'),
        [
            {position: 0, product: 'produit-3'},
            {position: 1, product: 'produit-0'},
            {position: 2, product: 'produit-1'},
            {position: 3, product: 'produit-2'},
        ],
        'Un produit doit pouvoir remonter plusieurs positions.',
    );

    await changePosition(page, 'multiple_positions', 'produit-3', 3);

    assert.deepEqual(
        await readState(page, '#multiple_positions'),
        [
            {position: 0, product: 'produit-0'},
            {position: 1, product: 'produit-1'},
            {position: 2, product: 'produit-2'},
            {position: 3, product: 'produit-3'},
        ],
        'Un produit doit pouvoir redescendre plusieurs positions.',
    );

    await changePosition(page, 'bounded_positions', 'produit-0', 99);
    await changePosition(page, 'bounded_positions', 'produit-0', -10);
    await changePosition(page, 'bounded_positions', 'produit-1', '');

    assert.deepEqual(
        await readState(page, '#bounded_positions'),
        [
            {position: 0, product: 'produit-0'},
            {position: 1, product: 'produit-1'},
            {position: 2, product: 'produit-2'},
        ],
        'Les positions hors limites ou vides doivent être ramenées à un ordre valide.',
    );
    assert.deepEqual(browserErrors, []);
} finally {
    await browser.close();
}

console.log('OK — les positions de collection restent uniques et ordonnées.');
