/**
 * Mobile Audit Spec
 * -----------------
 * Visits every user-facing page in the app at iPhone 14 Pro and iPhone SE
 * viewports (portrait), takes full-page screenshots to .audit/mobile/*, and
 * writes an index.md grouped by route area.
 *
 * Self-seeds the super-admin via better-auth's REST sign-up endpoint so it
 * does NOT depend on the docker-based smoke setup.
 *
 * Run:  pnpm exec playwright test --project=mobile-audit
 */
import { test, type Page, type APIRequestContext } from '@playwright/test';
import { promises as fs } from 'node:fs';
import path from 'node:path';
import { IPHONE_DEVICES } from '../fixtures/devices';

const PORT_SLUG = 'port-nimara';
const ADMIN = {
  email: 'admin@portnimara.test',
  password: 'SuperAdmin12345!',
  name: 'Test Admin',
};

const OUT_ROOT = path.resolve(process.cwd(), '.audit/mobile');

// Anchor viewports covering the active iPhone range (portrait CSS pixels).
// See docs/superpowers/specs/2026-04-29-mobile-optimization-design.md §2.1.
const VIEWPORTS = [
  { ...IPHONE_DEVICES.iphoneSe, name: 'iphone-se', label: IPHONE_DEVICES.iphoneSe.name },
  { ...IPHONE_DEVICES.iphone16, name: 'iphone-16', label: IPHONE_DEVICES.iphone16.name },
  { ...IPHONE_DEVICES.iphone16Pro, name: 'iphone-16-pro', label: IPHONE_DEVICES.iphone16Pro.name },
  {
    ...IPHONE_DEVICES.iphoneProMax,
    name: 'iphone-pro-max',
    label: IPHONE_DEVICES.iphoneProMax.name,
  },
] as const;

type Auth = 'admin' | 'public';
type Route = {
  group: string;
  slug: string;
  path: string;
  auth: Auth;
  /**
   * If set, after navigating to `path` we click the first matching anchor and
   * screenshot the resulting detail page under the slug `slug + '-detail'`.
   */
  detailLinkSelector?: string;
  /** Extra wait after navigation (ms) for content to settle. */
  settleMs?: number;
  /** If true, skip - useful for known-broken or out-of-scope routes. */
  skip?: boolean;
};

const ROUTES: Route[] = [
  // (auth)
  { group: 'auth', slug: 'auth-login', path: '/login', auth: 'public' },
  { group: 'auth', slug: 'auth-reset-password', path: '/reset-password', auth: 'public' },
  { group: 'auth', slug: 'auth-set-password', path: '/set-password', auth: 'public' },

  // (portal) public
  { group: 'portal', slug: 'portal-login', path: '/portal/login', auth: 'public' },
  { group: 'portal', slug: 'portal-activate', path: '/portal/activate', auth: 'public' },
  {
    group: 'portal',
    slug: 'portal-forgot-password',
    path: '/portal/forgot-password',
    auth: 'public',
  },
  {
    group: 'portal',
    slug: 'portal-reset-password',
    path: '/portal/reset-password',
    auth: 'public',
  },

  // (dashboard)
  { group: 'dashboard', slug: 'dash-port-home', path: `/${PORT_SLUG}`, auth: 'admin' },
  { group: 'dashboard', slug: 'dash-overview', path: `/${PORT_SLUG}/dashboard`, auth: 'admin' },

  // CRUD lists + detail
  {
    group: 'clients',
    slug: 'clients-list',
    path: `/${PORT_SLUG}/clients`,
    auth: 'admin',
    detailLinkSelector: 'a[href*="/clients/"]:not([href$="/clients"])',
  },
  {
    group: 'yachts',
    slug: 'yachts-list',
    path: `/${PORT_SLUG}/yachts`,
    auth: 'admin',
    detailLinkSelector: 'a[href*="/yachts/"]:not([href$="/yachts"])',
  },
  {
    group: 'companies',
    slug: 'companies-list',
    path: `/${PORT_SLUG}/companies`,
    auth: 'admin',
    detailLinkSelector: 'a[href*="/companies/"]:not([href$="/companies"])',
  },
  {
    group: 'berths',
    slug: 'berths-list',
    path: `/${PORT_SLUG}/berths`,
    auth: 'admin',
    detailLinkSelector: 'a[href*="/berths/"]:not([href$="/berths"])',
  },
  {
    group: 'interests',
    slug: 'interests-list',
    path: `/${PORT_SLUG}/interests`,
    auth: 'admin',
  },
  {
    group: 'invoices',
    slug: 'invoices-list',
    path: `/${PORT_SLUG}/invoices`,
    auth: 'admin',
    detailLinkSelector: 'a[href*="/invoices/"]:not([href$="/invoices"]):not([href$="/new"])',
  },
  { group: 'invoices', slug: 'invoices-new', path: `/${PORT_SLUG}/invoices/new`, auth: 'admin' },
  {
    group: 'expenses',
    slug: 'expenses-list',
    path: `/${PORT_SLUG}/expenses`,
    auth: 'admin',
    detailLinkSelector: 'a[href*="/expenses/"]:not([href$="/expenses"]):not([href$="/scan"])',
  },
  { group: 'expenses', slug: 'expenses-scan', path: `/${PORT_SLUG}/expenses/scan`, auth: 'admin' },

  // Cross-cutting features
  { group: 'documents', slug: 'documents', path: `/${PORT_SLUG}/documents`, auth: 'admin' },
  { group: 'email', slug: 'email', path: `/${PORT_SLUG}/email`, auth: 'admin' },
  { group: 'alerts', slug: 'alerts', path: `/${PORT_SLUG}/alerts`, auth: 'admin' },
  { group: 'reports', slug: 'reports', path: `/${PORT_SLUG}/reports`, auth: 'admin' },
  { group: 'reminders', slug: 'reminders', path: `/${PORT_SLUG}/reminders`, auth: 'admin' },
  { group: 'settings', slug: 'settings-user', path: `/${PORT_SLUG}/settings`, auth: 'admin' },

  // Admin
  { group: 'admin', slug: 'admin-home', path: `/${PORT_SLUG}/admin`, auth: 'admin' },
  { group: 'admin', slug: 'admin-settings', path: `/${PORT_SLUG}/admin/settings`, auth: 'admin' },
  { group: 'admin', slug: 'admin-branding', path: `/${PORT_SLUG}/admin/branding`, auth: 'admin' },
  { group: 'admin', slug: 'admin-forms', path: `/${PORT_SLUG}/admin/forms`, auth: 'admin' },
  { group: 'admin', slug: 'admin-ocr', path: `/${PORT_SLUG}/admin/ocr`, auth: 'admin' },
  { group: 'admin', slug: 'admin-roles', path: `/${PORT_SLUG}/admin/roles`, auth: 'admin' },
  { group: 'admin', slug: 'admin-tags', path: `/${PORT_SLUG}/admin/tags`, auth: 'admin' },
  { group: 'admin', slug: 'admin-audit', path: `/${PORT_SLUG}/admin/audit`, auth: 'admin' },
  { group: 'admin', slug: 'admin-documenso', path: `/${PORT_SLUG}/admin/documenso`, auth: 'admin' },
  { group: 'admin', slug: 'admin-users', path: `/${PORT_SLUG}/admin/users`, auth: 'admin' },
  { group: 'admin', slug: 'admin-templates', path: `/${PORT_SLUG}/admin/templates`, auth: 'admin' },
  {
    group: 'admin',
    slug: 'admin-custom-fields',
    path: `/${PORT_SLUG}/admin/custom-fields`,
    auth: 'admin',
  },
  {
    group: 'admin',
    slug: 'admin-monitoring',
    path: `/${PORT_SLUG}/admin/monitoring`,
    auth: 'admin',
  },
  { group: 'admin', slug: 'admin-backup', path: `/${PORT_SLUG}/admin/backup`, auth: 'admin' },
  { group: 'admin', slug: 'admin-webhooks', path: `/${PORT_SLUG}/admin/webhooks`, auth: 'admin' },
  { group: 'admin', slug: 'admin-import', path: `/${PORT_SLUG}/admin/import`, auth: 'admin' },
  { group: 'admin', slug: 'admin-ports', path: `/${PORT_SLUG}/admin/ports`, auth: 'admin' },

  // Scanner PWA
  { group: 'scanner', slug: 'scanner-scan', path: `/${PORT_SLUG}/scan`, auth: 'admin' },
];

type Capture = {
  group: string;
  slug: string;
  path: string;
  file: string;
  status: 'ok' | 'error';
  error?: string;
};

async function ensureAdminExists(request: APIRequestContext) {
  const headers = {
    'Content-Type': 'application/json',
    Origin: 'http://localhost:3000',
    Referer: 'http://localhost:3000/',
  };
  const signUp = await request.post('/api/auth/sign-up/email', {
    headers,
    data: { email: ADMIN.email, password: ADMIN.password, name: ADMIN.name },
    failOnStatusCode: false,
  });
  if (!signUp.ok()) {
    // Already exists - verify sign-in works
    const signIn = await request.post('/api/auth/sign-in/email', {
      headers,
      data: { email: ADMIN.email, password: ADMIN.password },
      failOnStatusCode: false,
    });
    if (!signIn.ok()) {
      const body = await signIn.text();
      throw new Error(`Cannot sign in admin: ${signIn.status()} ${body}`);
    }
  }
}

async function loginThroughUI(page: Page) {
  await page.goto('/login');
  await page.fill('input[type="email"], input[name="email"]', ADMIN.email);
  await page.fill('input[type="password"], input[name="password"]', ADMIN.password);
  await page.click('button[type="submit"]');
  // Better-auth redirect can land on `/` or `/[portSlug]` depending on user_port_roles.
  await page.waitForLoadState('networkidle', { timeout: 15_000 }).catch(() => {});
}

async function captureRoute(
  page: Page,
  route: Route,
  outDir: string,
  captures: Capture[],
): Promise<void> {
  if (route.skip) return;

  // Main capture
  const mainFile = path.join(outDir, `${route.slug}.png`);
  try {
    await page.goto(route.path, { waitUntil: 'domcontentloaded' });
    await page.waitForLoadState('networkidle', { timeout: 8000 }).catch(() => {});
    await page.waitForTimeout(route.settleMs ?? 600);
    await page.screenshot({ path: mainFile, fullPage: true });
    captures.push({
      group: route.group,
      slug: route.slug,
      path: route.path,
      file: mainFile,
      status: 'ok',
    });
    console.log(`  ✓ ${route.path}`);
  } catch (err) {
    const msg = err instanceof Error ? err.message : String(err);
    captures.push({
      group: route.group,
      slug: route.slug,
      path: route.path,
      file: mainFile,
      status: 'error',
      error: msg.split('\n')[0],
    });
    console.log(`  ✗ ${route.path} - ${msg.split('\n')[0]}`);
    return;
  }

  // Detail capture (optional)
  if (route.detailLinkSelector) {
    const detailSlug = `${route.slug.replace(/-list$/, '')}-detail`;
    const detailFile = path.join(outDir, `${detailSlug}.png`);
    try {
      const link = page.locator(route.detailLinkSelector).first();
      const count = await link.count();
      if (count === 0) {
        captures.push({
          group: route.group,
          slug: detailSlug,
          path: `${route.path} → (no rows seeded)`,
          file: detailFile,
          status: 'error',
          error: 'No detail rows present in list',
        });
        console.log(`  – ${route.path} detail skipped (empty list)`);
        return;
      }
      const href = await link.getAttribute('href');
      const target = href ?? route.path;
      await page.goto(target, { waitUntil: 'domcontentloaded' });
      await page.waitForLoadState('networkidle', { timeout: 8000 }).catch(() => {});
      await page.waitForTimeout(700);
      await page.screenshot({ path: detailFile, fullPage: true });
      captures.push({
        group: route.group,
        slug: detailSlug,
        path: target,
        file: detailFile,
        status: 'ok',
      });
      console.log(`  ✓ ${target} (detail)`);
    } catch (err) {
      const msg = err instanceof Error ? err.message : String(err);
      captures.push({
        group: route.group,
        slug: detailSlug,
        path: `${route.path} → detail`,
        file: detailFile,
        status: 'error',
        error: msg.split('\n')[0],
      });
      console.log(`  ✗ ${route.path} detail - ${msg.split('\n')[0]}`);
    }
  }
}

async function writeIndex(allByViewport: Map<string, Capture[]>): Promise<void> {
  const lines: string[] = [
    '# Mobile Audit',
    '',
    `Generated: ${new Date().toISOString()}`,
    '',
    'Captured at iPhone 14 Pro (393×852) and iPhone SE 3 (375×667), portrait, full-page.',
    '',
  ];

  for (const [vpName, captures] of allByViewport) {
    lines.push(`## ${vpName}`, '');

    const grouped = new Map<string, Capture[]>();
    for (const c of captures) {
      if (!grouped.has(c.group)) grouped.set(c.group, []);
      grouped.get(c.group)!.push(c);
    }

    for (const [group, items] of grouped) {
      lines.push(`### ${group}`, '');
      lines.push('| route | shot |');
      lines.push('| --- | --- |');
      for (const c of items) {
        const rel = path.relative(OUT_ROOT, c.file);
        if (c.status === 'ok') {
          lines.push(`| \`${c.path}\` | ![${c.slug}](${rel}) |`);
        } else {
          lines.push(`| \`${c.path}\` | _error: ${c.error}_ |`);
        }
      }
      lines.push('');
    }
  }

  await fs.writeFile(path.join(OUT_ROOT, 'index.md'), lines.join('\n'), 'utf-8');
}

test('mobile audit - every page at iPhone viewports', async ({ browser, request }) => {
  test.setTimeout(1_800_000);

  await fs.mkdir(OUT_ROOT, { recursive: true });
  await ensureAdminExists(request);

  const allByViewport = new Map<string, Capture[]>();

  for (const vp of VIEWPORTS) {
    console.log(`\n─── Viewport: ${vp.label} ───`);
    const outDir = path.join(OUT_ROOT, vp.name);
    await fs.mkdir(outDir, { recursive: true });

    const context = await browser.newContext({
      viewport: vp.viewport,
      deviceScaleFactor: vp.deviceScaleFactor,
      isMobile: vp.isMobile,
      hasTouch: vp.hasTouch,
      userAgent: vp.userAgent,
    });
    const page = await context.newPage();

    const captures: Capture[] = [];

    // Public pages first (no auth state)
    for (const route of ROUTES.filter((r) => r.auth === 'public')) {
      await captureRoute(page, route, outDir, captures);
    }

    // Sign in once via UI for authenticated pages
    await loginThroughUI(page);

    for (const route of ROUTES.filter((r) => r.auth === 'admin')) {
      await captureRoute(page, route, outDir, captures);
    }

    allByViewport.set(vp.label, captures);
    await context.close();
  }

  await writeIndex(allByViewport);
  console.log(`\nIndex written to ${path.join(OUT_ROOT, 'index.md')}`);
});
