import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { test, expect, type Page } from '@playwright/test';
import { loginWithOtp } from './helpers/auth';
import {
  projectMockStore,
  registerProjectApiMocks,
  resetProjectMockStore,
} from './helpers/project-mocks';

/**
 * Release Candidate QA — evidence collection (no new features).
 *
 * Captures, for the Agile workspace surfaces:
 *  - browser console errors + uncaught page errors
 *  - failed API responses (status >= 400 on /api/ requests)
 *  - responsive screenshots at 1440 / 768 / 390
 *  - dark-mode screenshots for charts, cards, tabs, and a modal
 *  - permission gating for viewer / member(agent) / manager / owner
 */

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const SHOT_DIR = path.resolve(__dirname, '../../../../../docs/uat-screenshots');
const RESPONSIVE_DIR = path.join(SHOT_DIR, 'responsive');
const DARK_DIR = path.join(SHOT_DIR, 'dark');

const pid = projectMockStore.projectId;

const SURFACES: Array<[string, string]> = [
  ['overview', `/projects/${pid}`],
  ['executive', `/projects/${pid}/executive`],
  ['backlog', `/projects/${pid}/backlog`],
  ['sprints', `/projects/${pid}/sprints`],
  ['sprint-planning', `/projects/${pid}/sprint-planning`],
  ['board', `/projects/${pid}/board`],
  ['capacity', `/projects/${pid}/capacity`],
  ['gantt', `/projects/${pid}/gantt`],
  ['risks', `/projects/${pid}/risks`],
  ['dependencies', `/projects/${pid}/dependencies`],
  ['milestones', `/projects/${pid}/milestones`],
  ['reports', `/projects/${pid}/reports`],
  ['report-velocity', `/projects/${pid}/reports/velocity`],
  ['settings', `/projects/${pid}/settings`],
];

// Known browser noise that is unrelated to the Agile module. Documented in the
// RC QA report so the assertion stays honest about what is filtered.
const CONSOLE_NOISE = [/favicon/i, /ResizeObserver loop/i, /React DevTools/i];

interface Capture {
  consoleErrors: string[];
  pageErrors: string[];
  failedApi: string[];
}

function attachCapture(page: Page): Capture {
  const cap: Capture = { consoleErrors: [], pageErrors: [], failedApi: [] };
  page.on('console', (msg) => {
    if (msg.type() !== 'error') return;
    const text = msg.text();
    if (CONSOLE_NOISE.some((re) => re.test(text))) return;
    cap.consoleErrors.push(text);
  });
  page.on('pageerror', (err) => cap.pageErrors.push(err.message));
  page.on('response', (res) => {
    const url = res.url();
    const status = res.status();
    if (status >= 400 && /\/api\//.test(url)) {
      cap.failedApi.push(`${status} ${res.request().method()} ${url}`);
    }
  });
  return cap;
}

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

test.describe('Release Candidate QA (seeded, mocked API)', () => {
  test.beforeAll(() => {
    resetProjectMockStore();
    projectMockStore.projectName = 'UAT Enterprise Agile';
    projectMockStore.seedUatData();
  });

  test.beforeEach(async ({ page }) => {
    await registerProjectApiMocks(page);
    await loginWithOtp(page);
  });

  test('no console errors / no failed API responses across all surfaces', async ({ page }) => {
    const cap = attachCapture(page);

    for (const [, route] of SURFACES) {
      await page.goto(route);
      await page.waitForLoadState('networkidle');
      await page.waitForTimeout(300);
    }

    // Exercise interactive surfaces that mount charts/modals/tabs.
    await page.goto(`/projects/${pid}/risks`);
    await page.waitForLoadState('networkidle');
    for (const tab of ['Heatmap', 'Distribution', 'Register', 'Risk Matrix']) {
      const btn = page.getByRole('button', { name: new RegExp(`^${tab}$`, 'i') });
      if (await btn.isVisible().catch(() => false)) {
        await btn.click();
        await page.waitForTimeout(200);
      }
    }
    const moreToggle = page.getByRole('button', { name: /more analytics/i });
    await page.goto(`/projects/${pid}/reports`);
    await page.waitForLoadState('networkidle');
    if (await moreToggle.isVisible().catch(() => false)) {
      await moreToggle.click();
      await page.waitForTimeout(200);
    }

    expect(cap.pageErrors, `uncaught page errors:\n${cap.pageErrors.join('\n')}`).toEqual([]);
    expect(cap.consoleErrors, `console errors:\n${cap.consoleErrors.join('\n')}`).toEqual([]);
    expect(cap.failedApi, `failed API responses:\n${cap.failedApi.join('\n')}`).toEqual([]);
  });

  const RESPONSIVE_SURFACES: Array<[string, string]> = [
    ['executive', `/projects/${pid}/executive`],
    ['backlog', `/projects/${pid}/backlog`],
    ['risks', `/projects/${pid}/risks`],
    ['reports', `/projects/${pid}/reports`],
    ['gantt', `/projects/${pid}/gantt`],
    ['settings', `/projects/${pid}/settings`],
  ];

  for (const [width, height, label] of [
    [1440, 900, '1440-desktop'],
    [768, 1024, '768-tablet'],
    [390, 844, '390-mobile'],
  ] as Array<[number, number, string]>) {
    test(`responsive @ ${label}`, async ({ page }) => {
      await page.setViewportSize({ width, height });
      for (const [name, route] of RESPONSIVE_SURFACES) {
        await page.goto(route);
        await page.waitForLoadState('networkidle');
        await page.waitForTimeout(400);
        await page.screenshot({
          path: path.join(RESPONSIVE_DIR, `${label}-${name}.png`),
          fullPage: true,
        });
      }
    });
  }

  test('dark mode — charts, cards, tabs, modal', async ({ page }) => {
    await page.goto(`/projects/${pid}/executive`);
    await page.waitForLoadState('networkidle');
    const themeToggle = page.getByRole('button', { name: /toggle theme/i });
    await themeToggle.click();
    await page.waitForTimeout(400);
    await page.screenshot({ path: path.join(DARK_DIR, 'dark-executive.png'), fullPage: true });

    await page.goto(`/projects/${pid}/reports`);
    await page.waitForLoadState('networkidle');
    await page.waitForTimeout(400);
    await page.screenshot({ path: path.join(DARK_DIR, 'dark-reports.png'), fullPage: true });

    await page.goto(`/projects/${pid}/risks`);
    await page.waitForLoadState('networkidle');
    await page.waitForTimeout(400);
    await page.screenshot({ path: path.join(DARK_DIR, 'dark-risks-matrix.png'), fullPage: true });

    await page.goto(`/projects/${pid}/settings`);
    await page.waitForLoadState('networkidle');
    for (const tab of ['Workflow', 'Archive']) {
      const btn = page.getByRole('tab', { name: new RegExp(`^${tab}$`, 'i') });
      if (await btn.isVisible().catch(() => false)) await btn.click();
    }
    await page.waitForTimeout(300);
    await page.screenshot({ path: path.join(DARK_DIR, 'dark-settings-tabs.png'), fullPage: true });

    // Modal in dark mode.
    await page.goto(`/projects/${pid}/milestones`);
    await page.waitForLoadState('networkidle');
    await page.getByRole('button', { name: /add milestone/i }).first().click();
    await expect(page.getByRole('dialog')).toBeVisible();
    await page.waitForTimeout(300);
    await page.screenshot({ path: path.join(DARK_DIR, 'dark-modal.png'), fullPage: true });
  });

  test('arabic localization — changed labels render in AR', async ({ page }) => {
    await page.addInitScript(() => window.localStorage.setItem('locale', 'ar'));

    await page.goto(`/projects/${pid}/reports`);
    await page.waitForLoadState('networkidle');
    await page.waitForTimeout(400);
    await expect(page.getByText('مركز التحليلات').first()).toBeVisible();
    await page.screenshot({ path: path.join(SHOT_DIR, 'i18n', 'ar-analytics-center.png'), fullPage: true });

    await page.goto(`/projects/${pid}/executive`);
    await page.waitForLoadState('networkidle');
    await page.waitForTimeout(400);
    await expect(page.getByText('تسليم السبرنت').first()).toBeVisible();
    await page.screenshot({ path: path.join(SHOT_DIR, 'i18n', 'ar-executive.png'), fullPage: true });

    await page.goto(`/projects/${pid}/settings`);
    await page.waitForLoadState('networkidle');
    await page.waitForTimeout(400);
    await expect(page.getByRole('button', { name: 'حفظ التفاصيل' }).first()).toBeVisible();
    await page.screenshot({ path: path.join(SHOT_DIR, 'i18n', 'ar-settings.png'), fullPage: true });
  });

  test('permissions — viewer is read-only on backlog and settings', async ({ page }) => {
    projectMockStore.permissions = {
      role: 'viewer',
      can_manage: false,
      can_update_work_items: false,
      can_plan_capacity: false,
    };
    await page.goto(`/projects/${pid}/backlog`);
    await page.waitForLoadState('networkidle');
    await expect(page.getByRole('button', { name: /create backlog item/i })).toHaveCount(0);
    await expect(page.getByRole('button', { name: /create epic/i })).toHaveCount(0);

    await page.goto(`/projects/${pid}/settings`);
    await page.waitForLoadState('networkidle');
    await expect(page.getByRole('button', { name: /save details/i })).toHaveCount(0);
  });

  test('permissions — member can edit work items but not manage settings', async ({ page }) => {
    projectMockStore.permissions = {
      role: 'agent',
      can_manage: false,
      can_update_work_items: true,
      can_plan_capacity: false,
    };
    await page.goto(`/projects/${pid}/backlog`);
    await page.waitForLoadState('networkidle');
    await expect(page.getByRole('button', { name: /create backlog item/i })).toBeVisible();

    await page.goto(`/projects/${pid}/settings`);
    await page.waitForLoadState('networkidle');
    await expect(page.getByRole('button', { name: /save details/i })).toHaveCount(0);
  });

  test('permissions — manager can manage settings', async ({ page }) => {
    projectMockStore.permissions = {
      role: 'manager',
      can_manage: true,
      can_update_work_items: true,
      can_plan_capacity: true,
    };
    await page.goto(`/projects/${pid}/backlog`);
    await page.waitForLoadState('networkidle');
    await expect(page.getByRole('button', { name: /create backlog item/i })).toBeVisible();

    await page.goto(`/projects/${pid}/settings`);
    await page.waitForLoadState('networkidle');
    await expect(page.getByRole('button', { name: /save details/i })).toBeVisible();
  });

  test('permissions — owner/admin has full control incl. archive tab', async ({ page }) => {
    projectMockStore.permissions = {
      role: 'owner',
      can_manage: true,
      can_update_work_items: true,
      can_plan_capacity: true,
    };
    await page.goto(`/projects/${pid}/settings`);
    await page.waitForLoadState('networkidle');
    await expect(page.getByRole('button', { name: /save details/i })).toBeVisible();
    await expect(page.getByRole('tab', { name: /^archive$/i })).toBeVisible();
  });
});
