/**
 * Session e931ce1e: WhyThisRewrite draft_evidence golden-path E2E spec
 *
 * Verifies (browser layer, closing the gap that Vitest component tests cannot):
 *   - Golden path: WhyThisRewrite panel renders with "Top evidence" factor rows
 *     for a draftable recommendation that has NO draft yet (has_draft=false).
 *   - Regression guard: panel is correctly absent when has_draft=true (seeded rec).
 *   - Zero uncaught console errors / page exceptions on both paths.
 *
 * Design: creates a transient no-draft recommendation in beforeAll (linked to the
 * seeded site/analysis-run) and deletes it in afterAll. The seeded recommendation
 * (has_draft=true) is left unmodified so other specs remain unaffected.
 *
 * Visibility gate in Show.tsx:
 *   isDraftableAction(recommendation.action_type) && !recommendation.has_draft
 *
 * Controller logic (RecommendationController::show):
 *   draft_evidence = (action is draftable && !hasDraft)
 *     ? DraftEvidencePayloadBuilder::build(...)
 *     : null
 */

import * as fs from 'fs';
import * as path from 'path';
import { fileURLToPath } from 'url';
import { execSync } from 'child_process';

import { expect, test } from '../fixtures/auth';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

// ID of the transient no-draft recommendation created in beforeAll.
// Remains null if creation fails — golden-path test skips gracefully.
let noDraftRecId: number | null = null;

/** Write PHP lines to a temp file and pipe them through artisan tinker. */
function runTinker(phpLines: string[]): string {
  const tmpFile = path.join('/tmp', `e2e_wtr_${Date.now()}.php`);
  fs.writeFileSync(tmpFile, phpLines.join('\n'));
  try {
    return execSync(`php artisan tinker < "${tmpFile}" 2>&1`, {
      cwd: process.cwd(),
      encoding: 'utf8',
      timeout: 30_000,
    });
  } finally {
    try {
      fs.unlinkSync(tmpFile);
    } catch {
      // ignore temp-file cleanup errors
    }
  }
}

/** Collect only application-originating console errors (exclude known-noisy externals). */
function isAppConsoleError(text: string): boolean {
  return (
    !text.includes('extension') &&
    !text.includes('chrome-extension') &&
    !text.includes('moz-extension') &&
    !text.includes('lead-score') &&
    !text.includes('419')
  );
}

test.describe('WhyThisRewrite — draft_evidence golden path (session e931ce1e)', () => {
  test.beforeAll(async () => {
    // Read the seed data written by global-setup.ts after `php artisan e2e:seed --reset`.
    // The spec is not responsible for running the seed — Playwright's globalSetup handles it.
    const seedPath = path.join(__dirname, '..', '.seed-data.json');
    const seed = JSON.parse(fs.readFileSync(seedPath, 'utf8')) as {
      analysisRunId: number;
    };
    const { analysisRunId } = seed;

    // Create a content_rewrite recommendation WITHOUT an associated AiDraft.
    // This ensures has_draft=false so the WhyThisRewrite panel renders on Show.
    // The `evidence` blob carries the three keys in TOP_FACTOR_KEYS that produce
    // visible factor rows (clicks_before, clicks_after, delta_percent).
    const createLines = [
      '$rec = \\App\\Models\\Recommendation::create([',
      "    'analysis_run_id' => " + String(analysisRunId) + ',',
      "    'finding_id' => null,",
      "    'page_url' => 'https://e2e-test.example.com/no-draft-e2e',",
      "    'action_type' => \\App\\Enums\\ActionType::ContentRewrite,",
      "    'title' => 'E2E Why-This-Rewrite No-Draft Test',",
      "    'reasoning' => 'E2E test record for draft_evidence verification.',",
      "    'impact_score' => 72,",
      "    'confidence_score' => 81,",
      "    'evidence' => ['clicks_before' => 120, 'clicks_after' => 75, 'delta_percent' => -37.5],",
      "    'lifecycle_status' => \\App\\Enums\\LifecycleStatus::Pending,",
      ']);',
      "echo json_encode(['id' => $rec->id]);",
      'exit;',
    ];

    try {
      const output = runTinker(createLines);
      const jsonLine = output
        .split('\n')
        .reverse()
        .find((l) => l.trim().startsWith('{'));
      if (jsonLine) {
        noDraftRecId = (JSON.parse(jsonLine) as { id: number }).id;
      }
    } catch (err) {
      console.warn('[beforeAll] Failed to create no-draft recommendation:', err);
    }
  });

  test.afterAll(async () => {
    if (noDraftRecId !== null) {
      try {
        runTinker([
          '\\App\\Models\\Recommendation::find(' + String(noDraftRecId) + ')?->delete();',
          "echo 'cleaned';",
          'exit;',
        ]);
      } catch {
        // best-effort cleanup; failure is non-blocking
      }
    }
  });

  // ── Golden path: panel renders with structured evidence factors ──────────────

  test(
    'WhyThisRewrite panel is visible and shows Top evidence factors (has_draft=false)',
    async ({ page, seedData }) => {
      // Skip gracefully if beforeAll could not create the test record.
      if (noDraftRecId === null) {
        test.skip(true, 'no-draft recommendation was not created in beforeAll');
        return;
      }

      const recId = noDraftRecId; // TypeScript narrowing: number
      const pageErrors: string[] = [];
      const consoleErrors: string[] = [];

      page.on('pageerror', (err) => pageErrors.push(err.message));
      page.on('console', (msg) => {
        if (msg.type() === 'error' && isAppConsoleError(msg.text())) {
          consoleErrors.push(msg.text());
        }
      });

      await page.goto(`/sites/${seedData.siteId}/recommendations/${recId}`, {
        waitUntil: 'networkidle',
      });

      // URL must remain on the recommendation detail page (not redirected to login/error).
      await expect(page).toHaveURL(
        new RegExp(`/sites/${seedData.siteId}/recommendations/${recId}\\b`),
      );

      // ── Panel existence ──────────────────────────────────────────────────────

      // The WhyThisRewrite panel must be visible — this is the core assertion:
      // it proves the controller emitted draft_evidence (or the evidence fallback)
      // and Show.tsx rendered the panel correctly for a no-draft recommendation.
      const panel = page.locator('[data-testid="why-this-rewrite"]');
      await expect(panel).toBeVisible({ timeout: 10_000 });

      // ── Panel structure ──────────────────────────────────────────────────────

      // Panel heading (h2) — the accessible role assertion ensures the DOM hierarchy
      // is correct, not just that the text appears somewhere on the page.
      await expect(panel.getByRole('heading', { name: /why this rewrite/i })).toBeVisible();

      // "Rewrite mode" label — confirms the mode section rendered
      await expect(panel.getByText(/rewrite mode/i)).toBeVisible();

      // "Top evidence" label — confirms the factor list section rendered
      await expect(panel.getByText(/top evidence/i)).toBeVisible();

      // At least one factor row must be visible: the evidence object carries
      // clicks_before/clicks_after/delta_percent which are in TOP_FACTOR_KEYS.
      // A missing dl (because the builder silently returned an empty payload AND
      // the evidence fallback also failed) would fail here.
      const factorRows = panel.locator('dl div');
      await expect(factorRows.first()).toBeVisible({ timeout: 5_000 });

      // The dd (value cell) must be non-empty — catches rendering bugs where the
      // label renders but the formatted value is blank/undefined.
      await expect(factorRows.first().locator('dd')).not.toBeEmpty();

      // ── Error gate ───────────────────────────────────────────────────────────

      // Human success check (per SC success_criteria): the panel renders the
      // exact evidence factors the AI rewrite will be grounded in so the user
      // can decide whether to spend an AI credit with confidence.
      // The absence of console errors confirms no silent render failure.
      expect(
        consoleErrors,
        [
          'Console errors on WhyThisRewrite panel page:',
          ...consoleErrors.map((e) => `  - ${e}`),
        ].join('\n'),
      ).toHaveLength(0);

      expect(
        pageErrors,
        ['Uncaught page errors:', ...pageErrors.map((e) => `  - ${e}`)].join('\n'),
      ).toHaveLength(0);
    },
  );

  // ── Regression guard: panel absent when has_draft=true ──────────────────────

  test(
    'WhyThisRewrite panel is correctly absent for seeded recommendation (has_draft=true)',
    async ({ page, seedData }) => {
      const pageErrors: string[] = [];
      const consoleErrors: string[] = [];

      page.on('pageerror', (err) => pageErrors.push(err.message));
      page.on('console', (msg) => {
        if (msg.type() === 'error' && isAppConsoleError(msg.text())) {
          consoleErrors.push(msg.text());
        }
      });

      await page.goto(
        `/sites/${seedData.siteId}/recommendations/${seedData.recommendationId}`,
        { waitUntil: 'networkidle' },
      );

      await expect(page).toHaveURL(
        new RegExp(
          `/sites/${seedData.siteId}/recommendations/${seedData.recommendationId}\\b`,
        ),
      );

      // Page must render meaningful content (not a blank error shell).
      await expect(page.getByRole('heading', { level: 1 })).toBeVisible();

      // Panel MUST be hidden: the controller skips DraftEvidencePayloadBuilder
      // when has_draft=true and Show.tsx hides WhyThisRewrite for the same gate.
      // If the panel appeared here, it would mean the has_draft gate is broken.
      await expect(page.locator('[data-testid="why-this-rewrite"]')).toBeHidden();

      // Zero uncaught application errors on the has_draft=true path.
      expect(
        consoleErrors,
        [
          'Console errors (has_draft=true path):',
          ...consoleErrors.map((e) => `  - ${e}`),
        ].join('\n'),
      ).toHaveLength(0);

      expect(
        pageErrors,
        ['Uncaught page errors:', ...pageErrors.map((e) => `  - ${e}`)].join('\n'),
      ).toHaveLength(0);
    },
  );
});
