import { expect, test, type BrowserContext, type Page } from '@playwright/test';
import { expectNoA11yViolations } from './support/axe';
import { signIn } from './support/cms';

/**
 * Jornada P1 (estrutura como dado): tema, shell do CMS e menu principal editados no painel e
 * refletidos no portal **sem deploy** (ADR-016/ADR-036/ADR-046).
 *
 * Determinismo (ADR-001): cada teste restaura o dado original pela própria UI no `finally`;
 * nada de espera fixa (as asserções esperam o efeito observável). O portal é consultado no mesmo
 * contexto de browser, em página própria — o CMS tem domínio isolado (ADR-005/037).
 *
 * Pré-condição: `php artisan db:seed` (usuário e menu `principal` de referência).
 */

const PORTAL_ORIGIN = (process.env.BASE_URL ?? 'http://localhost:50700').replace(/\/$/, '');

/** Cor válida (oklch, escura o bastante para manter o contraste do texto branco). */
const THEME_COLOR = 'oklch(0.45 0.2 264)';

async function themeStylesheet(page: Page): Promise<{ body: string; etag: string }> {
  const origin = new URL(page.url()).origin;
  const response = await page.request.get(`${origin}/theme.css`);

  expect(response.status(), 'GET /theme.css').toBe(200);

  return { body: await response.text(), etag: response.headers()['etag'] ?? '' };
}

/**
 * O portal publica HTML com `max-age` (ADR-022). Para provar a mudança **nesta** execução, a
 * página é aberta com uma chave de cache única por estado — nunca uma espera fixa.
 */
async function openPortal(context: BrowserContext, state: string): Promise<Page> {
  const portal = await context.newPage();
  const response = await portal.goto(`${PORTAL_ORIGIN}/?e2e=${encodeURIComponent(state)}`);

  expect(response?.status(), 'GET / (portal)').toBe(200);

  return portal;
}

/** Posição relativa de sidebar e `<main>` na **ordem de tabulação** (DOM) do painel. */
async function focusOrder(page: Page): Promise<{ sidebar: number; main: number }> {
  return page.evaluate(() => {
    const focusable = Array.from(
      document.querySelectorAll<HTMLElement>('a[href], button, input, select, textarea'),
    ).filter((element) => !element.hasAttribute('disabled') && element.offsetParent !== null);

    return {
      sidebar: focusable.findIndex((element) => element.closest('#cms-sidebar') !== null),
      main: focusable.findIndex((element) => element.closest('main#cms-content') !== null),
    };
  });
}

test.describe.configure({ mode: 'serial' });

test.describe('CMS — estrutura do site (tema, shell e menus)', () => {
  test('troca um token de tema, publica no /theme.css e reflete no portal', async ({ page, context }) => {
    await signIn(page);

    await page.goto('/configuracoes/theme');
    await expect(page.getByRole('heading', { name: 'Aparência', level: 1 })).toBeVisible();
    await expectNoA11yViolations(page);

    const color = page.locator('#field-color_primary');
    const original = await color.inputValue();
    const before = await themeStylesheet(page);
    let mutated = false;

    try {
      await color.fill(THEME_COLOR);
      mutated = true;
      await page.getByRole('button', { name: 'Salvar' }).click();
      await expect(page.locator('.alert-success')).toContainText('Configurações salvas.');
      await expect(color).toHaveValue(THEME_COLOR);

      const after = await themeStylesheet(page);
      expect(after.body).toContain(`--tenant-primary: ${THEME_COLOR};`);
      expect(after.etag, 'ETag muda quando o token muda (ADR-022)').not.toBe(before.etag);

      const portal = await openPortal(context, `theme-${Date.now()}`);

      try {
        await expect(portal.locator('link[rel="stylesheet"][href$="/theme.css"]')).toHaveCount(1);

        const published = await portal.evaluate(() =>
          getComputedStyle(document.documentElement).getPropertyValue('--tenant-primary').trim(),
        );
        expect(published, 'o portal publica o token salvo no painel').toBe(THEME_COLOR);
        await expectNoA11yViolations(portal);
      } finally {
        await portal.close();
      }

      await expectNoA11yViolations(page);
    } finally {
      await page.goto('/configuracoes/theme');

      if (mutated && (await color.inputValue()) !== original) {
        await color.fill(original);
        await page.getByRole('button', { name: 'Salvar' }).click();
        await expect(page.locator('.alert-success')).toContainText('Configurações salvas.');
        await expect(color).toHaveValue(original);

        const restored = await themeStylesheet(page);
        expect(restored.body).toContain(`--tenant-primary: ${original};`);
        expect(restored.body).not.toContain(THEME_COLOR);
      }
    }
  });

  test('move a navegação do painel para a direita e a ordem de foco acompanha', async ({ page }) => {
    await signIn(page);

    await page.goto('/configuracoes/cms');
    await expect(page.getByRole('heading', { name: 'Painel', level: 1 })).toBeVisible();
    await expectNoA11yViolations(page);

    const position = page.locator('#field-sidebar_position');
    const original = await position.inputValue();
    const target = original === 'right' ? 'left' : 'right';
    let mutated = false;

    try {
      await position.selectOption(target);
      mutated = true;
      await page.getByRole('button', { name: 'Salvar' }).click();
      await expect(page.locator('.alert-success')).toContainText('Configurações salvas.');

      await page.goto('/configuracoes');
      await expect(page.locator('html')).toHaveAttribute('data-cms-sidebar', target);

      const order = await focusOrder(page);
      expect(order.sidebar, 'sidebar focável').toBeGreaterThanOrEqual(0);
      expect(order.main, 'conteúdo focável').toBeGreaterThanOrEqual(0);

      if (target === 'right') {
        expect(order.main, 'com a sidebar à direita, o conteúdo vem antes na ordem de foco').toBeLessThan(
          order.sidebar,
        );
      } else {
        expect(order.sidebar, 'com a sidebar à esquerda, ela vem antes na ordem de foco').toBeLessThan(
          order.main,
        );
      }

      await expectNoA11yViolations(page);
    } finally {
      await page.goto('/configuracoes/cms');

      if (mutated && (await position.inputValue()) !== original) {
        await position.selectOption(original);
        await page.getByRole('button', { name: 'Salvar' }).click();
        await expect(page.locator('.alert-success')).toContainText('Configurações salvas.');

        await page.goto('/configuracoes');
        await expect(page.locator('html')).toHaveAttribute('data-cms-sidebar', original);
      }
    }
  });

  test('edita o rótulo de um item do menu e o portal reflete', async ({ page, context }) => {
    await signIn(page);

    await page.goto('/menus');
    await expect(page.getByRole('heading', { name: 'Menus do site', level: 1 })).toBeVisible();
    await expectNoA11yViolations(page);

    await page
      .locator('tbody tr', { hasText: 'principal' })
      .getByRole('link', { name: 'Gerenciar itens' })
      .click();
    await expect(page.getByRole('heading', { name: 'Itens do menu', level: 1 })).toBeVisible();
    await expectNoA11yViolations(page);

    const itemsUrl = page.url();
    const uniqueLabel = `E2E Menu ${Date.now()}`;
    let originalLabel: string | null = null;
    let edited = false;

    try {
      await page.locator('tbody tr').first().getByRole('link', { name: 'Editar' }).click();
      await expect(page.getByRole('heading', { name: 'Editar item', level: 1 })).toBeVisible();
      await expectNoA11yViolations(page);

      const ptBr = page.locator('#field-label-pt_BR');
      originalLabel = await ptBr.inputValue();
      edited = true;

      await ptBr.fill(uniqueLabel);
      await page.getByRole('button', { name: 'Salvar' }).click();
      await expect(page.locator('.alert-success')).toContainText('Item salvo.');

      const portal = await openPortal(context, `menu-edit-${uniqueLabel}`);

      try {
        const nav = portal.getByRole('navigation', { name: 'Menu principal' });
        await expect(nav.getByText(uniqueLabel, { exact: true })).toBeVisible();
      } finally {
        await portal.close();
      }
    } finally {
      if (edited && originalLabel !== null) {
        await page.goto(`${itemsUrl}`);
        await page.locator('tbody tr').first().getByRole('link', { name: 'Editar' }).click();

        if ((await page.locator('#field-label-pt_BR').inputValue()) !== originalLabel) {
          await page.locator('#field-label-pt_BR').fill(originalLabel);
          await page.getByRole('button', { name: 'Salvar' }).click();
          await expect(page.locator('.alert-success')).toContainText('Item salvo.');
        }

        const portal = await openPortal(context, `menu-restore-${uniqueLabel}`);

        try {
          const nav = portal.getByRole('navigation', { name: 'Menu principal' });
          await expect(nav.getByText(originalLabel, { exact: true })).toBeVisible();
          await expect(nav.getByText(uniqueLabel, { exact: true })).toHaveCount(0);
        } finally {
          await portal.close();
        }
      }
    }
  });

  test('adiciona e remove um item do menu principal sem deixar link morto', async ({ page, context }) => {
    await signIn(page);

    await page.goto('/menus');
    await page
      .locator('tbody tr', { hasText: 'principal' })
      .getByRole('link', { name: 'Gerenciar itens' })
      .click();
    await expect(page.getByRole('heading', { name: 'Itens do menu', level: 1 })).toBeVisible();

    const itemsUrl = page.url();
    const uniqueLabel = `E2E Temporário ${Date.now()}`;
    let created = false;

    try {
      await page.locator('select[name="type"]').selectOption('route');
      await page.locator('#field-route').fill('portal.home');

      for (const locale of ['pt_BR', 'en', 'es', 'fr']) {
        await page.locator(`#field-label-${locale}`).fill(uniqueLabel);
      }

      await page.getByRole('button', { name: 'Adicionar item' }).click();
      await expect(page.locator('.alert-success')).toContainText('Item salvo.');
      await expect(page.locator('tbody tr', { hasText: uniqueLabel })).toHaveCount(1);
      await expectNoA11yViolations(page);
      created = true;

      const portal = await openPortal(context, `menu-add-${uniqueLabel}`);

      try {
        const nav = portal.getByRole('navigation', { name: 'Menu principal' });
        await expect(nav.getByText(uniqueLabel, { exact: true })).toBeVisible();
        await expectNoA11yViolations(portal);
      } finally {
        await portal.close();
      }

      await page.goto(itemsUrl);
      const row = page.locator('tbody tr', { hasText: uniqueLabel });
      const removal = page.waitForResponse(
        (response) =>
          response.request().method() === 'GET' &&
          new URL(response.url()).pathname === new URL(itemsUrl).pathname,
      );

      await row.getByRole('button', { name: 'Remover' }).click();
      expect((await removal).status(), 'remoção de item nunca responde 500').toBe(200);
      await expect(page.locator('.alert-success')).toContainText('Item removido.');
      await expect(page.locator('tbody tr', { hasText: uniqueLabel })).toHaveCount(0);
      created = false;

      const after = await openPortal(context, `menu-removed-${uniqueLabel}`);

      try {
        const nav = after.getByRole('navigation', { name: 'Menu principal' });
        await expect(nav.getByText(uniqueLabel, { exact: true })).toHaveCount(0);
      } finally {
        await after.close();
      }
    } finally {
      if (created) {
        await page.goto(itemsUrl);
        const row = page.locator('tbody tr', { hasText: uniqueLabel });

        if ((await row.count()) > 0) {
          await row.getByRole('button', { name: 'Remover' }).click();
          await expect(page.locator('.alert-success')).toContainText('Item removido.');
        }
      }
    }
  });
});
