/**
 * Artifact Viewer — renders AI-generated visual content in chat
 *
 * Supports HTML (sandboxed iframe), SVG (inline), and Mermaid (rendered via CDN).
 * Features: code/preview toggle, copy code, auto-resize, open in new tab.
 *
 * Security: HTML artifacts run in sandboxed iframes (allow-scripts only, no
 * allow-same-origin). SVG is rendered inline as it's generated by the AI in
 * the user's authenticated session — same trust model as claude.ai artifacts.
 */
import { useState, useRef, useEffect } from 'react'
import { Code2, Eye, Copy, Check, ExternalLink } from 'lucide-react'
import { cn } from '@/lib/utils'
import { Button } from '@/components/ui/button'
import { useCopy } from '@/client/lib/use-copy'
import { useTheme } from '@/client/components/theme-provider'

/**
 * Read the resolved value of a CSS custom property on the document root.
 * Returns undefined during SSR / before hydration.
 */
function readCssVar(name: string): string | undefined {
  if (typeof window === 'undefined') return undefined
  const v = getComputedStyle(document.documentElement).getPropertyValue(name).trim()
  return v || undefined
}

/** Resolve whether the current theme is dark by inspecting the `.dark` class. */
function useResolvedDarkMode(): boolean {
  const { theme } = useTheme()
  const [isDark, setIsDark] = useState(() => {
    if (typeof document === 'undefined') return false
    return document.documentElement.classList.contains('dark')
  })
  useEffect(() => {
    if (typeof document === 'undefined') return
    setIsDark(document.documentElement.classList.contains('dark'))
  }, [theme])
  return isDark
}

interface ArtifactData {
  _artifact: true
  type: 'html' | 'svg' | 'mermaid'
  title: string
  code: string
  height?: number
  artifactId?: string
}

export function isArtifact(output: unknown): output is ArtifactData {
  return !!output && typeof output === 'object' && (output as Record<string, unknown>)['_artifact'] === true
}

interface Props {
  artifact: ArtifactData
}

export function ArtifactViewer({ artifact }: Props) {
  const [showCode, setShowCode] = useState(false)
  const { copy, copied } = useCopy()
  const [autoHeight, setAutoHeight] = useState<number | null>(null)
  const iframeRef = useRef<HTMLIFrameElement>(null)
  const isDark = useResolvedDarkMode()

  const height = autoHeight || artifact.height || 400

  useEffect(() => {
    const handler = (e: MessageEvent) => {
      if (e.source !== iframeRef.current?.contentWindow) return
      if (e.data?.type === 'artifact-resize' && typeof e.data.height === 'number') {
        setAutoHeight(Math.min(e.data.height + 2, 800))
      }
    }
    window.addEventListener('message', handler)
    return () => window.removeEventListener('message', handler)
  }, [])

  const handleCopy = () => {
    void copy(artifact.code, { successMessage: 'Code copied' })
  }

  const iframeSrc = buildIframeSrc(artifact, isDark)

  return (
    <div className="my-2 rounded-lg border border-border overflow-hidden">
      <div className="flex items-center justify-between px-3 py-1.5 bg-muted/50 border-b border-border">
        <div className="flex items-center gap-2 min-w-0">
          <span className="text-xs font-medium truncate">{artifact.title}</span>
          <span className="text-[10px] text-muted-foreground uppercase">{artifact.type}</span>
        </div>
        <div className="flex items-center gap-1">
          <Button
            variant="ghost"
            size="icon-sm"
            onClick={() => setShowCode(!showCode)}
            className={cn('text-muted-foreground hover:text-foreground', showCode && 'bg-muted text-foreground')}
            title={showCode ? 'Show preview' : 'Show code'}
            aria-label={showCode ? 'Show preview' : 'Show code'}
          >
            {showCode ? <Eye className="size-3.5" /> : <Code2 className="size-3.5" />}
          </Button>
          <Button
            variant="ghost"
            size="icon-sm"
            onClick={handleCopy}
            className="text-muted-foreground hover:text-foreground transition-colors"
            title="Copy code"
            aria-label={copied ? 'Copied' : 'Copy code'}
          >
            {copied ? <Check className="size-3.5 text-green-600 dark:text-green-400" /> : <Copy className="size-3.5" />}
          </Button>
          {artifact.type === 'html' && (
            <Button
              variant="ghost"
              size="icon-sm"
              onClick={() => {
                const blob = new Blob([artifact.code], { type: 'text/html' })
                window.open(URL.createObjectURL(blob), '_blank')
              }}
              className="text-muted-foreground hover:text-foreground transition-colors"
              title="Open in new tab"
              aria-label="Open in new tab"
            >
              <ExternalLink className="size-3.5" />
            </Button>
          )}
        </div>
      </div>

      {showCode ? (
        <pre className="p-3 text-xs font-mono overflow-auto max-h-96 bg-background">{artifact.code}</pre>
      ) : (
        <div className="bg-muted/50">
          {artifact.type === 'svg' ? (
            <SvgRenderer code={artifact.code} height={height} isDark={isDark} />
          ) : (
            <iframe
              ref={iframeRef}
              srcDoc={iframeSrc}
              sandbox="allow-scripts"
              style={{ width: '100%', height: `${height}px`, border: 'none' }}
              title={artifact.title}
            />
          )}
        </div>
      )}
    </div>
  )
}

/** Resolve theme-aware iframe colours from the page's CSS variables. */
function resolveIframeColors(isDark: boolean): { bg: string; fg: string } {
  // Fall back to safe defaults if computed styles can't be read yet (SSR / first paint).
  const fallback = isDark
    ? { bg: 'hsl(222.2 84% 4.9%)', fg: 'hsl(210 40% 98%)' }
    : { bg: 'hsl(0 0% 100%)', fg: 'hsl(222.2 84% 4.9%)' }
  return {
    bg: readCssVar('--background') ?? fallback.bg,
    fg: readCssVar('--foreground') ?? fallback.fg,
  }
}

/** Render SVG safely via an iframe to avoid script injection */
function SvgRenderer({ code, height, isDark }: { code: string; height: number; isDark: boolean }) {
  const iframeRef = useRef<HTMLIFrameElement>(null)
  const { bg, fg } = resolveIframeColors(isDark)
  const html = `<!DOCTYPE html><html><head><style>body{margin:0;padding:16px;background:${bg};color:${fg};display:flex;justify-content:center;align-items:center;min-height:100vh;}</style></head><body>${code}</body></html>`
  return (
    <iframe
      ref={iframeRef}
      srcDoc={html}
      sandbox="allow-scripts"
      style={{ width: '100%', height: `${height}px`, border: 'none' }}
      title="SVG artifact"
    />
  )
}

function buildIframeSrc(artifact: ArtifactData, isDark: boolean): string {
  const resizeScript = `<script>new ResizeObserver(()=>{parent.postMessage({type:'artifact-resize',height:document.documentElement.scrollHeight},'*')}).observe(document.documentElement)<\/script>`

  if (artifact.type === 'mermaid') {
    const { bg, fg } = resolveIframeColors(isDark)
    const mermaidTheme = isDark ? 'dark' : 'default'
    return `<!DOCTYPE html>
<html><head>
<style>body{margin:0;padding:16px;background:${bg};color:${fg};font-family:system-ui;display:flex;justify-content:center;}</style>
<script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"><\/script>
</head><body>
<pre class="mermaid">${escapeHtml(artifact.code)}</pre>
<script>mermaid.initialize({startOnLoad:true,theme:'${mermaidTheme}'});<\/script>
${resizeScript}
</body></html>`
  }

  // HTML — inject resize script
  if (artifact.code.includes('</body>')) {
    return artifact.code.replace('</body>', `${resizeScript}</body>`)
  }
  return artifact.code + resizeScript
}

function escapeHtml(str: string): string {
  return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
}
