import { expect, test } from '@playwright/test';
import {
  observeRuntime,
  startNestedStaticServer,
  type NestedStaticServer,
} from './browser-helpers';

test.describe('static application and Toolbox shell', () => {
  test('runs standalone, stays lazy and supports keyboard-operated shell controls', async ({
    page,
  }) => {
    const runtime = observeRuntime(page);

    await page.goto('/');

    await expect(page).toHaveTitle('Audio & Video Tools');
    await expect(
      page.getByRole('heading', {
        level: 1,
        name: 'Audio & Video Tools',
      })
    ).toBeVisible();
    await expect(page.locator('[data-toolbox-context]')).toHaveAttribute(
      'data-toolbox-context',
      'standalone'
    );
    await expect(page.getByRole('main')).toBeVisible();
    await expect(
      page.getByRole('tab', { name: 'Quick Convert' })
    ).toHaveAttribute('aria-selected', 'true');
    await expect(
      page.getByRole('button', { name: 'Choose files' })
    ).toBeEnabled();

    expect(
      runtime.requests.some((url) => url.includes('/vendor/ffmpeg/'))
    ).toBe(false);
    expect(
      runtime.requests.some((url) => /\/assets\/worker-[^/]+\.js$/u.test(url))
    ).toBe(false);

    const helpButton = page.getByRole('button', { name: 'Help' });
    await helpButton.focus();
    await page.keyboard.press('Enter');
    const helpDialog = page.getByRole('dialog', {
      name: 'Audio & Video Tools',
    });
    await expect(helpDialog).toBeVisible();
    await expect(
      helpDialog.getByRole('heading', {
        level: 2,
        name: 'Audio & Video Tools',
      })
    ).toBeVisible();
    await page.keyboard.press('Escape');
    await expect(helpDialog).toBeHidden();

    const personalizeButton = page.getByRole('button', {
      name: 'Personalize',
    });
    await personalizeButton.focus();
    await page.keyboard.press('Enter');
    await expect(
      page.getByRole('heading', {
        level: 2,
        name: 'Personalize your toolbox',
      })
    ).toBeVisible();
    await page.getByRole('button', { name: 'Dark' }).click();
    await expect(page.locator('.toolbox-shell').first()).toHaveAttribute(
      'data-toolbox-theme',
      'dark'
    );
    await page.keyboard.press('Escape');
    await expect(personalizeButton).toBeFocused();

    const networkUrls = runtime.requests.filter((url) => /^https?:/u.test(url));
    expect(
      networkUrls.every(
        (url) => new URL(url).origin === 'http://127.0.0.1:4173'
      )
    ).toBe(true);
    expect(runtime.failedRequests).toEqual([]);
    expect(runtime.pageErrors).toEqual([]);
    expect(runtime.consoleErrors).toEqual([]);
  });

  test('keeps preview primary and exposes accessible Edit drawers on narrow screens', async ({
    page,
  }) => {
    await page.setViewportSize({ width: 390, height: 844 });
    await page.goto('/');
    await page.getByRole('tab', { name: 'Edit' }).click();

    await expect(page.getByRole('heading', { name: 'Preview' })).toBeVisible();
    const mediaDrawer = page.locator('#edit-media-drawer');
    const inspectorDrawer = page.locator('#edit-inspector-drawer');
    await expect(mediaDrawer).toBeHidden();
    await expect(inspectorDrawer).toBeHidden();

    const sources = page.getByRole('button', { name: 'Sources' });
    await sources.click();
    await expect(mediaDrawer).toBeVisible();
    await expect(
      mediaDrawer.getByRole('button', { name: 'Close media bin' })
    ).toBeFocused();
    await page.keyboard.press('Escape');
    await expect(mediaDrawer).toBeHidden();
    await expect(sources).toBeFocused();

    const inspector = page.getByRole('button', { name: 'Inspector' });
    await inspector.click();
    await expect(inspectorDrawer).toBeVisible();
    const closeInspector = inspectorDrawer.getByRole('button', {
      name: 'Close inspector',
    });
    await expect(closeInspector).toBeFocused();
    await closeInspector.click();
    await expect(inspectorDrawer).toBeHidden();
    await expect(inspector).toBeFocused();
  });
});

test.describe('nested static deployment and Toolbox context', () => {
  let nestedServer: NestedStaticServer;

  test.beforeAll(async () => {
    nestedServer = await startNestedStaticServer();
  });

  test.afterAll(async () => {
    await nestedServer.close();
  });

  test('loads a valid same-origin catalog and all runtime assets below the nested path', async ({
    page,
  }) => {
    const runtime = observeRuntime(page);
    await page.goto(`${nestedServer.appUrl}?toolbox=../toolbox.catalog.json`);

    await expect(page.locator('[data-toolbox-context]')).toHaveAttribute(
      'data-toolbox-context',
      'connected'
    );
    await expect(
      page.getByRole('link', { name: 'add·ideas Toolbox' })
    ).toHaveAttribute('href', `${nestedServer.origin}/deep/nested/`);

    await page.getByRole('button', { name: 'Apps' }).click();
    const appSwitcher = page.getByRole('navigation', {
      name: 'Toolbox applications',
    });
    await expect(
      appSwitcher.getByRole('link', { name: 'Browser Test Tool' })
    ).toBeVisible();
    await expect(
      appSwitcher.getByRole('link', { name: 'Audio & Video Tools' })
    ).toBeVisible();
    await page.keyboard.press('Escape');

    expect(
      runtime.requests.some((url) =>
        /\/deep\/nested\/av\/assets\/index-[^/]+\.js$/u.test(url)
      )
    ).toBe(true);
    expect(
      runtime.requests.some((url) =>
        /\/deep\/nested\/av\/assets\/index-[^/]+\.css$/u.test(url)
      )
    ).toBe(true);
    expect(
      runtime.requests.some((url) => url.includes('/vendor/ffmpeg/'))
    ).toBe(false);

    await page.getByText('Engine & browser').click();
    await page.getByLabel('Engine mode').selectOption('force-single-thread');
    await page.getByRole('button', { name: 'Initialize engine' }).click();

    await expect(
      page
        .locator('.engine-pill')
        .filter({ hasText: 'Single-threaded compatibility mode' })
    ).toBeVisible({ timeout: 120_000 });
    await expect(page.getByText('0.12.15 / 0.12.10')).toBeVisible();
    await expect(
      page.getByText('Encoders').locator('..').locator('dd')
    ).not.toHaveText('0');
    await expect(
      page.getByText('Filters').locator('..').locator('dd')
    ).not.toHaveText('0');

    const coreScript = runtime.responses.find((response) =>
      response.url.endsWith(
        '/deep/nested/av/vendor/ffmpeg/0.12.10-reviewed-stack5m.1/st/ffmpeg-core.js'
      )
    );
    const coreWasm = runtime.responses.find((response) =>
      response.url.endsWith(
        '/deep/nested/av/vendor/ffmpeg/0.12.10-reviewed-stack5m.1/st/ffmpeg-core.wasm'
      )
    );
    expect(coreScript).toMatchObject({
      status: 200,
      contentType: 'text/javascript; charset=utf-8',
    });
    expect(coreWasm).toMatchObject({
      status: 200,
      contentType: 'application/wasm',
    });
    expect(
      runtime.responses.some((response) =>
        /\/deep\/nested\/av\/assets\/worker-[^/]+\.js$/u.test(response.url)
      )
    ).toBe(true);

    const networkUrls = runtime.requests.filter((url) => /^https?:/u.test(url));
    expect(
      networkUrls.every((url) => new URL(url).origin === nestedServer.origin)
    ).toBe(true);
    expect(runtime.failedRequests).toEqual([]);
    expect(runtime.pageErrors).toEqual([]);
    expect(runtime.consoleErrors).toEqual([]);
  });

  test('rejects an invalid same-origin catalog and remains usable standalone', async ({
    page,
  }) => {
    const runtime = observeRuntime(page);
    await page.goto(`${nestedServer.appUrl}?toolbox=../invalid-catalog.json`);

    await expect
      .poll(() => runtime.consoleWarnings.length, { timeout: 10_000 })
      .toBeGreaterThan(0);
    await expect(page.locator('[data-toolbox-context]')).toHaveAttribute(
      'data-toolbox-context',
      'standalone'
    );
    await expect(
      page.getByRole('button', { name: 'Choose files' })
    ).toBeEnabled();
    expect(
      runtime.consoleWarnings.some((warning) =>
        warning.includes('Toolbox context unavailable; continuing standalone.')
      )
    ).toBe(true);
    expect(
      runtime.requests.some(
        (url) =>
          url === `${nestedServer.origin}/deep/nested/invalid-catalog.json`
      )
    ).toBe(true);
    expect(runtime.failedRequests).toEqual([]);
    expect(runtime.pageErrors).toEqual([]);
    expect(runtime.consoleErrors).toEqual([]);
  });
});
