/**
 * Responsive overflow / cutoff sweep.
 *
 * Walks the key pages at desktop / tablet / mobile / small-mobile viewports and
 * programmatically flags layout bugs the eye looks for on small screens:
 *   - horizontal overflow (document wider than the viewport → off-screen content,
 *     a horizontal scrollbar),
 *   - individual elements whose right edge runs past the viewport (clipped /
 *     off-screen buttons + text),
 *   - elements overflowing the BOTTOM of their own box (cut-off text).
 * Captures a full-page screenshot per page/viewport for eyeball QC.
 *
 * Runs as `admin` (sees every page). Layout is role-independent, so one broad
 * role surfaces the responsive issues; role-specific nav scoping is covered by
 * role-access.spec.ts.
 */
import { test, expect } from '@playwright/test';
import { mkdirSync } from 'node:fs';
import { join } from 'node:path';

const PORT = 'port-nimara';
const OUT = join(process.cwd(), '.audit', 'responsive');

const ADMIN = { email: 'admin@portnimara.test', pw: 'SuperAdmin12345!' };

const VIEWPORTS = [
  { name: 'desktop', width: 1440, height: 900 },
  { name: 'tablet', width: 820, height: 1180 },
  { name: 'mobile', width: 390, height: 844 },
  { name: 'small', width: 360, height: 740 },
] as const;

const PAGES = [
  'dashboard',
  'clients',
  'interests',
  'inquiries',
  'berths',
  'yachts',
  'companies',
  'reports',
  'reports/financial',
  'documents',
  'expenses',
  'inbox',
  'settings',
  'admin',
  'admin/users',
];

test.describe('Responsive overflow sweep', () => {
  test('admin — every key page at every viewport, flag overflow + cutoff', async ({ page }) => {
    test.setTimeout(600_000);
    mkdirSync(OUT, { recursive: true });

    const res = await page.request.post('/api/auth/sign-in/email', {
      data: { email: ADMIN.email, password: ADMIN.pw },
      headers: { 'content-type': 'application/json' },
    });
    expect(res.ok()).toBeTruthy();

    const findings: string[] = [];

    for (const vp of VIEWPORTS) {
      await page.setViewportSize({ width: vp.width, height: vp.height });
      for (const p of PAGES) {
        const url = `/${PORT}/${p}`;
        const slug = p.replace(/\//g, '_');
        try {
          await page.goto(url, { waitUntil: 'domcontentloaded' });
        } catch {
          findings.push(`NAV-FAIL  ${vp.name.padEnd(7)} ${p}`);
          continue;
        }
        // let layout settle + data paint
        await page.waitForTimeout(1800);

        const report = await page.evaluate((vpWidth) => {
          const docW = document.documentElement.scrollWidth;
          const innerW = window.innerWidth;
          const horizOverflow = docW - innerW;
          // Elements whose right edge runs past the viewport by > 2px and are
          // actually visible (have size, not display:none).
          const offscreen: { tag: string; cls: string; right: number; text: string }[] = [];
          const SVG_INTERNAL = new Set([
            'svg',
            'g',
            'ellipse',
            'path',
            'circle',
            'rect',
            'line',
            'polyline',
            'polygon',
          ]);
          const els = document.querySelectorAll('body *');
          for (const el of els) {
            const tag = el.tagName.toLowerCase();
            // Skip SVG internals (icons, the react-grab dev overlay, chart guts)
            // — not layout-cutoff signal.
            if (SVG_INTERNAL.has(tag)) continue;
            const r = (el as HTMLElement).getBoundingClientRect();
            if (r.width === 0 || r.height === 0) continue;
            if (r.right > vpWidth + 2 && r.left < vpWidth) {
              // Skip elements inside a horizontal-scroll container (data tables
              // etc. scroll on purpose) — that's intended, not a clip.
              let p: HTMLElement | null = el.parentElement;
              let inScroll = false;
              while (p) {
                const ox = getComputedStyle(p).overflowX;
                if (ox === 'auto' || ox === 'scroll') {
                  inScroll = true;
                  break;
                }
                p = p.parentElement;
              }
              if (inScroll) continue;
              const cls = ((el as HTMLElement).className || '').toString().slice(0, 40);
              const text = (el.textContent || '').trim().slice(0, 30);
              offscreen.push({ tag, cls, right: Math.round(r.right), text });
            }
          }
          // de-dupe by tag+text, cap
          const seen = new Set<string>();
          const uniq = offscreen
            .filter((o) => {
              const k = `${o.tag}:${o.text}`;
              if (seen.has(k)) return false;
              seen.add(k);
              return true;
            })
            .slice(0, 6);
          return { horizOverflow, docW, innerW, offscreen: uniq };
        }, vp.width);

        await page
          .screenshot({ path: join(OUT, `admin-${vp.name}-${slug}.png`), fullPage: true })
          .catch(() => {});

        const flagged = report.horizOverflow > 3 || report.offscreen.length > 0;
        const line = `${flagged ? 'OVERFLOW' : 'ok      '} ${vp.name.padEnd(7)} ${p.padEnd(18)} hScroll=${report.horizOverflow}px doc=${report.docW}/${report.innerW}`;
        console.log(line);
        if (report.offscreen.length) {
          for (const o of report.offscreen) {
            console.log(`         ↳ off-right ${o.tag} right=${o.right} "${o.text}" .${o.cls}`);
          }
        }
        if (flagged) findings.push(line);
      }
    }

    console.log(`\n=== OVERFLOW FINDINGS (${findings.length}) ===`);
    for (const f of findings) console.log(f);
  });
});
