import { test, expect, type Page } from '@playwright/test'
import { execFileSync } from 'node:child_process'
import { existsSync } from 'node:fs'
import path from 'node:path'

type ReportingFixture = {
  viewerEmail: string
  viewerPassword: string
  cardId: number
  cardPath: string
}

const VITE_MANIFEST_PATH = path.join(process.cwd(), 'public', 'build', 'manifest.json')

function ensureCompiledAssets() {
  if (existsSync(VITE_MANIFEST_PATH)) {
    return
  }

  if (process.platform === 'win32') {
    execFileSync('cmd.exe', ['/c', 'npm', 'run', 'build'], {
      cwd: process.cwd(),
      stdio: 'inherit',
    })

    return
  }

  execFileSync('npm', ['run', 'build'], {
    cwd: process.cwd(),
    stdio: 'inherit',
  })
}

function ensureNovaCardSeedData() {
  execFileSync('php', ['artisan', 'db:seed', '--class=Database\\Seeders\\NovaCardCategorySeeder', '--no-interaction'], {
    cwd: process.cwd(),
    stdio: 'inherit',
  })

  execFileSync('php', ['artisan', 'db:seed', '--class=Database\\Seeders\\NovaCardTemplateSeeder', '--no-interaction'], {
    cwd: process.cwd(),
    stdio: 'inherit',
  })
}

function resetBotProtectionState() {
  const script = [
    'use Illuminate\\Support\\Facades\\DB;',
    'use Illuminate\\Support\\Facades\\Schema;',
    "foreach (['forum_bot_logs', 'forum_bot_ip_blacklist', 'forum_bot_device_fingerprints', 'forum_bot_behavior_profiles'] as $table) {",
    '  if (Schema::hasTable($table)) {',
    '    DB::table($table)->delete();',
    '  }',
    '}',
    "echo 'ok';",
  ].join(' ')

  execFileSync('php', ['artisan', 'tinker', `--execute=${script}`], {
    cwd: process.cwd(),
    encoding: 'utf8',
  })
}

function seedReportingFixture(): ReportingFixture {
  const token = `${Date.now().toString().slice(-6)}${Math.floor(Math.random() * 1000).toString().padStart(3, '0')}`
  const viewerEmail = `e2e-nova-report-${token}@example.test`
  const viewerUsername = `nrviewer${token}`.slice(0, 20)
  const creatorEmail = `e2e-nova-report-creator-${token}@example.test`
  const creatorUsername = `nrcreator${token}`.slice(0, 20)
  const cardSlug = `nova-report-card-${token}`

  const script = [
    'use App\\Models\\NovaCard;',
    'use App\\Models\\NovaCardCategory;',
    'use App\\Models\\NovaCardTemplate;',
    'use App\\Models\\User;',
    'use Illuminate\\Support\\Facades\\Hash;',
    `$viewer = User::updateOrCreate(['email' => '${viewerEmail}'], [`,
    "  'name' => 'E2E Nova Report Viewer',",
    `  'username' => '${viewerUsername}',`,
    "  'onboarding_step' => 'complete',",
    "  'email_verified_at' => now(),",
    "  'is_active' => 1,",
    "  'password' => Hash::make('password'),",
    ']);',
    `$creator = User::updateOrCreate(['email' => '${creatorEmail}'], [`,
    "  'name' => 'E2E Nova Report Creator',",
    `  'username' => '${creatorUsername}',`,
    "  'onboarding_step' => 'complete',",
    "  'email_verified_at' => now(),",
    "  'is_active' => 1,",
    "  'password' => Hash::make('password'),",
    ']);',
    '$category = NovaCardCategory::query()->orderBy("id")->first();',
    '$template = NovaCardTemplate::query()->orderBy("id")->first();',
    '$card = NovaCard::query()->create([',
    '  "user_id" => $creator->id,',
    '  "category_id" => $category->id,',
    '  "template_id" => $template->id,',
    "  'title' => 'Playwright reportable card',",
    `  'slug' => '${cardSlug}',`,
    "  'quote_text' => 'Reported via Playwright.',",
    "  'format' => NovaCard::FORMAT_SQUARE,",
    '  "project_json" => [',
    '    "content" => ["title" => "Playwright reportable card", "quote_text" => "Reported via Playwright."],',
    '    "layout" => ["layout" => "quote_heavy", "position" => "center", "alignment" => "center", "padding" => "comfortable", "max_width" => "balanced"],',
    '    "typography" => ["font_preset" => "modern-sans", "text_color" => "#ffffff", "accent_color" => "#e0f2fe", "quote_size" => 72, "author_size" => 28, "letter_spacing" => 0, "line_height" => 1.2, "shadow_preset" => "soft"],',
    '    "background" => ["type" => "gradient", "gradient_preset" => "midnight-nova", "gradient_colors" => ["#0f172a", "#1d4ed8"], "overlay_style" => "dark-soft", "focal_position" => "center", "blur_level" => 0, "opacity" => 50],',
    '    "decorations" => [],',
    '  ],',
    "  'render_version' => 2,",
    "  'schema_version' => 2,",
    "  'background_type' => 'gradient',",
    "  'visibility' => NovaCard::VISIBILITY_PUBLIC,",
    "  'status' => NovaCard::STATUS_PUBLISHED,",
    "  'moderation_status' => NovaCard::MOD_APPROVED,",
    "  'allow_download' => true,",
    "  'allow_remix' => true,",
    "  'published_at' => now()->subMinute(),",
    ']);',
    'echo json_encode([',
    '  "viewerEmail" => $viewer->email,',
    '  "viewerPassword" => "password",',
    '  "cardId" => $card->id,',
    '  "cardPath" => "/cards/{$card->slug}-{$card->id}",',
    ']);',
  ].join(' ')

  const raw = execFileSync('php', ['artisan', 'tinker', `--execute=${script}`], {
    cwd: process.cwd(),
    encoding: 'utf8',
  })

  const jsonLine = raw
    .split(/\r?\n/)
    .map((line) => line.trim())
    .filter(Boolean)
    .reverse()
    .find((line) => line.startsWith('{') && line.endsWith('}'))

  if (!jsonLine) {
    throw new Error(`Unable to parse Nova Card reporting fixture JSON: ${raw}`)
  }

  return JSON.parse(jsonLine) as ReportingFixture
}

function reportCount(cardId: number, viewerEmail: string): number {
  const script = [
    'use App\\Models\\Report;',
    'use App\\Models\\User;',
    `$viewer = User::query()->where('email', '${viewerEmail}')->first();`,
    'if (! $viewer) { echo 0; return; }',
    `echo Report::query()->where('reporter_id', $viewer->id)->where('target_type', 'nova_card')->where('target_id', ${cardId})->count();`,
  ].join(' ')

  const raw = execFileSync('php', ['artisan', 'tinker', `--execute=${script}`], {
    cwd: process.cwd(),
    encoding: 'utf8',
  })

  const parsed = Number.parseInt(raw.trim().split(/\s+/).pop() || '0', 10)
  return Number.isNaN(parsed) ? 0 : parsed
}

async function login(page: Page, fixture: ReportingFixture) {
  for (let attempt = 0; attempt < 2; attempt += 1) {
    await page.goto('/login')
    const emailField = page.locator('input[name="email"]')
    const internalServerError = page.getByText('Internal Server Error')

    await Promise.race([
      emailField.waitFor({ state: 'visible', timeout: 8000 }),
      internalServerError.waitFor({ state: 'visible', timeout: 8000 }),
    ])

    if ((await internalServerError.isVisible().catch(() => false)) && !(await emailField.isVisible().catch(() => false))) {
      throw new Error('Nova Cards reporting login failed because the login page returned an internal server error.')
    }

    await emailField.fill(fixture.viewerEmail)
    await page.locator('input[name="password"]').fill(fixture.viewerPassword)
    await page.getByRole('button', { name: 'Sign In' }).click()

    try {
      await page.waitForURL((url) => url.pathname !== '/login', { timeout: 8000, waitUntil: 'domcontentloaded' })
      await expect(page.getByRole('button', { name: /E2E Nova Report Viewer/i })).toBeVisible()
      return
    } catch {
      const suspiciousActivity = page.getByText('Suspicious activity detected.')
      if (attempt === 0 && (await suspiciousActivity.isVisible().catch(() => false))) {
        resetBotProtectionState()
        continue
      }

      throw new Error('Nova Cards reporting login failed before reaching an authenticated page.')
    }
  }
}

test.describe('Nova Cards reporting', () => {
  test.describe.configure({ mode: 'serial' })

  let fixture: ReportingFixture

  test.beforeAll(() => {
    ensureCompiledAssets()
    ensureNovaCardSeedData()
    fixture = seedReportingFixture()
  })

  test.beforeEach(() => {
    resetBotProtectionState()
  })

  test('authenticated viewers can submit a report from the public card page', async ({ page }) => {
    await login(page, fixture)
    await page.goto(fixture.cardPath, { waitUntil: 'domcontentloaded' })

    await expect(page.locator('[data-card-report]')).toBeVisible()

    const dialogExpectations = [
      { type: 'prompt', message: 'Why are you reporting this card?', value: 'Playwright report reason' },
      { type: 'prompt', message: 'Add extra details for moderators (optional)', value: 'Playwright detail for moderation context.' },
      { type: 'alert', message: 'Report submitted. Thank you.' },
    ]

    let handledDialogs = 0

    const dialogHandler = async (dialog: Parameters<Page['on']>[1] extends (event: infer T) => any ? T : never) => {
      const expected = dialogExpectations[handledDialogs]
      expect(expected).toBeTruthy()
      expect(dialog.type()).toBe(expected.type)
      expect(dialog.message()).toBe(expected.message)

      if (expected.type === 'prompt') {
        await dialog.accept(expected.value)
      } else {
        await dialog.accept()
      }

      handledDialogs += 1
    }

    page.on('dialog', dialogHandler)

    await page.locator('[data-card-report]').click()

    await expect.poll(() => handledDialogs, { timeout: 10000 }).toBe(3)
    await expect.poll(() => reportCount(fixture.cardId, fixture.viewerEmail), { timeout: 10000 }).toBe(1)

    page.off('dialog', dialogHandler)
  })
})