// Bytecode on-demand loading tests
// Verifies .sxbc modules are only downloaded when needed, not all at boot.

const { test, expect } = require('playwright/test');
const { BASE_URL, waitForSxReady } = require('./helpers');

test.describe('Bytecode Loading', () => {

  test('manifest is fetched at boot', async ({ page }) => {
    const manifestReqs = [];
    page.on('request', req => {
      if (req.url().includes('module-manifest.json')) manifestReqs.push(req.url());
    });

    await page.goto(BASE_URL + '/sx/', { waitUntil: 'domcontentloaded' });
    await waitForSxReady(page);

    expect(manifestReqs.length).toBe(1);
  });

  test('boot loads only manifest deps, not all modules', async ({ page }) => {
    const sxbcReqs = [];
    page.on('request', req => {
      const url = req.url();
      if (url.includes('.sxbc') && !url.includes('manifest')) {
        const name = url.split('/').pop().split('?')[0].replace('.sxbc', '');
        sxbcReqs.push(name);
      }
    });

    await page.goto(BASE_URL + '/sx/', { waitUntil: 'domcontentloaded' });
    await waitForSxReady(page);

    // Boot loads manifest deps for hydration — NOT all 26
    // 13 boot deps, 13 deferred (compiler, vm, harness, etc.)
    expect(sxbcReqs.length).toBeLessThanOrEqual(14);
    expect(sxbcReqs.length).toBeGreaterThan(5);

    // Boot-critical modules present
    expect(sxbcReqs).toContain('boot');
    expect(sxbcReqs).toContain('dom');

    // Dev/test modules NOT loaded on homepage
    const nonBootModules = ['compiler', 'vm', 'harness', 'harness-web', 'harness-reactive'];
    for (const mod of nonBootModules) {
      expect(sxbcReqs).not.toContain(mod);
    }
  });

  test('no .sx fallback requests when bytecode exists', async ({ page }) => {
    const sxFallbackReqs = [];
    page.on('request', req => {
      const url = req.url();
      // .sx source fallback (not .sxbc) in the wasm directory
      if (url.match(/\/static\/wasm\/sx\/[^/]+\.sx(\?|$)/) && !url.includes('.sxbc')) {
        sxFallbackReqs.push(url);
      }
    });

    await page.goto(BASE_URL + '/sx/', { waitUntil: 'domcontentloaded' });
    await waitForSxReady(page);

    expect(sxFallbackReqs).toEqual([]);
  });

  test('all .sxbc requests use cache-busting hash', async ({ page }) => {
    const sxbcUrls = [];
    page.on('request', req => {
      if (req.url().includes('.sxbc')) sxbcUrls.push(req.url());
    });

    await page.goto(BASE_URL + '/sx/', { waitUntil: 'domcontentloaded' });
    await waitForSxReady(page);

    // Every .sxbc request should have a ?v= cache-buster
    for (const url of sxbcUrls) {
      expect(url).toMatch(/[?&]v=/);
    }

    // All should share the same hash (combined hash from data-sxbc-hash)
    const hashes = sxbcUrls.map(u => new URL(u).searchParams.get('v')).filter(Boolean);
    const unique = [...new Set(hashes)];
    expect(unique.length).toBe(1);
  });

  test('SPA navigation does not re-fetch boot modules', async ({ page }) => {
    await page.goto(BASE_URL + '/sx/', { waitUntil: 'domcontentloaded' });
    await waitForSxReady(page);

    // Start tracking requests AFTER boot
    const postBootSxbc = [];
    page.on('request', req => {
      if (req.url().includes('.sxbc')) {
        const name = req.url().split('/').pop().split('?')[0].replace('.sxbc', '');
        postBootSxbc.push(name);
      }
    });

    // Navigate to a different page via SPA
    await page.click('a[href*="geography"]');
    await page.waitForTimeout(2000);

    // Boot modules should NOT be re-fetched
    const bootModules = ['dom', 'signals', 'boot', 'engine', 'orchestration'];
    for (const mod of bootModules) {
      expect(postBootSxbc).not.toContain(mod);
    }
  });

});
