/**
 * Journey: ROI Dashboard — CohortProofCard + measurement holdout opt-in (R6PROD-010)
 *
 * Verifies the new CohortProofCard + measurement_holdout_opt_in column changes:
 *
 *   - ROI Dashboard empty state renders correctly (RoiPreflightChecklist + CTA)
 *   - ROI Dashboard loads without uncaught console errors (default: not opted in)
 *   - CohortProofCard is hidden when site is NOT opted in (default)
 *   - CohortProofCard renders when site opted in + completed AioRecoveryRun with holdout data
 *   - MeasuredImpactCard renders when FEATURE_MEASURED_ROI is on + snapshots exist
 *   - Share card drawer opens/closes correctly on MeasuredImpactCard
 *   - Filter pills (Action Type, Tracking Period) navigate correctly
 *   - Unauthenticated access redirects to /login
 *
 * Data setup uses artisan tinker (runPhp pattern from draft-show-regenerate.spec.ts).
 * The spec creates minimal data: RecommendationRoiSnapshot + optionally
 * SiteAnalysisSettings.measurement_holdout_opt_in = true + AioRecoveryRun with holdout rows.
 *
 * == NOTE on public_id routing (migration 2026_05_27_000001) ==
 * Site URLs use ULID public_id, not integer PK. This spec resolves ULIDs via tinker.
 *
 * == Background noise filter ==
 * APP_URL=https://rankwiz.test / test base http://127.0.0.1:PORT produces two known
 * background noise classes (419 CSRF on pql-signal, ERR_TOO_MANY_REDIRECTS on polling).
 * These are filtered via KNOWN_NOISE before asserting zero console errors.
 */

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

// ---------------------------------------------------------------------------
// PHP execution helpers
// ---------------------------------------------------------------------------

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

/** 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;
}

/** Creates a minimal RecommendationRoiSnapshot for the given site's recommendation. */
function createRoiSnapshot(siteId: number): void {
  runPhp(`
    $site = \\App\\Models\\Site::find(${siteId});
    $run = \\App\\Models\\AnalysisRun::where('site_id', $site->id)->first();
    if (!$run) { echo 'NO_RUN'; return; }
    $rec = \\App\\Models\\Recommendation::where('analysis_run_id', $run->id)->first();
    if (!$rec) { echo 'NO_REC'; return; }
    // Only create if none exists yet
    $existing = \\App\\Models\\RecommendationRoiSnapshot::where('recommendation_id', $rec->id)->first();
    if ($existing) { echo 'EXISTS'; return; }
    $rec->lifecycle_status = \\App\\Enums\\LifecycleStatus::Applied;
    $rec->save();
    \\App\\Models\\RecommendationRoiSnapshot::factory()->create([
      'recommendation_id' => $rec->id,
      'baseline_date' => now()->subDays(30),
      'baseline_clicks' => 100,
      'current_clicks' => 150,
      'clicks_delta' => 50,
      'clicks_delta_pct' => 50.0,
      'days_tracked' => 30,
      'is_significant' => true,
    ]);
    echo 'CREATED';
  `);
}

/** Opts a site in to measurement holdout. */
function optInMeasurementHoldout(siteId: number): void {
  runPhp(`
    $site = \\App\\Models\\Site::find(${siteId});
    $settings = $site->siteAnalysisSettings ?? \\App\\Models\\SiteAnalysisSettings::factory()->create(['site_id' => $site->id]);
    $settings->measurement_holdout_opt_in = true;
    $settings->save();
    echo 'OPT_IN_DONE';
  `);
}

/** Resets measurement holdout opt-in for a site. */
function resetMeasurementHoldout(siteId: number): void {
  runPhp(`
    $site = \\App\\Models\\Site::find(${siteId});
    if ($site->siteAnalysisSettings) {
      $site->siteAnalysisSettings->update(['measurement_holdout_opt_in' => false]);
    }
    echo 'RESET_DONE';
  `);
}

// ---------------------------------------------------------------------------
// Known background noise (APP_URL=https://rankwiz.test vs http://127.0.0.1:PORT)
// ---------------------------------------------------------------------------
const KNOWN_NOISE = [
  '/api/lead-score/pql-signal', // 419 CSRF (pql-signal fires with wrong origin CSRF token)
  '/profile',                   // background XHR may 4xx during seeding
  'ERR_TOO_MANY_REDIRECTS',     // polling XHRs redirect-loop when APP_URL is HTTPS
];

// ---------------------------------------------------------------------------
// Resolved IDs (set once in beforeAll)
// ---------------------------------------------------------------------------
let sitePublicId = '';
let siteId = 0;

test.describe.configure({ mode: 'serial' });

test.describe('Journey: ROI Dashboard — CohortProofCard & holdout (R6PROD-010)', () => {
  test.beforeAll(async () => {
    const seedDataPath = path.join(process.cwd(), 'tests/e2e/.seed-data.json');
    const seedData = JSON.parse(fs.readFileSync(seedDataPath, 'utf8')) as {
      siteId: number;
    };
    siteId = seedData.siteId;
    sitePublicId = resolveSitePublicId(siteId);
    console.log(`[ROI Cohort Proof] site public_id: ${sitePublicId} (integer: ${siteId})`);
  });

  // ────────────────────────────────────────────────────────────────────────────
  // Empty state (no ROI snapshots)
  // ────────────────────────────────────────────────────────────────────────────
  test.describe('Empty state — no tracked ROI snapshots', () => {
    test.beforeAll(async () => {
      // Ensure no ROI snapshots exist for this site (idempotent cleanup so
      // the empty-state tests are stable regardless of which project runs first).
      runPhp(`
        $site = \\App\\Models\\Site::find(${siteId});
        $run = \\App\\Models\\AnalysisRun::where('site_id', $site->id)->first();
        if ($run) {
          $recIds = \\App\\Models\\Recommendation::where('analysis_run_id', $run->id)->pluck('id');
          \\App\\Models\\RecommendationRoiSnapshot::whereIn('recommendation_id', $recIds)->delete();
        }
        echo 'CLEANUP_DONE';
      `);
    });

    test('ROI dashboard loads without console errors (empty state)', async ({ page }) => {
      const errors: string[] = [];
      page.on('pageerror', (err) => errors.push(err.message));
      page.on('console', (msg) => {
        if (msg.type() === 'error') errors.push(msg.text());
      });

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

      const appErrors = errors.filter(
        (e) =>
          !e.includes('extension') &&
          !e.includes('chrome-extension') &&
          !KNOWN_NOISE.some((n) => e.includes(n)),
      );
      expect(appErrors, `Console errors: ${appErrors.join(', ')}`).toHaveLength(0);
    });

    test('empty state shows ROI Dashboard heading', async ({ page }) => {
      await page.goto(`/sites/${sitePublicId}/roi`, { waitUntil: 'domcontentloaded' });
      await expect(page.getByRole('heading', { name: /roi dashboard/i })).toBeVisible();
    });

    test('empty state shows "Apply your first recommendation" CTA', async ({ page }) => {
      await page.goto(`/sites/${sitePublicId}/roi`, { waitUntil: 'domcontentloaded' });
      // EmptyState CTA for the zero-tracked state
      const cta = page.getByRole('link', { name: /apply your first recommendation/i });
      await expect(cta).toBeVisible();
    });

    test('CohortProofCard is NOT rendered in empty state', async ({ page }) => {
      await page.goto(`/sites/${sitePublicId}/roi`, { waitUntil: 'domcontentloaded' });
      // CohortProofCard uses data-testid="cohort-proof-card"
      await expect(page.locator('[data-testid="cohort-proof-card"]')).not.toBeAttached();
    });
  });

  // ────────────────────────────────────────────────────────────────────────────
  // Populated state — site not opted in (default)
  // ────────────────────────────────────────────────────────────────────────────
  test.describe('Populated state — not opted in to measurement holdout', () => {
    test.beforeAll(async () => {
      // Seed a ROI snapshot so the dashboard shows the populated view
      createRoiSnapshot(siteId);
      // Ensure site is NOT opted in (default)
      resetMeasurementHoldout(siteId);
    });

    test('ROI dashboard loads with data without console errors', async ({ page }) => {
      const errors: string[] = [];
      const failedRequests: string[] = [];
      page.on('pageerror', (err) => errors.push(err.message));
      page.on('console', (msg) => {
        if (msg.type() === 'error') errors.push(msg.text());
      });
      page.on('requestfailed', (req) => {
        const url = req.url();
        if (!KNOWN_NOISE.some((n) => url.includes(n))) {
          failedRequests.push(`${req.method()} ${url} — ${req.failure()?.errorText ?? 'unknown'}`);
        }
      });

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

      const appErrors = errors.filter(
        (e) =>
          !e.includes('extension') &&
          !e.includes('chrome-extension') &&
          !KNOWN_NOISE.some((n) => e.includes(n)),
      );
      expect(appErrors, `Console errors: ${appErrors.join(', ')}`).toHaveLength(0);
      expect(failedRequests, `Failed requests: ${failedRequests.join(', ')}`).toHaveLength(0);
    });

    test('populated ROI dashboard shows ROI Dashboard heading', async ({ page }) => {
      await page.goto(`/sites/${sitePublicId}/roi`, { waitUntil: 'domcontentloaded' });
      await expect(page.getByRole('heading', { name: /roi dashboard/i })).toBeVisible();
    });

    test('CohortProofCard is NOT rendered when site is not opted in', async ({ page }) => {
      await page.goto(`/sites/${sitePublicId}/roi`, { waitUntil: 'domcontentloaded' });
      // cohort_proof is null when not opted in → CohortProofCard must be absent
      await expect(page.locator('[data-testid="cohort-proof-card"]')).not.toBeAttached();
    });

    test('filter pills render for Action Type and Tracking Period', async ({ page }) => {
      await page.goto(`/sites/${sitePublicId}/roi`, { waitUntil: 'domcontentloaded' });
      // Both filter group headings must be present
      // Use role=heading to disambiguate from the table column header "Action Type"
      await expect(page.getByRole('heading', { name: 'Action Type' })).toBeVisible();
      await expect(page.getByRole('heading', { name: 'Tracking Period' })).toBeVisible();
      // "All" filter pill must be present (default state)
      const allPill = page.getByRole('link', { name: /^all$/i }).first();
      await expect(allPill).toBeVisible();
    });

    test('Export CSV button is present when data exists', async ({ page }) => {
      await page.goto(`/sites/${sitePublicId}/roi`, { waitUntil: 'domcontentloaded' });
      await expect(page.getByRole('link', { name: /export csv/i })).toBeVisible();
    });
  });

  // ────────────────────────────────────────────────────────────────────────────
  // CohortProofCard — opted in + holdout data
  // ────────────────────────────────────────────────────────────────────────────
  test.describe('CohortProofCard — opted in to measurement holdout', () => {
    test.beforeAll(async () => {
      // Opt the site in to measurement holdout
      optInMeasurementHoldout(siteId);

      // Create a completed AioRecoveryRun with holdout data for this site.
      // Note: aio_recovery_items has no site_id column; aio_recovery_holdout uses
      // aio_recovery_item_id + aio_recovery_run_id + site_id + cohort + assignment_seed.
      runPhp(`
        $site = \\App\\Models\\Site::find(${siteId});
        $run = \\App\\Models\\AioRecoveryRun::factory()->create([
          'site_id' => $site->id,
          'status' => \\App\\Enums\\JobStatus::Completed,
          'completed_at' => now(),
          'summary' => ['items_processed' => 2],
        ]);
        $item = \\App\\Models\\AioRecoveryItem::factory()->create([
          'aio_recovery_run_id' => $run->id,
        ]);
        \\Illuminate\\Support\\Facades\\DB::table('aio_recovery_holdout')->insert([
          'aio_recovery_item_id' => $item->id,
          'aio_recovery_run_id' => $run->id,
          'site_id' => $site->id,
          'cohort' => 'control',
          'assignment_seed' => 'seed123',
          'created_at' => now(),
          'updated_at' => now(),
        ]);
        echo 'HOLDOUT_SEEDED';
      `);
    });

    test.afterAll(async () => {
      // Reset opt-in after the test group to avoid polluting subsequent tests
      resetMeasurementHoldout(siteId);
    });

    test('CohortProofCard renders when opted in with holdout data', async ({ page }) => {
      const errors: string[] = [];
      page.on('pageerror', (err) => errors.push(err.message));
      page.on('console', (msg) => {
        if (msg.type() === 'error') errors.push(msg.text());
      });

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

      const appErrors = errors.filter(
        (e) =>
          !e.includes('extension') &&
          !e.includes('chrome-extension') &&
          !KNOWN_NOISE.some((n) => e.includes(n)),
      );
      expect(appErrors, `Console errors: ${appErrors.join(', ')}`).toHaveLength(0);

      // CohortProofCard must be visible when opted in + holdout data exists
      await expect(page.locator('[data-testid="cohort-proof-card"]')).toBeVisible();
    });

    test('CohortProofCard shows "Holdout Comparison" heading', async ({ page }) => {
      await page.goto(`/sites/${sitePublicId}/roi`, { waitUntil: 'domcontentloaded' });
      await expect(
        page.getByRole('heading', { name: /holdout comparison/i }),
      ).toBeVisible();
    });

    test('CohortProofCard shows disclaimer: directional signal, not a rigorous RCT', async ({
      page,
    }) => {
      await page.goto(`/sites/${sitePublicId}/roi`, { waitUntil: 'domcontentloaded' });
      // Language contract: never claims rigorous causality
      await expect(
        page.getByText(/directional signal, not a rigorous rct/i),
      ).toBeVisible();
    });

    test('CohortProofCard shows treatment vs control comparison grid', async ({ page }) => {
      await page.goto(`/sites/${sitePublicId}/roi`, { waitUntil: 'domcontentloaded' });
      await expect(page.locator('[data-testid="cohort-comparison-grid"]')).toBeVisible();
      await expect(page.getByText(/treatment.*fixed/i)).toBeVisible();
      await expect(page.getByText(/control.*held out/i)).toBeVisible();
    });

    test('page has no console errors with CohortProofCard visible', async ({ page }) => {
      const errors: string[] = [];
      page.on('pageerror', (err) => errors.push(err.message));
      page.on('console', (msg) => {
        if (msg.type() === 'error') errors.push(msg.text());
      });

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

      const appErrors = errors.filter(
        (e) =>
          !e.includes('extension') &&
          !e.includes('chrome-extension') &&
          !KNOWN_NOISE.some((n) => e.includes(n)),
      );
      expect(appErrors, `Console errors: ${appErrors.join(', ')}`).toHaveLength(0);
    });
  });

  // ────────────────────────────────────────────────────────────────────────────
  // Unauthenticated access
  // ────────────────────────────────────────────────────────────────────────────
  test.describe('Unauthenticated access is blocked', () => {
    test('blocks unauthenticated access to ROI dashboard', async ({ browser }) => {
      const context = await browser.newContext({ storageState: { cookies: [], origins: [] } });
      const page = await context.newPage();

      await page.goto(`/sites/${sitePublicId}/roi`);
      await expect(page).toHaveURL(/\/login/);

      await context.close();
    });
  });
});
