/**
 * Journey: Analyze/Index — EmptyState branch for completed run with no click data
 *
 * Verifies the UI fix that added an EmptyState component for the case:
 *   latestRun.status === 'completed' && !latestRun.summary?.overall
 *
 * Before the fix this state rendered blank (nothing between the TimeWindowPicker
 * and the page footer). After the fix it renders an EmptyState with the title
 * "No click data for the selected time window".
 *
 * == NOTE on public_id routing (migration 2026_05_27_000001) ==
 *
 * All user-facing routes use ULID public_id, not integer PK. The spec resolves
 * sitePublicId via artisan tinker before the suite runs. The integer siteId from
 * seed-data.json is used only for DB operations (factory create), never in URLs.
 *
 * == States covered ==
 *
 *  A. Happy path — completed run WITH summary.overall → MetricsOverview renders (no regression)
 *  B. Empty-state path — completed run WITHOUT summary.overall → EmptyState renders
 *  C. Page loads without console errors in both states
 */

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

// ---------------------------------------------------------------------------
// PHP execution via temp file (avoids shell-quoting issues with artisan tinker)
// ---------------------------------------------------------------------------

function runPhp(phpCode: string): string {
  const tmpFile = path.join('/tmp', `e2e_analyze_${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 {
    fs.unlinkSync(tmpFile);
  }
}

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

/** Resolves the ULID public_id for a site given its integer PK. */
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 AnalysisRun for the given site WITH summary.overall present.
 * Returns the public_id of the created run.
 */
function seedCompletedRunWithData(siteId: number): string {
  // Remove existing completed runs first to avoid count confusion
  runPhp(`
\\App\\Models\\AnalysisRun::where('site_id', ${siteId})->where('status', 'completed')->each(function ($r) {
  \\App\\Models\\Finding::where('analysis_run_id', $r->id)->delete();
  $r->delete();
});
echo 'cleared';
  `);

  const raw = runPhp(`
$site = \\App\\Models\\Site::find(${siteId});
$run = \\App\\Models\\AnalysisRun::factory()->create([
  'site_id' => $site->id,
  'status' => \\App\\Enums\\JobStatus::Completed,
  'before_start' => now()->subDays(56)->toDateString(),
  'before_end'   => now()->subDays(29)->toDateString(),
  'after_start'  => now()->subDays(28)->toDateString(),
  'after_end'    => now()->subDays(1)->toDateString(),
  'summary' => [
    'overall' => [
      'clicks_after'          => 1234,
      'clicks_delta_pct'      => 5.0,
      'impressions_after'     => 45678,
      'impressions_delta_pct' => 3.0,
      'ctr_after'             => 0.027,
      'ctr_delta_pct'         => 2.0,
      'position_after'        => 12.4,
      'position_delta_pct'    => -1.0,
    ],
  ],
]);
echo $run->public_id;
  `);

  const publicId = raw.trim().split('\n').map((l) => l.trim()).filter(Boolean).pop() ?? '';
  if (!publicId) throw new Error(`seedCompletedRunWithData failed. Raw:\n${raw}`);
  return publicId;
}

/**
 * Create a completed AnalysisRun for the given site WITHOUT summary.overall (null summary).
 * Returns the public_id of the created run.
 */
function seedCompletedRunNoData(siteId: number): string {
  runPhp(`
\\App\\Models\\AnalysisRun::where('site_id', ${siteId})->where('status', 'completed')->each(function ($r) {
  \\App\\Models\\Finding::where('analysis_run_id', $r->id)->delete();
  $r->delete();
});
echo 'cleared';
  `);

  const raw = runPhp(`
$site = \\App\\Models\\Site::find(${siteId});
$run = \\App\\Models\\AnalysisRun::factory()->create([
  'site_id'    => $site->id,
  'status'     => \\App\\Enums\\JobStatus::Completed,
  'before_start' => now()->subDays(56)->toDateString(),
  'before_end'   => now()->subDays(29)->toDateString(),
  'after_start'  => now()->subDays(28)->toDateString(),
  'after_end'    => now()->subDays(1)->toDateString(),
  'summary'    => null,
]);
echo $run->public_id;
  `);

  const publicId = raw.trim().split('\n').map((l) => l.trim()).filter(Boolean).pop() ?? '';
  if (!publicId) throw new Error(`seedCompletedRunNoData failed. Raw:\n${raw}`);
  return publicId;
}

// ---------------------------------------------------------------------------
// Suite
// ---------------------------------------------------------------------------

// Needed for fullyParallel: true — ensures beforeAll fires once for the file.
test.describe.configure({ mode: 'serial' });

test.describe('Analyze/Index — EmptyState branch', () => {
  let sitePublicId: string;
  let seedData: E2ESeedData;

  test.beforeAll(async ({ _browser }) => {
    // Load seed data from .seed-data.json (written by global-setup)
    const seedDataPath = path.join(import.meta.dirname, '..', '.seed-data.json');
    seedData = JSON.parse(fs.readFileSync(seedDataPath, 'utf8')) as E2ESeedData;
    sitePublicId = resolveSitePublicId(seedData.siteId);
  });

  // ─── A. Happy path — completed run WITH summary.overall ─────────────────

  test('A: completed run with click data renders MetricsOverview (no regression)', async ({
    page,
  }) => {
    // Seed a completed run WITH summary.overall
    seedCompletedRunWithData(seedData.siteId);

    const consoleErrors: string[] = [];
    page.on('console', (msg) => {
      if (msg.type() === 'error') consoleErrors.push(msg.text());
    });
    const pageErrors: string[] = [];
    page.on('pageerror', (err) => pageErrors.push(err.message));

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

    // MetricsOverview renders — the four KPI labels are visible
    // Catches: regression where MetricsOverview silently fails to render
    await expect(page.getByText(/clicks/i).first()).toBeVisible();

    // EmptyState must NOT appear when there IS summary data
    // Catches: the new EmptyState branch firing incorrectly (false-positive)
    await expect(
      page.getByText('No click data for the selected time window'),
    ).toBeHidden();

    // No console errors (filter known E2E environment 401s — Sanctum stateful domain
    // mismatch when APP_URL differs from localhost:8000 test base URL, and 419 CSRF
    // mismatches on background analytics/signal fetch() calls — these are environment
    // limitations, not workflow bugs)
    const appErrors = consoleErrors.filter(
      (e) =>
        !e.includes('chrome-extension') &&
        !e.includes('extensions/') &&
        !e.includes('favicon') &&
        !(e.includes('401') && e.includes('Unauthorized')) &&
        !(e.includes('419')),
    );
    expect(
      appErrors,
      `Console errors on happy path:\n${appErrors.map((e) => `  - ${e}`).join('\n')}`,
    ).toHaveLength(0);
    expect(
      pageErrors,
      `Page errors on happy path:\n${pageErrors.map((e) => `  - ${e}`).join('\n')}`,
    ).toHaveLength(0);
  });

  // ─── B. Empty-state path — completed run WITHOUT summary.overall ─────────

  test('B: completed run with NO click data renders EmptyState (not blank)', async ({
    page,
  }) => {
    // Seed a completed run WITHOUT summary.overall
    seedCompletedRunNoData(seedData.siteId);

    const consoleErrors: string[] = [];
    page.on('console', (msg) => {
      if (msg.type() === 'error') consoleErrors.push(msg.text());
    });
    const pageErrors: string[] = [];
    page.on('pageerror', (err) => pageErrors.push(err.message));

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

    // The EmptyState title MUST be visible
    // Catches: the pre-fix blank-render bug — page renders nothing and reads as empty
    await expect(
      page.getByText('No click data for the selected time window'),
    ).toBeVisible();

    // The eyebrow "No data for this window" should also render
    await expect(page.getByText('No data for this window')).toBeVisible();

    // MetricsOverview must NOT render (no summary.overall to display)
    // Catches: regression where both MetricsOverview and EmptyState render simultaneously
    await expect(
      page.getByText(/clicks after/i).first(),
    ).toBeHidden();

    // No console errors (same 401/419 environment filter as happy path)
    const appErrors = consoleErrors.filter(
      (e) =>
        !e.includes('chrome-extension') &&
        !e.includes('extensions/') &&
        !e.includes('favicon') &&
        !(e.includes('401') && e.includes('Unauthorized')) &&
        !(e.includes('419')),
    );
    expect(
      appErrors,
      `Console errors on empty-state path:\n${appErrors.map((e) => `  - ${e}`).join('\n')}`,
    ).toHaveLength(0);
    expect(
      pageErrors,
      `Page errors on empty-state path:\n${pageErrors.map((e) => `  - ${e}`).join('\n')}`,
    ).toHaveLength(0);
  });

  // ─── C. Human success check ──────────────────────────────────────────────

  test('C: EmptyState description instructs the user on next steps', async ({ page }) => {
    // Ensures the EmptyState is actionable, not just a blank error message
    seedCompletedRunNoData(seedData.siteId);

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

    // The description guides the user to the time window picker — demonstrating
    // the human success check: "user knows what to do next, page is not blank"
    await expect(
      page.getByText(/Use the time window picker above/i),
    ).toBeVisible();
  });
});
