/**
 * Workflow spec: Report PDF error-state UI (session f0b3b8c2)
 *
 * Covers the new conditional branch in resources/js/Pages/Reports/Preview.tsx
 * introduced in BHUNT-ERR-02:
 *
 *   Branch A (pdf_failed_at set, has_pdf false):
 *     → AlertCircle icon + destructive-styled button
 *       "PDF generation failed — try again"
 *
 *   Branch B (pdf_failed_at null, has_pdf false):
 *     → Normal Download icon + "Export to PDF" button
 *
 * Golden path:
 *   1. Navigate to completed report with pdf_failed_at=now() and has_pdf=false
 *   2. Assert "PDF generation failed — try again" button is visible
 *   3. Assert "Export to PDF" is NOT visible (mutually exclusive)
 *   4. Navigate to completed report with pdf_failed_at=null and has_pdf=false
 *   5. Assert "Export to PDF" is visible
 *   6. Assert "PDF generation failed — try again" is NOT visible
 *
 * Sad paths:
 *   - No uncaught console errors on either branch
 *   - No failed XHR (4xx/5xx) on either branch
 *
 * Background noise filter (same as report-preview-shared-4bc6660c.spec.ts):
 *   /api/lead-score/pql-signal — 419 CSRF (expected, ignorable)
 *   ERR_TOO_MANY_REDIRECTS    — polling XHRs when APP_URL is HTTPS (expected)
 */

import { execSync } from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import { fileURLToPath } from 'url';
import { expect, test } from '../fixtures/auth';

// ESM-compatible __dirname
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// ---------------------------------------------------------------------------
// PHP tinker helpers (same pattern as report-preview-shared-4bc6660c.spec.ts)
// ---------------------------------------------------------------------------

function runPhp(phpCode: string): string {
  const tmpFile = path.join('/tmp', `e2e_rep_pdf_${process.pid}_${Date.now()}.php`);
  try {
    fs.writeFileSync(tmpFile, phpCode, 'utf8');
    return execSync(`php artisan tinker --execute="$(cat ${tmpFile})" 2>&1`, {
      cwd: process.cwd(),
      encoding: 'utf8',
      timeout: 30_000,
      shell: '/bin/bash',
    });
  } finally {
    try { fs.unlinkSync(tmpFile); } catch { /* ignore */ }
  }
}

function resolveSitePublicId(siteId: number): string {
  const raw = runPhp(
    `$s = \\App\\Models\\Site::find(${siteId}); echo $s ? $s->public_id : 'NOT_FOUND';`,
  );
  const result = raw.trim().split('\n').map((l) => l.trim()).filter(Boolean).pop() ?? '';
  if (!result || result === 'NOT_FOUND') {
    throw new Error(`resolveSitePublicId(${siteId}) failed. Raw:\n${raw}`);
  }
  return result;
}

/**
 * Create a completed report with pdf_failed_at=now() and pdf_path=null (PDF failed state).
 * Returns the public_id.
 */
function createPdfFailedReport(siteId: number, userId: number): string {
  const raw = runPhp(`
    $r = \\App\\Models\\Report::factory()->completed()->create([
      'site_id'      => ${siteId},
      'user_id'      => ${userId},
      'name'         => 'E2E PDF Failed Report',
      'pdf_path'     => null,
      'pdf_failed_at'=> now(),
    ]);
    echo $r->public_id;
  `);
  const id = raw.trim().split('\n').map((l) => l.trim()).filter(Boolean).pop() ?? '';
  if (!id) throw new Error(`createPdfFailedReport failed. Raw:\n${raw}`);
  return id;
}

/**
 * Create a completed report with pdf_failed_at=null and pdf_path=null (Export to PDF state).
 * Returns the public_id.
 */
function createPdfReadyToExportReport(siteId: number, userId: number): string {
  const raw = runPhp(`
    $r = \\App\\Models\\Report::factory()->completed()->create([
      'site_id'      => ${siteId},
      'user_id'      => ${userId},
      'name'         => 'E2E PDF Ready to Export',
      'pdf_path'     => null,
      'pdf_failed_at'=> null,
    ]);
    echo $r->public_id;
  `);
  const id = raw.trim().split('\n').map((l) => l.trim()).filter(Boolean).pop() ?? '';
  if (!id) throw new Error(`createPdfReadyToExportReport failed. Raw:\n${raw}`);
  return id;
}

function cleanupReport(publicId: string): void {
  runPhp(`
    $r = \\App\\Models\\Report::where('public_id', '${publicId}')->first();
    if ($r) { $r->sharedLinks()->delete(); $r->delete(); }
    echo 'DONE';
  `);
}

// ---------------------------------------------------------------------------
// Background noise filter
// ---------------------------------------------------------------------------

const NOISE_PATTERNS = [
  '/api/lead-score/pql-signal',
  'ERR_TOO_MANY_REDIRECTS',
  'ERR_ABORTED',
  'Failed to load resource: net::ERR_',
  'Non-Error promise rejection captured',
  '[blocked] The page at',
  'Cross-Origin',
];

function isNoise(msg: string): boolean {
  return NOISE_PATTERNS.some((p) => msg.includes(p));
}

// ===========================================================================
// Tests
// ===========================================================================

test.describe('Report PDF error-state UI (BHUNT-ERR-02)', () => {
  let sitePublicId: string;
  let siteId: number;
  let userId: number;

  test.beforeAll(async () => {
    const seedData = (() => {
      const p = path.join(__dirname, '..', '.seed-data.json');
      return JSON.parse(fs.readFileSync(p, 'utf8')) as {
        siteId: number;
        userId: number;
      };
    })();
    siteId = seedData.siteId;
    userId = seedData.userId;
    sitePublicId = resolveSitePublicId(siteId);
  });

  test('Branch A — pdf_failed_at set shows destructive error button, not Export to PDF', async ({
    page,
  }) => {
    const consoleErrors: string[] = [];
    const failedXhr: string[] = [];

    page.on('console', (msg) => {
      if (msg.type() === 'error' && !isNoise(msg.text())) {
        consoleErrors.push(msg.text());
      }
    });
    page.on('pageerror', (err) => {
      if (!isNoise(err.message)) consoleErrors.push(`UNCAUGHT: ${err.message}`);
    });
    page.on('response', (resp) => {
      const url = resp.url();
      const status = resp.status();
      // Only track XHR/fetch (non-navigation) responses with 4xx/5xx
      if (
        status >= 400 &&
        !url.includes('/api/lead-score/pql-signal') &&
        !url.includes('__webpack')
      ) {
        failedXhr.push(`${status} ${url}`);
      }
    });

    const reportId = createPdfFailedReport(siteId, userId);

    try {
      await page.goto(`/sites/${sitePublicId}/reports/${reportId}`, {
        waitUntil: 'networkidle',
      });

      // Confirm the page loaded the completed report
      await expect(page.getByText('Report Ready')).toBeVisible();

      // Branch A: error affordance MUST be visible
      await expect(
        page.getByRole('button', { name: /PDF generation failed — try again/i }),
      ).toBeVisible();

      // Branch A: normal export button MUST NOT be present (mutually exclusive)
      await expect(
        page.getByRole('button', { name: /^Export to PDF$/i }),
      ).not.toBeVisible();

      // No uncaught console errors
      expect(consoleErrors, 'uncaught console errors on Branch A').toEqual([]);

      // No 4xx/5xx XHR on initial load
      expect(failedXhr, 'failed XHR responses on Branch A').toEqual([]);
    } finally {
      cleanupReport(reportId);
    }
  });

  test('Branch B — pdf_failed_at null shows Export to PDF, not error button', async ({
    page,
  }) => {
    const consoleErrors: string[] = [];
    const failedXhr: string[] = [];

    page.on('console', (msg) => {
      if (msg.type() === 'error' && !isNoise(msg.text())) {
        consoleErrors.push(msg.text());
      }
    });
    page.on('pageerror', (err) => {
      if (!isNoise(err.message)) consoleErrors.push(`UNCAUGHT: ${err.message}`);
    });
    page.on('response', (resp) => {
      const url = resp.url();
      const status = resp.status();
      if (
        status >= 400 &&
        !url.includes('/api/lead-score/pql-signal') &&
        !url.includes('__webpack')
      ) {
        failedXhr.push(`${status} ${url}`);
      }
    });

    const reportId = createPdfReadyToExportReport(siteId, userId);

    try {
      await page.goto(`/sites/${sitePublicId}/reports/${reportId}`, {
        waitUntil: 'networkidle',
      });

      // Confirm the page loaded the completed report
      await expect(page.getByText('Report Ready')).toBeVisible();

      // Branch B: normal export button MUST be visible
      await expect(
        page.getByRole('button', { name: /^Export to PDF$/i }),
      ).toBeVisible();

      // Branch B: error affordance MUST NOT be present
      await expect(
        page.getByRole('button', { name: /PDF generation failed — try again/i }),
      ).not.toBeVisible();

      // No uncaught console errors
      expect(consoleErrors, 'uncaught console errors on Branch B').toEqual([]);

      // No 4xx/5xx XHR on initial load
      expect(failedXhr, 'failed XHR responses on Branch B').toEqual([]);
    } finally {
      cleanupReport(reportId);
    }
  });

  test('Branch A human-success check — error button is the only PDF-export affordance', async ({
    page,
  }) => {
    // Human success = "A user whose PDF export has permanently failed sees an
    // unambiguous error message on the report page, not a misleading 'Export to PDF'
    // button. They can retry from the error button directly."
    const reportId = createPdfFailedReport(siteId, userId);

    try {
      await page.goto(`/sites/${sitePublicId}/reports/${reportId}`, {
        waitUntil: 'networkidle',
      });

      await expect(page.getByText('Report Ready')).toBeVisible();

      // Error button is visible and actionable
      const errorBtn = page.getByRole('button', { name: /PDF generation failed — try again/i });
      await expect(errorBtn).toBeVisible();
      await expect(errorBtn).toBeEnabled();

      // Download PDF button is not present (report has no PDF yet)
      await expect(page.getByRole('button', { name: /^Download PDF$/i })).not.toBeVisible();

      // "Export to PDF" text is absent — no silent fall-through to the normal branch
      await expect(page.getByRole('button', { name: /^Export to PDF$/i })).not.toBeVisible();
    } finally {
      cleanupReport(reportId);
    }
  });
});
