/**
 * Workflow spec: Report Preview + SharedReport Body (session 4bc6660c)
 *
 * Covers the two components changed in this session:
 *   - resources/js/Components/Reports/ReportPreview.tsx
 *     (authenticated preview embedded in Pages/Reports/Preview.tsx)
 *   - resources/js/Components/SharedReport/Body.tsx
 *     (public-facing section renderer in Pages/SharedReport/View.tsx)
 *
 * Golden path:
 *   1. Create a completed Report with all 6 sections of data (tinker)
 *   2. Navigate to /sites/{publicSiteId}/reports/{publicReportId}
 *   3. Assert ReportPreview renders all sections: Traffic Overview, Top Pages,
 *      Findings, Recommendations, AI Drafts, ROI Tracking
 *   4. Create a SharedReportLink for the same report
 *   5. Navigate to /shared-reports/{token} (unauthenticated)
 *   6. Assert SharedReportBody renders: HeroKpiBand, Trend chart section,
 *      Top pages, Findings, Recommendations, Drafts, ROI
 *
 * Sad paths:
 *   pending  — report queued state shows "Report Queued" card
 *   failed   — report failed state shows error card
 *   no-data  — completed report with null data shows EmptyState
 *   public-expired — expired shared link shows "Expired" page
 *   public-404 — invalid token returns 404
 *
 * Background noise filter:
 *   /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 (same pattern as other e2e specs in this project)
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// ---------------------------------------------------------------------------
// PHP execution helpers (same pattern as roi-dashboard-e5r6r7r8-02ac0323.spec.ts)
// ---------------------------------------------------------------------------

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

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

/**
 * Build the JSON data blob that ReportPreview and SharedReportBody expect.
 * All 6 sections are included: traffic_overview, top_pages, findings,
 * recommendations, ai_drafts, roi_metrics.
 */
function buildReportData(siteName: string, siteDomain: string): Record<string, unknown> {
  return {
    site: { name: siteName, domain: siteDomain },
    generated_at: new Date().toISOString(),
    branding: null,
    traffic_overview: {
      period: { start: '2026-04-01', end: '2026-04-30' },
      metrics: {
        total_clicks: 12345,
        total_impressions: 198765,
        avg_ctr: 6.21,
        avg_position: 14.3,
      },
      daily_metrics: [
        { date: '2026-04-01', clicks: 400, impressions: 6200, ctr: 6.45, position: 13.8 },
        { date: '2026-04-02', clicks: 410, impressions: 6400, ctr: 6.41, position: 14.0 },
        { date: '2026-04-03', clicks: 395, impressions: 6100, ctr: 6.48, position: 14.2 },
      ],
      comparison: {
        period: { start: '2026-03-01', end: '2026-03-31' },
        metrics: {
          total_clicks: 11000,
          total_impressions: 185000,
          avg_ctr: 5.95,
          avg_position: 15.1,
        },
        deltas: {
          clicks_delta_pct: 12.2,
          impressions_delta_pct: 7.4,
          ctr_delta_pct: 4.4,
          position_delta_pct: -5.3,
        },
      },
    },
    top_pages: [
      {
        page_url: `https://${siteDomain}/blog/seo-tips`,
        clicks: 2100,
        impressions: 34000,
        ctr: 6.18,
        position: 8.3,
      },
      {
        page_url: `https://${siteDomain}/pricing`,
        clicks: 1800,
        impressions: 29000,
        ctr: 6.21,
        position: 9.1,
      },
    ],
    findings: {
      findings: [
        {
          id: 1,
          type: 'traffic_change',
          direction: 'winner',
          page_url: `https://${siteDomain}/blog/seo-tips`,
          segment_type: 'device',
          segment_value: 'MOBILE',
          metric_before: 800,
          metric_after: 1050,
          delta_absolute: 250,
          delta_percent: 31.25,
          supporting_data: {},
        },
        {
          id: 2,
          type: 'traffic_change',
          direction: 'loser',
          page_url: `https://${siteDomain}/about`,
          segment_type: null,
          segment_value: null,
          metric_before: 450,
          metric_after: 310,
          delta_absolute: -140,
          delta_percent: -31.1,
          supporting_data: {},
        },
      ],
    },
    recommendations: {
      total_count: 2,
      recommendations: [
        {
          id: 101,
          page_url: `https://${siteDomain}/about`,
          action_type: 'content_rewrite',
          lifecycle_status: 'pending',
          impact_score: 82,
          confidence_score: 74,
          title: 'Rewrite the About page to target "who we are" queries',
          reasoning: 'Traffic dropped 31% — content freshness and keyword density are below threshold.',
          evidence: {},
          applied_at: null,
        },
        {
          id: 102,
          page_url: `https://${siteDomain}/pricing`,
          action_type: 'meta_tag_optimization',
          lifecycle_status: 'applied',
          impact_score: 68,
          confidence_score: 88,
          title: 'Optimise pricing-page meta title for CTR',
          reasoning: 'CTR is 6.2% vs expected 8.5% at position 9 — meta title lacks a CTA.',
          evidence: {},
          applied_at: '2026-04-15T10:00:00Z',
        },
      ],
    },
    ai_drafts: {
      total_count: 1,
      drafts: [
        {
          id: 201,
          recommendation_id: 101,
          page_url: `https://${siteDomain}/about`,
          action_type: 'content_rewrite',
          h1: 'About Us — Helping Bloggers Rank in 2026',
          meta_title: 'About RankWizAI | AI-Powered SEO for Indie Bloggers',
          meta_description:
            'RankWizAI diagnoses your traffic drops and generates SEO-optimised rewrites. Free plan available.',
          token_usage: 1240,
          created_at: '2026-04-16T08:30:00Z',
        },
      ],
    },
    roi_metrics: {
      total_applied: 1,
      total_clicks_gained: 312,
      avg_performance_multiplier: 1.3,
      snapshots: [
        {
          recommendation_id: 102,
          page_url: `https://${siteDomain}/pricing`,
          action_type: 'meta_tag_optimization',
          days_tracked: 30,
          baseline_clicks: 1800,
          current_clicks: 2112,
          clicks_delta_abs: 312,
          clicks_delta_pct: 17.3,
          baseline_ctr: 6.21,
          current_ctr: 7.28,
          ctr_delta_pct: 17.2,
          baseline_position: 9.1,
          current_position: 8.4,
          position_delta_pct: -7.7,
          is_significant: true,
        },
      ],
    },
  };
}

/** Create a completed report for the E2E seed site. Returns { reportPublicId }. */
function createCompletedReport(
  siteId: number,
  userId: number,
  data: Record<string, unknown>,
): { reportPublicId: string } {
  // sections stored as associative array: array_fill_keys(['section_name', ...], true)
  // html_content stores the JSON data blob (parsed by controller via json_decode)
  const dataJson = JSON.stringify(data);

  // Write data to a temp JSON file to avoid shell-escaping nightmares
  const tmpJson = `/tmp/e2e_report_data_${process.pid}_${Date.now()}.json`;
  fs.writeFileSync(tmpJson, dataJson, 'utf8');

  const raw = runPhp(`
    $dataJson = file_get_contents('${tmpJson}');
    @unlink('${tmpJson}');
    $sections = array_fill_keys([
      'traffic_overview', 'top_pages', 'findings',
      'recommendations', 'ai_drafts', 'roi_metrics',
    ], true);
    $report = \\App\\Models\\Report::factory()->completed()->create([
      'site_id'      => ${siteId},
      'user_id'      => ${userId},
      'name'         => 'E2E Full Report',
      'sections'     => $sections,
      'html_content' => $dataJson,
    ]);
    echo $report->public_id;
  `);
  // cleanup temp file in case PHP didn't
  try { fs.unlinkSync(tmpJson); } catch { /* ignore */ }

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

/** Create a SharedReportLink and return its token. */
function createSharedLink(reportPublicId: string): string {
  const raw = runPhp(`
    $report = \\App\\Models\\Report::where('public_id', '${reportPublicId}')->firstOrFail();
    $link = \\App\\Models\\SharedReportLink::factory()->create([
      'report_id' => $report->id,
    ]);
    echo $link->token;
  `);
  const token = raw.trim().split('\n').map((l) => l.trim()).filter(Boolean).pop() ?? '';
  if (!token) throw new Error(`createSharedLink failed. Raw:\n${raw}`);
  return token;
}

/** Create an expired SharedReportLink. */
function createExpiredSharedLink(reportPublicId: string): string {
  const raw = runPhp(`
    $report = \\App\\Models\\Report::where('public_id', '${reportPublicId}')->firstOrFail();
    $link = \\App\\Models\\SharedReportLink::factory()->expired()->create([
      'report_id' => $report->id,
    ]);
    echo $link->token;
  `);
  const token = raw.trim().split('\n').map((l) => l.trim()).filter(Boolean).pop() ?? '';
  if (!token) throw new Error(`createExpiredSharedLink failed. Raw:\n${raw}`);
  return token;
}

/** Create a report with status=pending. Returns publicId. */
function createPendingReport(siteId: number, userId: number): string {
  const raw = runPhp(
    `$r = \\App\\Models\\Report::factory()->pending()->create(['site_id'=>${siteId},'user_id'=>${userId},'name'=>'E2E Pending Report']); echo $r->public_id;`,
  );
  const id = raw.trim().split('\n').map((l) => l.trim()).filter(Boolean).pop() ?? '';
  if (!id) throw new Error(`createPendingReport failed. Raw:\n${raw}`);
  return id;
}

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

/** Delete a report by public_id (cleanup). */
function cleanupReport(reportPublicId: string): void {
  runPhp(`
    $r = \\App\\Models\\Report::where('public_id', '${reportPublicId}')->first();
    if ($r) { $r->sharedLinks()->delete(); $r->delete(); }
    echo 'DONE';
  `);
}

// ---------------------------------------------------------------------------
// Known background noise (filter from console-error assertions)
// ---------------------------------------------------------------------------
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 Preview (ReportPreview.tsx)', () => {
  let sitePublicId: string;
  let reportPublicId: string;

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

    sitePublicId = resolveSitePublicId(siteId);
    const data = buildReportData(siteName, 'e2e-test-site.com');
    ({ reportPublicId } = createCompletedReport(siteId, userId, data));
  });

  test.afterAll(() => {
    if (reportPublicId) cleanupReport(reportPublicId);
  });

  test('golden path — all 6 sections render without console errors', async ({ page }) => {
    const consoleErrors: 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}`);
    });

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

    // Report is ready banner
    await expect(page.getByText('Report Ready')).toBeVisible();

    // ── Traffic Overview ──────────────────────────────────────────────────
    await expect(page.getByText('Traffic Overview')).toBeVisible();
    // exact: true avoids collision with "Total Clicks Gained" in the ROI section
    await expect(page.getByText('Total Clicks', { exact: true })).toBeVisible();
    await expect(page.getByText('12,345').first()).toBeVisible();
    await expect(page.getByText('Total Impressions')).toBeVisible();
    await expect(page.getByText('Daily Traffic Trend')).toBeVisible();

    // ── Top Pages ─────────────────────────────────────────────────────────
    await expect(page.getByText('Top Performing Pages')).toBeVisible();
    await expect(page.getByText('/blog/seo-tips').first()).toBeVisible();

    // ── Findings ──────────────────────────────────────────────────────────
    await expect(page.getByText('Traffic Findings')).toBeVisible();
    // Winner: +31.25%  and Loser: -31.1%
    await expect(page.getByText('+31.3%')).toBeVisible();

    // ── Recommendations ───────────────────────────────────────────────────
    await expect(page.getByText('SEO Recommendations')).toBeVisible();
    await expect(
      page.getByText('Rewrite the About page to target "who we are" queries'),
    ).toBeVisible();

    // ── AI Drafts ─────────────────────────────────────────────────────────
    await expect(page.getByText('AI-Generated Content Drafts')).toBeVisible();
    await expect(page.getByText('About Us — Helping Bloggers Rank in 2026')).toBeVisible();

    // ── ROI Tracking ──────────────────────────────────────────────────────
    await expect(page.getByText('ROI Tracking')).toBeVisible();
    await expect(page.getByText('1.3×')).toBeVisible();

    // ── Branding footer uses RankWizAI branding (branding=null) ───────────
    // The full text is "Report generated with RankWizAI" — check the containing element
    await expect(page.getByText(/Report generated with/, { exact: false })).toBeVisible();

    expect(consoleErrors).toEqual([]);
  });

  test('pending state — shows Report Queued card', async ({ page }) => {
    const { siteId, userId } = (() => {
      const p = path.join(__dirname, '..', '.seed-data.json');
      return JSON.parse(fs.readFileSync(p, 'utf8')) as { siteId: number; userId: number };
    })();
    const pendingId = createPendingReport(siteId, userId);
    try {
      await page.goto(`/sites/${sitePublicId}/reports/${pendingId}`, {
        waitUntil: 'domcontentloaded',
      });
      await expect(page.getByText('Report Queued')).toBeVisible();
      // No crash — no raw exception visible
      await expect(page.getByRole('heading', { name: /Report Queued/ })).toBeVisible();
    } finally {
      cleanupReport(pendingId);
    }
  });

  test('failed state — shows error card', async ({ page }) => {
    const { siteId, userId } = (() => {
      const p = path.join(__dirname, '..', '.seed-data.json');
      return JSON.parse(fs.readFileSync(p, 'utf8')) as { siteId: number; userId: number };
    })();
    const failedId = createFailedReport(siteId, userId);
    try {
      await page.goto(`/sites/${sitePublicId}/reports/${failedId}`, {
        waitUntil: 'domcontentloaded',
      });
      await expect(page.getByText('Report Generation Failed')).toBeVisible();
    } finally {
      cleanupReport(failedId);
    }
  });
});

test.describe('SharedReport Body (SharedReport/Body.tsx)', () => {
  let reportPublicId: string;
  let shareToken: string;
  let expiredToken: string;

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

    const data = buildReportData(siteName, 'e2e-shared.com');
    ({ reportPublicId } = createCompletedReport(siteId, userId, data));
    shareToken = createSharedLink(reportPublicId);
    expiredToken = createExpiredSharedLink(reportPublicId);
  });

  test.afterAll(() => {
    if (reportPublicId) cleanupReport(reportPublicId);
  });

  test('golden path — shared report renders all sections without console errors', async ({
    browser,
  }) => {
    // Use an unauthenticated context to simulate a public viewer
    const context = await browser.newContext();
    const page = await context.newPage();

    const consoleErrors: 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}`);
    });

    try {
      await page.goto(`/shared-reports/${shareToken}`, { waitUntil: 'networkidle' });

      // Page title block
      await expect(page.getByRole('heading', { name: 'E2E Full Report' })).toBeVisible();

      // CTA banner (non-white-label)
      await expect(page.getByText('Try RankWizAI Free')).toBeVisible();

      // ── HeroKpiBand ────────────────────────────────────────────────────
      // The HeroKpiBand renders "Total clicks", "Impressions", "Avg. CTR", "Avg. position"
      await expect(page.getByText('Total clicks')).toBeVisible();
      await expect(page.getByText('12,345').first()).toBeVisible();
      // Use first() / aria-label to avoid strict-mode collision with chart legend + table header
      await expect(page.getByLabel('Headline metrics').getByText('Impressions')).toBeVisible();
      await expect(page.getByText('Avg. CTR')).toBeVisible();
      await expect(page.getByText('Avg. position')).toBeVisible();

      // ── Trend chart SectionFrame ────────────────────────────────────────
      await expect(page.getByText('Trend over the reporting window')).toBeVisible();

      // ── Comparison SectionFrame ─────────────────────────────────────────
      await expect(page.getByText('vs. prior period')).toBeVisible();

      // ── Top Pages SectionFrame ──────────────────────────────────────────
      await expect(page.getByText('Highest traffic in the period')).toBeVisible();
      await expect(page.getByText('/blog/seo-tips').first()).toBeVisible();

      // ── Findings SectionFrame ───────────────────────────────────────────
      await expect(page.getByText('What changed in this window')).toBeVisible();

      // ── Recommendations SectionFrame ────────────────────────────────────
      await expect(page.getByText('Actions to recover or amplify')).toBeVisible();
      await expect(
        page.getByText('Rewrite the About page to target "who we are" queries'),
      ).toBeVisible();

      // ── AI Drafts SectionFrame ──────────────────────────────────────────
      await expect(page.getByText('AI-generated content rewrites')).toBeVisible();
      await expect(
        page.getByText('About Us — Helping Bloggers Rank in 2026'),
      ).toBeVisible();

      // ── ROI SectionFrame ────────────────────────────────────────────────
      await expect(page.getByText('Outcomes from applied recommendations')).toBeVisible();
      await expect(page.getByText('1.3×')).toBeVisible();

      expect(consoleErrors).toEqual([]);
    } finally {
      await context.close();
    }
  });

  test('expired link — renders Expired page', async ({ browser }) => {
    const context = await browser.newContext();
    const page = await context.newPage();
    try {
      await page.goto(`/shared-reports/${expiredToken}`, { waitUntil: 'domcontentloaded' });
      // Should render SharedReport/Expired — check for the heading "Link Expired"
      await expect(page.getByText('Link Expired')).toBeVisible();
    } finally {
      await context.close();
    }
  });

  test('invalid token — returns 404', async ({ browser }) => {
    const context = await browser.newContext();
    const page = await context.newPage();
    try {
      const response = await page.goto('/shared-reports/this-token-does-not-exist-12345', {
        waitUntil: 'domcontentloaded',
      });
      expect(response?.status()).toBe(404);
    } finally {
      await context.close();
    }
  });
});
