'use client'

import { useState, useEffect, useRef } from 'react'
import {
  Shield, AlertTriangle, Globe, FolderOpen, Terminal,
  ArrowLeft, Copy, Check, ArrowRight, Lock, Eye,
} from 'lucide-react'

// --- Types ---

type Phase = 'input' | 'scanning' | 'report'

interface ScanLine {
  id: number
  text: string
  type: 'info' | 'warning' | 'success' | 'error'
}

interface Finding {
  type: string
  severity: string
  title: string
  description: string
  command: string
}

interface ThreatReport {
  riskScore: number
  riskLevel: string
  summary: string
  findings: Finding[]
}

// --- Constants ---

const SENSITIVE_PATTERNS = [
  '.env', '.env.local', '.env.production', '.env.development',
  '.env.example', '.env.sample', '.env.test',
  'secrets.yml', 'secrets.yaml', 'credentials.json',
  'serviceAccountKey.json', '.npmrc', '.pypirc',
  'id_rsa', '.pem', '.key', '.p12',
  'docker-compose.yml', 'docker-compose.yaml', 'Dockerfile',
  '.htpasswd',
]

const CONFIG_PATTERNS = [
  'config/', '.github/', '.circleci/', '.gitlab-ci.yml',
  'webpack.config', 'vite.config', 'next.config', 'nuxt.config',
  'tsconfig.json', 'nginx.conf', 'Procfile', 'vercel.json',
  'netlify.toml', 'terraform/', 'k8s/', '.aws/', 'Makefile',
]

const STACK_DETECT: [string, (f: string[]) => boolean][] = [
  ['Node.js', f => f.some(x => x === 'package.json')],
  ['TypeScript', f => f.some(x => x === 'tsconfig.json' || x.endsWith('.ts') || x.endsWith('.tsx'))],
  ['Python', f => f.some(x => ['requirements.txt', 'setup.py', 'pyproject.toml', 'Pipfile'].includes(x))],
  ['Go', f => f.some(x => x === 'go.mod')],
  ['Rust', f => f.some(x => x === 'Cargo.toml')],
  ['Ruby', f => f.some(x => x === 'Gemfile')],
  ['Java', f => f.some(x => x === 'pom.xml' || x === 'build.gradle')],
  ['React', f => f.some(x => x.endsWith('.jsx') || x.endsWith('.tsx'))],
  ['Next.js', f => f.some(x => x.startsWith('next.config'))],
  ['Django', f => f.some(x => x === 'manage.py')],
  ['Docker', f => f.some(x => x === 'Dockerfile' || x.startsWith('docker-compose'))],
  ['Terraform', f => f.some(x => x.endsWith('.tf'))],
]

// --- Helpers ---

function parseGitHubUrl(url: string) {
  const cleaned = url.trim().replace(/\.git$/, '').replace(/\/$/, '')
  const m = cleaned.match(/(?:(?:https?:\/\/)?github\.com\/)?([^\/\s]+)\/([^\/\s]+)/)
  if (!m) throw new Error('Invalid GitHub URL. Use format: github.com/owner/repo')
  return { owner: m[1], repo: m[2] }
}

const delay = (ms: number) => new Promise(r => setTimeout(r, ms))

async function fetchTree(owner: string, repo: string): Promise<{ files: string[]; truncated: boolean }> {
  const meta = await fetch(`https://api.github.com/repos/${owner}/${repo}`)
  if (meta.status === 404) throw new Error('Repository not found. Is it public?')
  if (meta.status === 403) throw new Error('GitHub rate limit hit. Try again in a minute.')
  if (!meta.ok) throw new Error(`GitHub error: ${meta.status}`)
  const { default_branch } = await meta.json()

  const tree = await fetch(
    `https://api.github.com/repos/${owner}/${repo}/git/trees/${default_branch}?recursive=1`
  )
  if (!tree.ok) throw new Error('Failed to fetch repo tree')
  const data = await tree.json()
  const files = (data.tree || [])
    .filter((n: any) => n.type === 'blob')
    .map((n: any) => n.path)
  return { files, truncated: !!data.truncated }
}

async function fetchFile(owner: string, repo: string, path: string): Promise<string | null> {
  try {
    const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/contents/${path}`)
    if (!res.ok) return null
    const { content, encoding } = await res.json()
    if (encoding === 'base64') return atob(content.replace(/\n/g, ''))
    return null
  } catch { return null }
}

function severityColor(s: string) {
  if (s === 'critical') return 'text-red-400'
  if (s === 'high') return 'text-primary'
  if (s === 'medium') return 'text-yellow-400'
  return 'text-green-400'
}

function severityBg(s: string) {
  if (s === 'critical') return 'bg-red-400/10 border border-red-400/20'
  if (s === 'high') return 'bg-primary/10 border border-primary/20'
  if (s === 'medium') return 'bg-yellow-400/10 border border-yellow-400/20'
  return 'bg-green-400/10 border border-green-400/20'
}

function typeIcon(t: string) {
  if (t === 'credential_read') return <Lock className="h-4 w-4" />
  if (t === 'network_call') return <Globe className="h-4 w-4" />
  if (t === 'directory_access') return <FolderOpen className="h-4 w-4" />
  if (t === 'command_execution') return <Terminal className="h-4 w-4" />
  return <Eye className="h-4 w-4" />
}

function riskColor(level: string) {
  if (level === 'CRITICAL') return '#ef4444'
  if (level === 'HIGH') return '#D95E2A'
  if (level === 'MEDIUM') return '#eab308'
  return '#22c55e'
}

// --- Component ---

export default function GamePage() {
  const [phase, setPhase] = useState<Phase>('input')
  const [url, setUrl] = useState('')
  const [lines, setLines] = useState<ScanLine[]>([])
  const [report, setReport] = useState<ThreatReport | null>(null)
  const [repoName, setRepoName] = useState('')
  const [detectedStack, setDetectedStack] = useState<string[]>([])
  const [error, setError] = useState('')
  const [copied, setCopied] = useState(false)
  const [animatedScore, setAnimatedScore] = useState(0)
  const termRef = useRef<HTMLDivElement>(null)
  const lineId = useRef(0)
  const hasAutoScanned = useRef(false)

  function addLine(text: string, type: ScanLine['type']) {
    lineId.current++
    setLines(prev => [...prev, { id: lineId.current, text, type }])
  }

  // Auto-scroll terminal
  useEffect(() => {
    if (termRef.current) termRef.current.scrollTop = termRef.current.scrollHeight
  }, [lines])

  // Auto-scan from URL param
  useEffect(() => {
    if (hasAutoScanned.current) return
    const params = new URLSearchParams(window.location.search)
    const repoParam = params.get('repo')
    if (repoParam) {
      hasAutoScanned.current = true
      const fullUrl = repoParam.includes('github.com') ? repoParam : `https://github.com/${repoParam}`
      setUrl(fullUrl)
      scan(fullUrl)
    }
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  // Animate score counter
  useEffect(() => {
    if (!report) return
    const target = report.riskScore
    const start = Date.now()
    const duration = 1500
    function tick() {
      const progress = Math.min((Date.now() - start) / duration, 1)
      setAnimatedScore(Math.round(target * (1 - Math.pow(1 - progress, 3))))
      if (progress < 1) requestAnimationFrame(tick)
    }
    requestAnimationFrame(tick)
  }, [report])

  async function scan(inputUrl?: string) {
    const targetUrl = inputUrl || url
    if (!targetUrl.trim()) return

    setPhase('scanning')
    setLines([])
    setError('')
    setReport(null)
    lineId.current = 0

    try {
      const { owner, repo } = parseGitHubUrl(targetUrl)
      setRepoName(`${owner}/${repo}`)
      window.history.replaceState(null, '', `${window.location.pathname}?repo=${owner}/${repo}`)

      addLine(`Connecting to github.com/${owner}/${repo}...`, 'info')
      await delay(400)

      addLine('Fetching repository tree...', 'info')
      const { files, truncated } = await fetchTree(owner, repo)
      const dirCount = new Set(files.map(f => f.split('/').slice(0, -1).join('/'))).size
      addLine(`Found ${files.length.toLocaleString()} files across ${dirCount} directories`, 'success')
      if (truncated) addLine('Large repo — tree was truncated by GitHub', 'info')
      await delay(300)

      // Detect stack
      addLine('Detecting stack & dependencies...', 'info')
      await delay(200)
      const stack = STACK_DETECT.filter(([, test]) => test(files)).map(([name]) => name)
      setDetectedStack(stack)
      addLine(`Stack: ${stack.join(' \u00b7 ') || 'Unknown'}`, 'success')
      await delay(300)

      // Sensitive files
      addLine('Scanning for sensitive files & secrets...', 'info')
      await delay(200)
      const sensitive = files.filter(f => {
        const name = f.split('/').pop() || f
        return SENSITIVE_PATTERNS.some(p => name === p || name.startsWith(p + '.') || f.includes('/' + p))
      })
      for (const f of sensitive.slice(0, 12)) {
        addLine(f, 'warning')
        await delay(100)
      }
      if (sensitive.length === 0) addLine('No obvious sensitive files found', 'success')
      if (sensitive.length > 12) addLine(`...and ${sensitive.length - 12} more`, 'warning')
      await delay(200)

      // Config files
      const configs = files.filter(f => CONFIG_PATTERNS.some(p => f.includes(p))).slice(0, 15)

      // Fetch dependencies
      addLine('Analyzing dependencies...', 'info')
      await delay(200)
      let deps: Record<string, string> = {}
      const pkgRaw = await fetchFile(owner, repo, 'package.json')
      if (pkgRaw) {
        try {
          const pkg = JSON.parse(pkgRaw)
          deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) }
        } catch { /* ignore parse errors */ }
      } else {
        const reqs = await fetchFile(owner, repo, 'requirements.txt')
        if (reqs) {
          reqs.split('\n').forEach(l => {
            const m = l.trim().match(/^([a-zA-Z0-9_-]+)/)
            if (m) deps[m[1]] = '*'
          })
        }
      }

      // Highlight interesting deps
      const interesting = Object.keys(deps).filter(d =>
        /stripe|aws|openai|anthropic|firebase|supabase|prisma|mongoose|pg|mysql|redis|twilio|sendgrid|auth0|passport|jwt|bcrypt|crypto|axios|ssh|docker|kubernetes|helm|vault|sentry/i.test(d)
      )
      for (const d of interesting.slice(0, 8)) {
        addLine(`${d} ${deps[d] !== '*' ? deps[d] : ''}`, 'warning')
        await delay(80)
      }
      if (Object.keys(deps).length > 0) {
        addLine(`${Object.keys(deps).length} total dependencies`, 'success')
      }
      await delay(300)

      // Generate report via LLM
      addLine('Generating agent threat report...', 'info')
      const res = await fetch('/api/analyze', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ owner, repo, files, stack, dependencies: deps, sensitiveFiles: sensitive, configFiles: configs }),
      })

      if (!res.ok) {
        const data = await res.json().catch(() => ({}))
        throw new Error(data.error || 'Failed to generate report')
      }

      const reportData = await res.json()
      addLine('Report complete.', 'success')
      await delay(600)

      setReport(reportData)
      setPhase('report')
    } catch (e: any) {
      addLine(e.message || 'Something went wrong', 'error')
      setError(e.message)
    }
  }

  function reset() {
    setPhase('input')
    setUrl('')
    setLines([])
    setReport(null)
    setError('')
    setRepoName('')
    setDetectedStack([])
    setAnimatedScore(0)
    window.history.replaceState(null, '', window.location.pathname)
  }

  async function copyLink() {
    await navigator.clipboard.writeText(window.location.href)
    setCopied(true)
    setTimeout(() => setCopied(false), 2000)
  }

  const color = report ? riskColor(report.riskLevel) : '#D95E2A'
  const circumference = 2 * Math.PI * 45

  return (
    <main className="min-h-screen">
      {/* Nav */}
      <nav className="fixed top-0 left-0 right-0 z-50 border-b border-border/50 bg-background/80 backdrop-blur-md">
        <div className="mx-auto max-w-5xl flex items-center justify-between px-6 h-14">
          <a href="/" className="flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground transition-colors">
            <ArrowLeft className="h-4 w-4" />
            <span className="hidden sm:inline">Back to Greywall</span>
          </a>
          <div className="flex items-center gap-2.5">
            <svg viewBox="0 0 32 32" fill="none" className="h-5 w-5" xmlns="http://www.w3.org/2000/svg">
              <path d="M16 2L4 7V15C4 22.18 9.11 28.79 16 30C22.89 28.79 28 22.18 28 15V7L16 2Z" fill="#D95E2A" />
              <path d="M16 6L8 9.5V15C8 20.05 11.42 24.68 16 26C20.58 24.68 24 20.05 24 15V9.5L16 6Z" fill="#161614" />
              <circle cx="16" cy="12" r="2" fill="#D95E2A" />
              <circle cx="12" cy="17" r="1.5" fill="#D95E2A" />
              <circle cx="20" cy="17" r="1.5" fill="#D95E2A" />
              <circle cx="16" cy="21" r="1.5" fill="#D95E2A" />
              <path d="M16 14V19.5M14 16L12.5 17M18 16L19.5 17" stroke="#D95E2A" strokeWidth="1" strokeLinecap="round" />
            </svg>
            <span className="font-serif font-semibold text-lg tracking-tight">Greyscan</span>
          </div>
          <div className="w-20" />
        </div>
      </nav>

      <div className="pt-14">
        {/* ── INPUT PHASE ── */}
        {phase === 'input' && (
          <section className="relative min-h-[calc(100vh-3.5rem)] flex items-center justify-center px-4 sm:px-6">
            <div className="absolute inset-0 bg-[radial-gradient(ellipse_at_top,rgba(217,94,42,0.06)_0%,transparent_50%)]" />
            <div
              className="absolute inset-0 opacity-[0.03]"
              style={{
                backgroundImage: 'linear-gradient(rgba(249,249,247,1) 1px, transparent 1px), linear-gradient(90deg, rgba(249,249,247,1) 1px, transparent 1px)',
                backgroundSize: '64px 64px',
              }}
            />

            <div className="relative max-w-2xl w-full text-center animate-fade-up">
              <h1 className="font-serif text-3xl sm:text-4xl md:text-5xl font-semibold tracking-tight leading-[1.1] mb-4">
                What would an AI agent{' '}
                <em className="italic text-primary">try on your machine?</em>
              </h1>

              <p className="text-muted-foreground font-serif text-base sm:text-lg leading-relaxed mb-10 max-w-xl mx-auto">
                AI agents run as you, with access to everything you have. Paste a repo URL and we&apos;ll show what an unrestricted agent would attempt on your machine when working on that codebase.
              </p>

              <form
                onSubmit={e => { e.preventDefault(); scan() }}
                className="flex gap-2 max-w-lg mx-auto"
              >
                <input
                  type="text"
                  value={url}
                  onChange={e => setUrl(e.target.value)}
                  placeholder="github.com/owner/repo"
                  className="flex-1 bg-card/50 border border-border/50 rounded-lg px-4 py-3 text-sm font-mono placeholder:text-muted-foreground/50 focus:outline-none focus:border-primary/50 focus:ring-1 focus:ring-primary/20 transition-all"
                />
                <button
                  type="submit"
                  disabled={!url.trim()}
                  className="bg-primary text-primary-foreground px-5 py-3 rounded-lg text-sm font-sans font-medium hover:bg-primary/90 disabled:opacity-40 disabled:cursor-not-allowed transition-all flex items-center gap-2 cursor-pointer"
                >
                  Scan
                  <ArrowRight className="h-4 w-4" />
                </button>
              </form>

              <p className="text-xs text-muted-foreground/50 mt-4 font-sans">
                Public repos only &middot; No code is stored &middot; Powered by <a href="https://github.com/GreyhavenHQ/greywall" target="_blank" rel="noopener noreferrer" className="text-primary hover:text-primary/80 transition-colors">Greywall</a>
              </p>
            </div>
          </section>
        )}

        {/* ── SCANNING PHASE ── */}
        {phase === 'scanning' && (
          <section className="min-h-[calc(100vh-3.5rem)] flex items-center justify-center px-4 sm:px-6 py-12">
            <div className="max-w-2xl w-full animate-fade-up">
              <div className="flex items-center gap-2 mb-4">
                <Shield className="h-4 w-4 text-primary" />
                <span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
                  Scanning {repoName}
                </span>
              </div>

              <div
                ref={termRef}
                className="code-block p-5 sm:p-6 max-h-[60vh] overflow-y-auto scrollbar-hide"
              >
                <div className="flex items-center gap-2 mb-5">
                  <div className="w-3 h-3 rounded-full bg-red-500/70" />
                  <div className="w-3 h-3 rounded-full bg-yellow-500/70" />
                  <div className="w-3 h-3 rounded-full bg-green-500/70" />
                  <span className="ml-2 text-xs font-mono text-muted-foreground">
                    greywall scan
                  </span>
                </div>

                <div className="space-y-1.5 font-mono text-xs sm:text-sm">
                  {lines.map(line => (
                    <div key={line.id} className="animate-fade-up" style={{ animationDuration: '0.3s' }}>
                      {line.type === 'info' && (
                        <span className="text-muted-foreground">
                          <span className="opacity-50">$ </span>{line.text}
                        </span>
                      )}
                      {line.type === 'success' && (
                        <span className="text-green-400">
                          <span className="opacity-50">{'\u2713'} </span>{line.text}
                        </span>
                      )}
                      {line.type === 'warning' && (
                        <span className="text-primary">
                          <span className="opacity-60">{'\u26A0'} </span>{line.text}
                        </span>
                      )}
                      {line.type === 'error' && (
                        <span className="text-red-400">
                          <span className="opacity-60">{'\u2717'} </span>{line.text}
                        </span>
                      )}
                    </div>
                  ))}
                  {!error && (
                    <div className="inline-block w-1.5 h-4 bg-primary/70 animate-pulse mt-1" />
                  )}
                </div>
              </div>

              {error && (
                <div className="mt-4 text-center">
                  <button
                    onClick={reset}
                    className="text-sm text-primary hover:text-primary/80 transition-colors font-sans cursor-pointer"
                  >
                    &larr; Try another repo
                  </button>
                </div>
              )}
            </div>
          </section>
        )}

        {/* ── REPORT PHASE ── */}
        {phase === 'report' && report && (
          <section className="px-4 sm:px-6 py-12 sm:py-16">
            <div className="max-w-2xl mx-auto">
              {/* Report Card */}
              <div className="border border-border/30 rounded-xl overflow-hidden bg-card/20 animate-fade-up glow-orange">
                {/* Header */}
                <div className="p-6 sm:p-8 border-b border-border/20">
                  <div className="flex items-start justify-between gap-4">
                    <div>
                      <div className="flex items-center gap-2 mb-3">
                        <Shield className="h-4 w-4 text-primary" />
                        <span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
                          What an agent would try on your machine
                        </span>
                      </div>
                      <h2 className="font-mono text-lg sm:text-xl text-foreground mb-1">
                        {repoName}
                      </h2>
                      <p className="text-xs text-muted-foreground font-sans">
                        {detectedStack.join(' \u00b7 ')}
                      </p>
                    </div>

                    {/* Risk Score Circle */}
                    <div className="relative w-24 h-24 sm:w-28 sm:h-28 flex-shrink-0">
                      <svg className="w-full h-full -rotate-90" viewBox="0 0 100 100">
                        <circle
                          cx="50" cy="50" r="45" fill="none"
                          stroke="rgb(var(--border))" strokeWidth="5" opacity="0.3"
                        />
                        <circle
                          cx="50" cy="50" r="45" fill="none"
                          stroke={color} strokeWidth="5"
                          strokeDasharray={circumference}
                          strokeDashoffset={circumference - (animatedScore / 100) * circumference}
                          strokeLinecap="round"
                          className="transition-[stroke-dashoffset] duration-100"
                        />
                      </svg>
                      <div className="absolute inset-0 flex flex-col items-center justify-center">
                        <span className="text-2xl sm:text-3xl font-bold font-sans" style={{ color }}>
                          {animatedScore}
                        </span>
                        <span className="text-[10px] text-muted-foreground uppercase tracking-wider">
                          /100
                        </span>
                      </div>
                    </div>
                  </div>

                  {/* Risk Level Badge */}
                  <div
                    className="mt-4 inline-flex items-center gap-1.5 px-2.5 py-1 rounded-md text-xs font-sans font-medium"
                    style={{
                      background: `${color}15`,
                      border: `1px solid ${color}30`,
                      color,
                    }}
                  >
                    <AlertTriangle className="h-3 w-3" />
                    {report.riskLevel} RISK
                  </div>
                </div>

                {/* Summary */}
                <div className="px-6 sm:px-8 py-4 border-b border-border/20 bg-card/10">
                  <p className="text-sm text-muted-foreground font-serif leading-relaxed">
                    {report.summary}
                  </p>
                </div>

                {/* Findings */}
                <div className="divide-y divide-border/10">
                  {report.findings.map((finding, i) => (
                    <div
                      key={i}
                      className="px-6 sm:px-8 py-4 animate-fade-up"
                      style={{ animationDelay: `${i * 100}ms` }}
                    >
                      <div className="flex items-start gap-3">
                        <div className={`mt-0.5 p-1.5 rounded ${severityBg(finding.severity)} ${severityColor(finding.severity)}`}>
                          {typeIcon(finding.type)}
                        </div>
                        <div className="flex-1 min-w-0">
                          <div className="flex items-center gap-2 mb-1">
                            <span className={`text-[10px] font-sans font-medium uppercase tracking-wider ${severityColor(finding.severity)}`}>
                              {finding.severity}
                            </span>
                          </div>
                          <h3 className="text-sm font-sans font-medium text-foreground mb-1">
                            {finding.title}
                          </h3>
                          <p className="text-xs text-muted-foreground font-serif leading-relaxed mb-2">
                            {finding.description}
                          </p>
                          <code className="text-[11px] font-mono text-muted-foreground/70 bg-background/50 px-2 py-1 rounded break-all inline-block">
                            {finding.command}
                          </code>
                        </div>
                      </div>
                    </div>
                  ))}
                </div>

                {/* CTA */}
                <div className="px-6 sm:px-8 py-6 sm:py-8 border-t border-border/20 bg-card/20 text-center">
                  <p className="font-serif text-lg sm:text-xl font-semibold tracking-tight mb-2">
                    This is what Greywall would have blocked.
                  </p>
                  <p className="text-xs text-muted-foreground font-serif mb-5">
                    Container-free sandboxing with real-time observability for AI agents.
                  </p>
                  <a
                    href="https://github.com/GreyhavenHQ/greywall"
                    target="_blank"
                    rel="noopener noreferrer"
                    className="inline-flex items-center gap-2 bg-primary text-primary-foreground px-5 py-2.5 rounded-lg text-sm font-sans font-medium hover:bg-primary/90 transition-all"
                  >
                    Install Greywall
                    <ArrowRight className="h-4 w-4" />
                  </a>
                </div>
              </div>

              {/* Actions */}
              <div className="flex items-center justify-center gap-3 mt-6">
                <button
                  onClick={copyLink}
                  className="flex items-center gap-2 px-4 py-2 rounded-lg border border-border/30 text-sm text-muted-foreground hover:text-foreground hover:border-border/50 transition-all font-sans cursor-pointer"
                >
                  {copied ? <Check className="h-4 w-4 text-green-400" /> : <Copy className="h-4 w-4" />}
                  {copied ? 'Copied!' : 'Share Report'}
                </button>
                <button
                  onClick={reset}
                  className="flex items-center gap-2 px-4 py-2 rounded-lg border border-border/30 text-sm text-muted-foreground hover:text-foreground hover:border-border/50 transition-all font-sans cursor-pointer"
                >
                  Scan Another Repo
                </button>
              </div>
            </div>
          </section>
        )}
      </div>
    </main>
  )
}
