/**
 * Journey: Recovery Run / Autopilot Settings (R6PROD-002)
 *
 * Tests the new "Recovery Run" section added to the Site Settings page
 * (Sites/Settings.tsx, route: sites.schedule.edit).
 *
 * Changed UI:
 *   - Autopilot mode select (off / draft / stage / publish)
 *   - Conditional max_pages_per_run number input (visible when mode != off)
 *   - Conditional monthly_autopilot_budget_usd input (visible when mode != off)
 *   - Conditional auto_rollback_on_regression toggle (visible when mode = stage | publish)
 *
 * Golden path:
 *   1. Page loads without JS errors
 *   2. "Recovery Run" section heading is visible
 *   3. Autopilot mode select renders with all 4 options
 *   4. Conditional fields hidden when mode = off (default)
 *   5. Switching to "draft" mode reveals max_pages and budget inputs
 *   6. Switching to "stage" mode additionally reveals auto_rollback toggle
 *   7. Switching to "publish" mode also shows auto_rollback toggle
 *   8. Save button is disabled when form is pristine, enabled after change
 *   9. Saving the form (PATCH) completes without 4xx/5xx
 *
 * What this catches:
 *   - Recovery Run section missing or crashing from bad prop shape
 *   - Conditional rendering logic inverted (fields always visible or never visible)
 *   - Save button coupled to wrong dirty flag (form stays disabled after editing Recovery Run)
 *   - PATCH to sites.automation.update failing due to missing/invalid new fields
 *   - auto_rollback toggle appearing for "draft" mode (should only appear for stage/publish)
 *
 * == NOTE on public_id routing (migration 2026_05_27_000001) ==
 * All URL parameters use ULID public_id, not integer PK.
 * See draft-show-regenerate.spec.ts for the canonical resolveSitePublicId() pattern.
 */

import { execSync } from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import { expect, test } 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_recovery_${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 errors
    }
  }
}

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

// URL builder — uses ULID public_id, not integer PK
const SETTINGS_URL = (sitePublicId: string) => `/sites/${sitePublicId}/schedule/edit`;

// Known background noise to filter from console error assertions.
// - /api/lead-score/pql-signal: 419 CSRF when APP_URL=https://rankwiz.test
// - ERR_TOO_MANY_REDIRECTS: background polling redirect loop when APP_URL is HTTPS
const KNOWN_NOISE = ['/api/lead-score/pql-signal', '/profile', 'ERR_TOO_MANY_REDIRECTS'];

// Resolved once per test file.
let sitePublicId = '';

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

test.describe('Journey: Recovery Run / Autopilot Settings', () => {
  test.beforeAll(async (_fixtures, testInfo) => {
    const seedDataPath = path.join(process.cwd(), 'tests/e2e/.seed-data.json');
    const seedData = JSON.parse(fs.readFileSync(seedDataPath, 'utf8')) as {
      siteId: number;
    };
    sitePublicId = resolveSitePublicId(seedData.siteId);
    console.log(`[RecoveryRun] site public_id: ${sitePublicId}`);
    void testInfo;
  });

  // ──────────────────────────────────────────────────────────────────────────
  // Page load
  // ──────────────────────────────────────────────────────────────────────────

  test.describe('Page loads without errors', () => {
    test('settings page renders for authenticated user', async ({ page }) => {
      await page.goto(SETTINGS_URL(sitePublicId), { waitUntil: 'domcontentloaded' });
      await expect(page).toHaveURL(new RegExp(`/sites/${sitePublicId}/schedule/edit`));
    });

    test('settings page renders without uncaught JS 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('response', (res) => {
        if (res.status() >= 400) {
          failedRequests.push(`${res.status()} ${res.url()}`);
        }
      });

      await page.goto(SETTINGS_URL(sitePublicId), { waitUntil: 'domcontentloaded' });

      const appErrors = errors.filter(
        (e) =>
          !e.includes('extension') &&
          !e.includes('chrome-extension') &&
          !e.includes('favicon') &&
          !KNOWN_NOISE.some((n) => e.includes(n)) &&
          !(e.includes('401') && e.includes('Unauthorized')),
      );

      if (failedRequests.length > 0) {
        console.log(`[RecoveryRun] Background 4xx/5xx: ${failedRequests.join(', ')}`);
      }

      expect(appErrors, `Unexpected console errors: ${appErrors.join('; ')}`).toHaveLength(0);
    });
  });

  // ──────────────────────────────────────────────────────────────────────────
  // Recovery Run section visibility
  // ──────────────────────────────────────────────────────────────────────────

  test.describe('Recovery Run section', () => {
    test('Recovery Run heading is visible', async ({ page }) => {
      await page.goto(SETTINGS_URL(sitePublicId), { waitUntil: 'domcontentloaded' });
      await expect(page.getByRole('heading', { name: /Recovery Run/i })).toBeVisible();
    });

    test('autopilot mode select is visible with its label', async ({ page }) => {
      await page.goto(SETTINGS_URL(sitePublicId), { waitUntil: 'domcontentloaded' });
      await expect(page.getByText(/Autopilot mode/i)).toBeVisible();

      // The select trigger (shadcn SelectTrigger) should be present
      const selectTrigger = page.locator('#autopilot-mode');
      await expect(selectTrigger).toBeVisible();
    });

    test('autopilot mode defaults to "Off"', async ({ page }) => {
      await page.goto(SETTINGS_URL(sitePublicId), { waitUntil: 'domcontentloaded' });

      // The trigger should show "Off" as the current value
      const selectTrigger = page.locator('#autopilot-mode');
      await expect(selectTrigger).toContainText('Off');
    });
  });

  // ──────────────────────────────────────────────────────────────────────────
  // Conditional field visibility — mode = off (default)
  // ──────────────────────────────────────────────────────────────────────────

  test.describe('Conditional fields when mode = off', () => {
    test('max_pages_per_run input is hidden when mode is off', async ({ page }) => {
      await page.goto(SETTINGS_URL(sitePublicId), { waitUntil: 'domcontentloaded' });

      const maxPagesInput = page.locator('#max-pages');
      await expect(maxPagesInput).not.toBeVisible();
    });

    test('monthly_autopilot_budget input is hidden when mode is off', async ({ page }) => {
      await page.goto(SETTINGS_URL(sitePublicId), { waitUntil: 'domcontentloaded' });

      const budgetInput = page.locator('#autopilot-budget');
      await expect(budgetInput).not.toBeVisible();
    });

    test('auto_rollback toggle is hidden when mode is off', async ({ page }) => {
      await page.goto(SETTINGS_URL(sitePublicId), { waitUntil: 'domcontentloaded' });

      const rollbackSwitch = page.locator('#auto-rollback');
      await expect(rollbackSwitch).not.toBeVisible();
    });
  });

  // ──────────────────────────────────────────────────────────────────────────
  // Conditional field visibility — mode = draft
  // ──────────────────────────────────────────────────────────────────────────

  test.describe('Conditional fields when mode = draft', () => {
    test('max_pages and budget inputs become visible after selecting "Draft only"', async ({
      page,
    }) => {
      await page.goto(SETTINGS_URL(sitePublicId), { waitUntil: 'domcontentloaded' });

      // Open the select dropdown
      await page.locator('#autopilot-mode').click();

      // Select "Draft only"
      await page.getByRole('option', { name: /Draft only/i }).click();

      // max_pages and budget should now be visible
      await expect(page.locator('#max-pages')).toBeVisible();
      await expect(page.locator('#autopilot-budget')).toBeVisible();
    });

    test('auto_rollback toggle remains hidden for "Draft only" mode', async ({ page }) => {
      await page.goto(SETTINGS_URL(sitePublicId), { waitUntil: 'domcontentloaded' });

      await page.locator('#autopilot-mode').click();
      await page.getByRole('option', { name: /Draft only/i }).click();

      // auto_rollback should remain hidden — it's only for stage/publish
      const rollbackSwitch = page.locator('#auto-rollback');
      await expect(rollbackSwitch).not.toBeVisible();
    });
  });

  // ──────────────────────────────────────────────────────────────────────────
  // Conditional field visibility — mode = stage
  // ──────────────────────────────────────────────────────────────────────────

  test.describe('Conditional fields when mode = stage', () => {
    test('all three conditional fields visible after selecting "Stage to WP"', async ({
      page,
    }) => {
      await page.goto(SETTINGS_URL(sitePublicId), { waitUntil: 'domcontentloaded' });

      await page.locator('#autopilot-mode').click();
      await page.getByRole('option', { name: /Stage to WP/i }).click();

      await expect(page.locator('#max-pages')).toBeVisible();
      await expect(page.locator('#autopilot-budget')).toBeVisible();
      await expect(page.locator('#auto-rollback')).toBeVisible();
    });

    test('auto_rollback toggle label describes regression threshold', async ({ page }) => {
      await page.goto(SETTINGS_URL(sitePublicId), { waitUntil: 'domcontentloaded' });

      await page.locator('#autopilot-mode').click();
      await page.getByRole('option', { name: /Stage to WP/i }).click();

      // Verify the label text is rendered (key user information)
      await expect(page.getByText(/Auto-rollback on regression/i)).toBeVisible();
      await expect(page.getByText(/30%/i)).toBeVisible();
    });
  });

  // ──────────────────────────────────────────────────────────────────────────
  // Conditional field visibility — mode = publish
  // ──────────────────────────────────────────────────────────────────────────

  test.describe('Conditional fields when mode = publish', () => {
    test('all three conditional fields visible after selecting "Auto-publish"', async ({
      page,
    }) => {
      await page.goto(SETTINGS_URL(sitePublicId), { waitUntil: 'domcontentloaded' });

      await page.locator('#autopilot-mode').click();
      await page.getByRole('option', { name: /Auto-publish/i }).click();

      await expect(page.locator('#max-pages')).toBeVisible();
      await expect(page.locator('#autopilot-budget')).toBeVisible();
      await expect(page.locator('#auto-rollback')).toBeVisible();
    });
  });

  // ──────────────────────────────────────────────────────────────────────────
  // Save button state
  // ──────────────────────────────────────────────────────────────────────────

  test.describe('Save button state', () => {
    test('Save button is disabled when form is pristine', async ({ page }) => {
      await page.goto(SETTINGS_URL(sitePublicId), { waitUntil: 'domcontentloaded' });

      const saveButton = page.getByRole('button', { name: /Save settings/i });
      await expect(saveButton).toBeDisabled();
    });

    test('Save button becomes enabled after changing autopilot mode', async ({ page }) => {
      await page.goto(SETTINGS_URL(sitePublicId), { waitUntil: 'domcontentloaded' });

      // Change the autopilot mode to dirty the automation form
      await page.locator('#autopilot-mode').click();
      await page.getByRole('option', { name: /Draft only/i }).click();

      const saveButton = page.getByRole('button', { name: /Save settings/i });
      await expect(saveButton).toBeEnabled();
    });
  });

  // ──────────────────────────────────────────────────────────────────────────
  // Form save — happy path
  // ──────────────────────────────────────────────────────────────────────────

  test.describe('Save form — PATCH to automation endpoint', () => {
    test('saving autopilot mode "draft" completes without 4xx/5xx', async ({ page }) => {
      const failedRequests: string[] = [];
      const errors: string[] = [];

      page.on('pageerror', (err) => errors.push(err.message));
      page.on('response', (res) => {
        if (res.status() >= 400) {
          failedRequests.push(`${res.status()} ${res.url()}`);
        }
      });

      await page.goto(SETTINGS_URL(sitePublicId), { waitUntil: 'domcontentloaded' });

      // Switch to "Draft only" mode
      await page.locator('#autopilot-mode').click();
      await page.getByRole('option', { name: /Draft only/i }).click();

      // Set max pages to 5
      await page.locator('#max-pages').fill('5');

      // Click Save
      const saveButton = page.getByRole('button', { name: /Save settings/i });
      await saveButton.click();

      // Wait for navigation or network idle (successful PATCH redirects back to same page)
      await page.waitForLoadState('domcontentloaded');

      // Filter out known background noise
      const realFailures = failedRequests.filter(
        (r) => !KNOWN_NOISE.some((n) => r.includes(n)),
      );

      expect(
        realFailures,
        `Unexpected 4xx/5xx responses after save: ${realFailures.join('; ')}`,
      ).toHaveLength(0);

      // The page should still be on the settings page (PATCH redirects back)
      await expect(page).toHaveURL(new RegExp(`/sites/${sitePublicId}/schedule/edit`));
    });

    test('saving autopilot mode "stage" with rollback toggle completes without errors', async ({
      page,
    }) => {
      const failedRequests: string[] = [];

      page.on('response', (res) => {
        if (res.status() >= 400) {
          failedRequests.push(`${res.status()} ${res.url()}`);
        }
      });

      await page.goto(SETTINGS_URL(sitePublicId), { waitUntil: 'domcontentloaded' });

      // Switch to "Stage to WP" mode
      await page.locator('#autopilot-mode').click();
      await page.getByRole('option', { name: /Stage to WP/i }).click();

      // Enable auto-rollback
      const rollbackSwitch = page.locator('#auto-rollback');
      await expect(rollbackSwitch).toBeVisible();
      await rollbackSwitch.click();

      // Save
      await page.getByRole('button', { name: /Save settings/i }).click();
      await page.waitForLoadState('domcontentloaded');

      const realFailures = failedRequests.filter(
        (r) => !KNOWN_NOISE.some((n) => r.includes(n)),
      );

      expect(
        realFailures,
        `Unexpected 4xx/5xx responses: ${realFailures.join('; ')}`,
      ).toHaveLength(0);
    });
  });

  // ──────────────────────────────────────────────────────────────────────────
  // Human success check
  // ──────────────────────────────────────────────────────────────────────────

  test.describe('Human success check', () => {
    test('user can set autopilot to stage+rollback and see settings persist after save', async ({
      page,
    }) => {
      await page.goto(SETTINGS_URL(sitePublicId), { waitUntil: 'domcontentloaded' });

      // Set mode to "Stage to WP"
      await page.locator('#autopilot-mode').click();
      await page.getByRole('option', { name: /Stage to WP/i }).click();

      // Set max pages to 3
      await page.locator('#max-pages').fill('3');

      // Enable rollback
      await page.locator('#auto-rollback').click();

      // Save
      await page.getByRole('button', { name: /Save settings/i }).click();
      await page.waitForLoadState('networkidle');

      // After save the page should still be on the settings page
      await expect(page).toHaveURL(new RegExp(`/sites/${sitePublicId}/schedule/edit`));

      // Verify the section is still visible (page re-renders correctly post-save)
      await expect(page.getByRole('heading', { name: /Recovery Run/i })).toBeVisible();
    });
  });
});
