/**
 * /eye-ingest — run or inspect the Awwwards eye-library ingester.
 *
 * Subcommands:
 *   list              show ingested site count by tier
 *   crawl [tier]      discover tier pages + extract site metadata
 *                     (no Playwright needed — validates network + parser)
 *   run               full ingest: crawl + Playwright capture + persist
 *                     (requires `playwright` + `ffmpeg` on disk)
 *   run <tier> <N>    ingest only one tier, N pages
 */

import type { Command } from '../commands.js'
import type { LocalCommandCall } from '../types/command.js'
import {
  fetchGalleryPage,
  discoverPageCount,
  runIngest,
  AWARD_TIERS,
  type AwardTier,
  type IngestProgressEvent,
} from '../services/designHarness/eyeIngester/index.js'
import { listIngestedSlugs, readSiteRecord } from '../services/designHarness/eyeIngester/storage.js'
import { DEFAULT_INGEST_CONFIG } from '../services/designHarness/eyeIngester/types.js'

const HELP = [
  'Usage:',
  '  /eye-ingest list              count of ingested sites by tier',
  '  /eye-ingest crawl             crawl all tiers, no capture (safe dry run)',
  '  /eye-ingest crawl <TIER>      crawl one tier (SOTY|SOTM|SOTD)',
  '  /eye-ingest run               full ingest — requires playwright + ffmpeg',
  '  /eye-ingest run <TIER> <N>    ingest tier for N pages only',
].join('\n')

const call: LocalCommandCall = async (args) => {
  const parts = args.trim().split(/\s+/).filter(Boolean)
  const sub = (parts[0] ?? 'help').toLowerCase()

  if (sub === 'help' || sub === '?' || sub === '') {
    return { type: 'text', value: HELP }
  }

  if (sub === 'list') {
    return { type: 'text', value: await renderLibrarySummary() }
  }

  if (sub === 'crawl') {
    const tierArg = parts[1]?.toUpperCase()
    const tiers = tierArg ? ([tierArg] as AwardTier[]) : [...AWARD_TIERS]
    if (!tiers.every(isAwardTier)) {
      return { type: 'text', value: `Unknown tier: ${tierArg}\n\n${HELP}` }
    }
    return { type: 'text', value: await renderCrawl(tiers) }
  }

  if (sub === 'run') {
    const tierArg = parts[1]?.toUpperCase()
    const pages = parts[2] ? Number(parts[2]) : 0

    if (tierArg && !isAwardTier(tierArg)) {
      return { type: 'text', value: `Unknown tier: ${tierArg}\n\n${HELP}` }
    }
    if (tierArg && (!Number.isFinite(pages) || pages < 0)) {
      return { type: 'text', value: `Invalid page count: ${parts[2]}\n\n${HELP}` }
    }

    return { type: 'text', value: await renderRun(tierArg as AwardTier | undefined, pages) }
  }

  return { type: 'text', value: `Unknown subcommand: ${sub}\n\n${HELP}` }
}

function isAwardTier(tier: string): tier is AwardTier {
  return (AWARD_TIERS as readonly string[]).includes(tier)
}

async function renderLibrarySummary(): Promise<string> {
  const slugs = await listIngestedSlugs()
  if (slugs.length === 0) {
    return 'Eye library is empty. Run `/eye-ingest run` to populate.'
  }

  const byTier: Record<AwardTier, number> = { SOTY: 0, SOTM: 0, SOTD: 0 }
  let unreadable = 0

  for (const slug of slugs) {
    const record = await readSiteRecord(slug)
    if (!record) {
      unreadable++
      continue
    }
    byTier[record.site.tier]++
  }

  const lines = [
    `Eye library: ${slugs.length} sites`,
    `  SOTY: ${byTier.SOTY}`,
    `  SOTM: ${byTier.SOTM}`,
    `  SOTD: ${byTier.SOTD}`,
  ]
  if (unreadable > 0) lines.push(`  (unreadable: ${unreadable})`)
  return lines.join('\n')
}

async function renderCrawl(tiers: readonly AwardTier[]): Promise<string> {
  const lines: string[] = []
  for (const tier of tiers) {
    lines.push(`\n${tier}`)
    try {
      const pages = await discoverPageCount(tier, DEFAULT_INGEST_CONFIG)
      lines.push(`  pages available: ${pages}`)
      const sites = await fetchGalleryPage(tier, 1, DEFAULT_INGEST_CONFIG)
      lines.push(`  page 1: ${sites.length} sites extracted`)
      for (const s of sites.slice(0, 5)) {
        lines.push(`    ${s.title} — ${s.liveUrl}`)
      }
      if (sites.length > 5) lines.push(`    … and ${sites.length - 5} more`)
      await sleep(DEFAULT_INGEST_CONFIG.requestDelayMs)
    } catch (err: unknown) {
      lines.push(`  crawl failed: ${err instanceof Error ? err.message : String(err)}`)
    }
  }
  return lines.join('\n').trim()
}

async function renderRun(tier: AwardTier | undefined, pages: number): Promise<string> {
  const progressLines: string[] = []
  const onProgress = (event: IngestProgressEvent): void => {
    const prefix = event.tier ? `[${event.tier}]` : ''
    const slug = event.slug ? ` ${event.slug}` : ''
    progressLines.push(`  ${event.phase}${prefix}${slug}: ${event.message}`)
  }

  try {
    const summary = await runIngest(
      {
        tiers: tier ? [tier] : [...AWARD_TIERS],
        maxPagesPerTier: pages,
      },
      onProgress,
    )

    const header = [
      'Ingest complete.',
      `  discovered: ${summary.sitesDiscovered}`,
      `  captured:   ${summary.sitesCaptured}`,
      `  skipped:    ${summary.sitesSkipped}`,
      `  failed:     ${summary.sitesFailed}`,
      `  duration:   ${(summary.durationMs / 1000).toFixed(1)}s`,
    ]

    const tail = progressLines.length > 40 ? progressLines.slice(-40) : progressLines
    return [...header, '', ...tail].join('\n')
  } catch (err: unknown) {
    const msg = err instanceof Error ? err.message : String(err)
    return [
      `Ingest failed: ${msg}`,
      '',
      'The full pipeline requires:',
      '  1. playwright       — `bun add playwright && bunx playwright install chromium`',
      '  2. ffmpeg on PATH   — https://ffmpeg.org/download.html',
      '',
      'Use `/eye-ingest crawl` for a capture-free dry run (network + parser only).',
    ].join('\n')
  }
}

function sleep(ms: number): Promise<void> {
  return new Promise((resolve) => setTimeout(resolve, ms))
}

const eyeIngest = {
  type: 'local',
  name: 'eye-ingest',
  description: 'Ingest award-winning sites from Awwwards into the eye library',
  argumentHint: '[list|crawl|run] [tier] [pages]',
  supportsNonInteractive: true,
  load: () => Promise.resolve({ call }),
} satisfies Command

export default eyeIngest
