"use client"

import React, { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react'
import { useTranslations } from 'next-intl'
import { pageSchema, type Page } from '@/lib/schema/page'
import { exportPage } from '@/lib/exporter/html'
import { AssetManager } from '@/lib/assets/assetManager'
import { buildZip } from '@/lib/exporter/zip'
import { buildExtrasForPage } from '@/lib/exporter/extras'
import { buildAssetsMetadata } from '@/lib/exporter/assets.meta'
import { buildAssetsIndex } from '@/lib/exporter/assets.index'
import type { Section } from '@/lib/state/store'
import { createFormStore, type FormState } from '@/lib/state/formStore'
import FormBuilderPanel from '@/components/FormBuilderPanel'
import BuilderShell from '@/components/BuilderShell'
import SectionCanvas from '@/components/SectionCanvas'
import SidebarComponents from '@/components/SidebarComponents'

function download(filename: string, data: Uint8Array) {
  const ab = data.slice().buffer as ArrayBuffer
  const blob = new Blob([ab], { type: 'application/zip' })
  try {
    if (typeof window !== 'undefined') {
      const w = window as unknown as { __captureExport?: boolean; __lastBlob?: Blob }
      if (w.__captureExport) {
        w.__lastBlob = blob
      }
    }
  } catch {}
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = filename
  document.body.appendChild(a)
  a.click()
  a.remove()
  URL.revokeObjectURL(url)
}

export default function BuilderClientPage() {
  const t = useTranslations()
  const [locale, setLocale] = useState<'en' | 'ko'>('en')
  const [title, setTitle] = useState('My Landing')
  const [primaryColor, setPrimaryColor] = useState('#0ea5e9')
  const [fontFamily, setFontFamily] = useState('Inter')
  const [sections, setSections] = useState<Section[]>([])
  const [selectedIndex, setSelectedIndex] = useState<number | null>(null)
  const [seoTitle, setSeoTitle] = useState<string>('')
  const [seoDescription, setSeoDescription] = useState<string>('')
  const [ogImage, setOgImage] = useState<string>('')
  const [ga4MeasurementId, setGa4MeasurementId] = useState<string>('')
  const [metaPixelId, setMetaPixelId] = useState<string>('')
  // Export Options (manifest/robots overrides)
  const [manifestName, setManifestName] = useState<string>('')
  const [manifestShortName, setManifestShortName] = useState<string>('')
  const [manifestStartUrl, setManifestStartUrl] = useState<string>('')
  const [manifestDisplay, setManifestDisplay] = useState<string>('')
  const [manifestThemeColor, setManifestThemeColor] = useState<string>('')
  const [robotsDisallow, setRobotsDisallow] = useState<string>('')
  const [sheetsMode, setSheetsMode] = useState<'auto' | 'include' | 'exclude'>('auto')
  const [assetPathStrategy, setAssetPathStrategy] = useState<'flat' | 'grouped'>('flat')
  // Font optimization: allow toggling Google Fonts preconnect links in exported HTML
  const [fontPreconnect, setFontPreconnect] = useState<boolean>(true)
  const [viewport, setViewport] = useState<'desktop' | 'mobile'>(() => {
    if (typeof window !== 'undefined') {
      try {
        const saved = localStorage.getItem('builder.viewport') as 'desktop' | 'mobile' | null
        if (saved === 'desktop' || saved === 'mobile') return saved
      } catch {}
    }
    return 'desktop'
  })
  const fileInputRef = useRef<HTMLInputElement | null>(null)
  const assetManagerRef = useRef<AssetManager>(
    new AssetManager({ maxBytes: 10 * 1024 * 1024, allowedExts: ['png', 'jpg', 'jpeg', 'webp', 'svg'] })
  )

  useEffect(() => {
    try {
      localStorage.setItem('builder.viewport', viewport)
    } catch {}
  }, [viewport])
  const [am, setAm] = useState<AssetManager | null>(null)
  useEffect(() => {
    setAm(assetManagerRef.current)
  }, [])
  const [selectedFileName, setSelectedFileName] = useState<string>('')

  // form store (stable instance without ref access in render)
  const formStore = useMemo(() => createFormStore(), [])
  const formState: FormState = useSyncExternalStore(
    formStore.subscribe,
    formStore.getState,
    formStore.getState
  )

  const addHero = useCallback(async () => {
    const fileEl = fileInputRef.current
    if (fileEl && fileEl.files && fileEl.files.length > 0) {
      const file = fileEl.files[0]
      const buf = new Uint8Array(await file.arrayBuffer())
      if (am) {
        await am.add({ name: file.name, type: file.type, bytes: buf })
      }
      setSelectedFileName(file.name)
    }
    // 파일이 없어도 Hero 섹션은 추가(이미지는 placeholder 사용)
    const hero: Section = { type: 'hero', props: { heading: 'Welcome', subheading: '' } }
    setSections((prev) => [...prev, hero])
  }, [am])

  const addFaq = useCallback(() => {
    const faq: Section = { type: 'faq', props: { items: [] } }
    setSections((prev) => [...prev, faq])
  }, [])

  const addCta = useCallback(() => {
    const cta: Section = { type: 'cta', props: { text: 'Get Started', href: '#' } }
    setSections((prev) => [...prev, cta])
  }, [])

  const addFeatures = useCallback(() => {
    const features: Section = { type: 'features', props: { items: ['Feature A', 'Feature B', 'Feature C'] } }
    setSections((prev) => [...prev, features])
  }, [])

  const addTestimonials = useCallback(() => {
    const testimonials: Section = {
      type: 'testimonials',
      props: { items: [{ author: 'Jane', quote: 'Great product!' }, { author: 'John', quote: 'Loved it.' }] },
    }
    setSections((prev) => [...prev, testimonials])
  }, [])

  const removeAt = useCallback((idx: number) => {
    setSections((prev) => prev.filter((_, i) => i !== idx))
  }, [])

  const moveSection = useCallback((from: number, to: number) => {
    setSections((prev) => {
      if (from < 0 || to < 0 || from >= prev.length || to >= prev.length) return prev
      const next = prev.slice()
      const [sp] = next.splice(from, 1)
      next.splice(to, 0, sp)
      return next
    })
  }, [])

  const pageData = useMemo<Page>(() => {
    const firstName = selectedFileName

    const mappedSections = sections.map((s: Section) => {
      if (s.type === 'hero') {
        return {
          type: 'hero' as const,
          props: {
            heading: s.props?.heading || 'Welcome',
            subheading: s.props?.subheading || '',
            imageUrl: firstName ? `local:${firstName}` : 'https://via.placeholder.com/800x400',
          },
        }
      }
      return s
    })

    return ({
      title,
      locale,
      theme: { primaryColor, fontFamily },
      sections: (mappedSections as unknown) as Page['sections'],
      form: formState,
      analytics: { ga4MeasurementId, metaPixelId, customHeadHtml: '' },
      seo: { title: seoTitle || title, description: (seoDescription && seoDescription.trim()) ? seoDescription : 'Landing page', ogImage: (ogImage && ogImage.trim()) ? ogImage : undefined },
    } as unknown) as Page
  }, [title, locale, primaryColor, fontFamily, sections, formState, selectedFileName, seoTitle, seoDescription, ga4MeasurementId, metaPixelId, ogImage])

  // test-only: expose preview exporter for E2E to read exported HTML/CSS/JS directly
  useEffect(() => {
    try {
      if (typeof window !== 'undefined') {
        const w = window as unknown as { __exportPreview?: () => { html: string; css: string; js: string } }
        // Build an effective AssetManager based on selected path strategy
        const buildEffectiveAm = (): AssetManager | undefined => {
          const base = am ?? undefined
          if (!base) return undefined
          if (assetPathStrategy === 'flat') return base
          const clone = new AssetManager({ maxBytes: 10 * 1024 * 1024, allowedExts: ['png', 'jpg', 'jpeg', 'webp', 'svg'], pathStrategy: assetPathStrategy })
          for (const ref of base.list()) {
            // re-add to apply new path strategy and recreate byOriginal mapping
            clone.add({ name: ref.originalName, type: ref.mimeType, bytes: ref.bytes })
          }
          return clone
        }
        w.__exportPreview = () => {
          try {
            // Export preview with current optimization options
            return exportPage(pageSchema.parse(pageData), { assetManager: buildEffectiveAm(), fontPreconnect })
          } catch {
            const fixed: Page = {
              ...pageData,
              form: {
                actionUrl: pageData.form?.actionUrl ?? '',
                fields: pageData.form?.fields ?? [],
                spamProtection: pageData.form?.spamProtection ?? { honeypotFieldName: '_hp', minSubmitSeconds: 2 },
              },
            }
            return exportPage(pageSchema.parse(fixed), { assetManager: buildEffectiveAm(), fontPreconnect })
          }
        }
      }
    } catch {}
  }, [pageData, am, fontPreconnect, assetPathStrategy])

  const doExport = useCallback(async () => {
    let valid = pageData as Page
    try {
      valid = pageSchema.parse(pageData)
    } catch {
      const fixed: Page = {
        ...pageData,
        form: {
          actionUrl: pageData.form?.actionUrl ?? '',
          fields: pageData.form?.fields ?? [],
          spamProtection: pageData.form?.spamProtection ?? { honeypotFieldName: '_hp', minSubmitSeconds: 2 },
        },
      } as Page
      valid = pageSchema.parse(fixed)
    }
    // Build an effective AssetManager based on selected path strategy
    const buildEffectiveAm = (): AssetManager | undefined => {
      const base = am ?? undefined
      if (!base) return undefined
      if (assetPathStrategy === 'flat') return base
      const clone = new AssetManager({ maxBytes: 10 * 1024 * 1024, allowedExts: ['png', 'jpg', 'jpeg', 'webp', 'svg'], pathStrategy: assetPathStrategy })
      for (const ref of base.list()) {
        clone.add({ name: ref.originalName, type: ref.mimeType, bytes: ref.bytes })
      }
      return clone
    }
    // Respect font optimization toggle when exporting
    const exported = exportPage(valid, { assetManager: buildEffectiveAm(), fontPreconnect })
    try {
      if (typeof window !== 'undefined') {
        const w = window as unknown as { __captureExport?: boolean; __lastExport?: { html: string; css: string; js: string } }
        if (w.__captureExport) {
          w.__lastExport = exported
        }
      }
    } catch {}
    const effectiveAm = (() => {
      const base = am ?? undefined
      if (!base) return undefined
      if (assetPathStrategy === 'flat') return base
      const clone = new AssetManager({ maxBytes: 10 * 1024 * 1024, allowedExts: ['png', 'jpg', 'jpeg', 'webp', 'svg'], pathStrategy: assetPathStrategy })
      for (const ref of base.list()) {
        clone.add({ name: ref.originalName, type: ref.mimeType, bytes: ref.bytes })
      }
      return clone
    })()
    const assets = effectiveAm ? effectiveAm.toZipStructure() : {}
    // Build overrides from UI options
    const manifestOverrideBase: Record<string, string> = {}
    if (manifestName && manifestName.trim().length > 0) manifestOverrideBase.name = manifestName.trim()
    if (manifestShortName && manifestShortName.trim().length > 0) manifestOverrideBase.short_name = manifestShortName.trim()
    if (manifestStartUrl && manifestStartUrl.trim().length > 0) manifestOverrideBase.start_url = manifestStartUrl.trim()
    if (manifestDisplay && manifestDisplay.trim().length > 0) manifestOverrideBase.display = manifestDisplay.trim()
    if (manifestThemeColor && manifestThemeColor.trim().length > 0) {
      const v = manifestThemeColor.trim()
      const hexOk = /^#([0-9a-fA-F]{3}){1,2}$/.test(v)
      if (hexOk) {
        manifestOverrideBase.theme_color = v
      }
    }
    const manifestOverride = Object.keys(manifestOverrideBase).length > 0 ? (manifestOverrideBase as { name?: string; short_name?: string; start_url?: string; display?: string; theme_color?: string }) : undefined
    const robotsTextOverride = (() => {
      const lines = (robotsDisallow || '').split(/\n+/).map((s) => s.trim()).filter((s) => s.length > 0)
      if (lines.length === 0) return undefined
      const disallowLines = lines.map((l) => (l.startsWith('Disallow:') ? l : `Disallow: ${l}`))
      return `User-agent: *\n${disallowLines.join('\n')}`
    })()
    const baseExtras = buildExtrasForPage(valid, { robotsText: robotsTextOverride, manifest: manifestOverride, sheetsMode })
    const extras: Record<string, Uint8Array> = {
      ...baseExtras,
      ...(am ? { 'assets.json': buildAssetsMetadata(am), 'assets.index.json': buildAssetsIndex(am) } : {}),
    }
    const zipBytes = await buildZip({ html: exported.html, css: exported.css, js: exported.js, assets, extras })
    download('landing.zip', zipBytes)
  }, [pageData, am, manifestName, manifestShortName, manifestStartUrl, manifestDisplay, manifestThemeColor, robotsDisallow, sheetsMode, fontPreconnect, assetPathStrategy])

  return (
    <div className="p-6 space-y-6">
      <h1 className="text-2xl font-semibold">{t('builder.title')}</h1>

      <BuilderShell
        left={
          <div className="space-y-4">
            <SidebarComponents
              onAddHero={addHero}
              onAddFAQ={addFaq}
              onAddCTA={addCta}
              onAddFeatures={addFeatures}
              onAddTestimonials={addTestimonials}
            />
            <FormBuilderPanel store={formStore} />
          </div>
        }
        center={
          <div className="space-y-4">
            <div className="space-y-2">
              <label className="block text-sm font-medium" htmlFor="builder-title">{t('builder.field.title')}</label>
              <input id="builder-title" className="border rounded px-3 py-2 w-full" value={title} onChange={(e) => setTitle(e.target.value)} />
            </div>
            <div className="flex gap-4">
              <div className="flex-1">
                <label className="block text-sm font-medium" htmlFor="builder-locale">{t('builder.field.locale')}</label>
                <select id="builder-locale" className="border rounded px-3 py-2 w-full" value={locale} onChange={(e) => setLocale(e.target.value as 'en' | 'ko')}>
                  <option value="en">en</option>
                  <option value="ko">ko</option>
                </select>
              </div>
              <div className="flex-1">
                <label className="block text-sm font-medium" htmlFor="builder-primary-color">{t('builder.field.primaryColor')}</label>
                <input id="builder-primary-color" type="color" className="border rounded px-2 py-2 w-full h-10" value={primaryColor} onChange={(e) => setPrimaryColor(e.target.value)} />
              </div>
              <div className="flex-1">
                <label className="block text-sm font-medium" htmlFor="builder-font-family">{t('builder.field.fontFamily')}</label>
                <input id="builder-font-family" className="border rounded px-3 py-2 w-full" value={fontFamily} onChange={(e) => setFontFamily(e.target.value)} />
              </div>
            </div>

            <div className="space-y-2">
              <label className="block text-sm font-medium" htmlFor="builder-hero-file">{t('builder.upload.hero')}</label>
              <input id="builder-hero-file" ref={fileInputRef} type="file" accept="image/*" className="block" />
            </div>

            <div className="flex gap-2">
              <button data-testid="btn-add-hero" className="bg-sky-600 text-white rounded px-4 py-2" onClick={addHero}>{t('builder.action.addHero')}</button>
              <button data-testid="btn-add-faq" className="bg-gray-200 rounded px-4 py-2" onClick={addFaq}>{t('builder.action.addFAQ')}</button>
              <button data-testid="btn-add-cta" className="bg-gray-200 rounded px-4 py-2" onClick={addCta}>{t('builder.action.addCTA')}</button>
            </div>

            <div className="space-y-2">
              <label className="block text-sm font-medium">{t('builder.sections')}</label>
              <SectionCanvas
                sections={sections}
                onMove={moveSection}
                onRemove={removeAt}
                onSelect={(i) => setSelectedIndex(i)}
              />
            </div>
            <div className="border rounded p-3 space-y-2">
              <h2 className="text-sm font-semibold mb-1">Export Options</h2>
              {/* Asset path strategy: flat vs grouped */}
              <label className="block" htmlFor="ins-asset-path-strategy">
                <span className="sr-only">Asset Path Strategy</span>
                <select
                  id="ins-asset-path-strategy"
                  aria-label="Asset Path Strategy"
                  className="border rounded px-3 py-2 w-full"
                  value={assetPathStrategy}
                  onChange={(e) => setAssetPathStrategy(e.target.value as 'flat' | 'grouped')}
                >
                  <option value="flat">Assets Path: Flat</option>
                  <option value="grouped">Assets Path: Grouped (images/fonts/other)</option>
                </select>
              </label>
              {/* Font optimization toggle for Google Fonts preconnect */}
              <label className="flex items-center gap-2" htmlFor="ins-font-preconnect">
                <input
                  id="ins-font-preconnect"
                  type="checkbox"
                  aria-label="Font Preconnect"
                  checked={fontPreconnect}
                  onChange={(e) => setFontPreconnect(e.target.checked)}
                />
                <span className="text-sm">Enable font preconnect (Google Fonts)</span>
              </label>
              <label className="block" htmlFor="ins-manifest-name">
                <span className="sr-only">Manifest Name</span>
                <input
                  id="ins-manifest-name"
                  aria-label="Manifest Name"
                  className="border rounded px-3 py-2 w-full"
                  value={manifestName}
                  onChange={(e) => setManifestName(e.target.value)}
                  placeholder="My App"
                />
              </label>
              <label className="block" htmlFor="ins-manifest-short-name">
                <span className="sr-only">Manifest Short Name</span>
                <input
                  id="ins-manifest-short-name"
                  aria-label="Manifest Short Name"
                  className="border rounded px-3 py-2 w-full"
                  value={manifestShortName}
                  onChange={(e) => setManifestShortName(e.target.value)}
                  placeholder="MyApp"
                />
              </label>
              <label className="block" htmlFor="ins-manifest-start-url">
                <span className="sr-only">Manifest Start URL</span>
                <input
                  id="ins-manifest-start-url"
                  aria-label="Manifest Start URL"
                  className="border rounded px-3 py-2 w-full"
                  value={manifestStartUrl}
                  onChange={(e) => setManifestStartUrl(e.target.value)}
                  placeholder="/"
                />
              </label>
              <label className="block" htmlFor="ins-manifest-display">
                <span className="sr-only">Manifest Display</span>
                <select
                  id="ins-manifest-display"
                  aria-label="Manifest Display"
                  className="border rounded px-3 py-2 w-full"
                  value={manifestDisplay}
                  onChange={(e) => setManifestDisplay(e.target.value)}
                >
                  <option value="">(default)</option>
                  <option value="standalone">standalone</option>
                  <option value="minimal-ui">minimal-ui</option>
                  <option value="browser">browser</option>
                </select>
              </label>
              <label className="block" htmlFor="ins-manifest-theme-color">
                <span className="sr-only">Manifest Theme Color</span>
                <input
                  id="ins-manifest-theme-color"
                  aria-label="Manifest Theme Color"
                  className="border rounded px-3 py-2 w-full"
                  value={manifestThemeColor}
                  onChange={(e) => setManifestThemeColor(e.target.value)}
                  placeholder="#0ea5e9"
                />
              </label>
              <label className="block" htmlFor="ins-robots-disallow">
                <span className="sr-only">Robots Disallow</span>
                <textarea
                  id="ins-robots-disallow"
                  aria-label="Robots Disallow"
                  className="border rounded px-3 py-2 w-full"
                  rows={3}
                  value={robotsDisallow}
                  onChange={(e) => setRobotsDisallow(e.target.value)}
                  placeholder="/private\n/tmp"
                />
              </label>
              <label className="block" htmlFor="ins-sheets-mode">
                <span className="sr-only">Google Sheets Template Mode</span>
                <select
                  id="ins-sheets-mode"
                  aria-label="Google Sheets Template Mode"
                  className="border rounded px-3 py-2 w-full"
                  value={sheetsMode}
                  onChange={(e) => setSheetsMode(e.target.value as 'auto'|'include'|'exclude')}
                >
                  <option value="auto">Sheets Template: Auto</option>
                  <option value="include">Sheets Template: Include</option>
                  <option value="exclude">Sheets Template: Exclude</option>
                </select>
              </label>
            </div>
          </div>
        }
        right={
          <div className="space-y-3">
            <button className="bg-emerald-600 text-white rounded px-4 py-2 w-full" onClick={doExport}>{t('builder.action.export')}</button>
            <div className="border rounded p-3 space-y-2">
              <h2 className="text-sm font-semibold mb-1">SEO / Analytics</h2>
              <label className="block" htmlFor="ins-seo-title">
                <span className="sr-only">SEO Title</span>
                <input
                  id="ins-seo-title"
                  aria-label="SEO Title"
                  aria-describedby="ins-seo-title-help"
                  className="border rounded px-3 py-2 w-full"
                  placeholder="e.g. Product landing page"
                  value={seoTitle}
                  onChange={(e) => setSeoTitle(e.target.value)}
                />
                <p id="ins-seo-title-help" className="mt-1 text-xs text-gray-500">Shown in browser title and social previews</p>
              </label>
              <label className="block" htmlFor="ins-seo-desc">
                <span className="sr-only">SEO Description</span>
                <input
                  id="ins-seo-desc"
                  aria-label="SEO Description"
                  aria-describedby="ins-seo-desc-help"
                  className="border rounded px-3 py-2 w-full"
                  placeholder="Short description shown in search/OG"
                  value={seoDescription}
                  onChange={(e) => setSeoDescription(e.target.value)}
                />
                <p id="ins-seo-desc-help" className="mt-1 text-xs text-gray-500">Used by search engines and OG description</p>
              </label>
              <label className="block" htmlFor="ins-og-image">
                <span className="sr-only">OG Image URL</span>
                <input
                  id="ins-og-image"
                  aria-label="OG Image URL"
                  aria-describedby="ins-og-image-help"
                  className="border rounded px-3 py-2 w-full"
                  placeholder="https://.../og-image.png"
                  value={ogImage}
                  onChange={(e) => setOgImage(e.target.value)}
                />
                <p id="ins-og-image-help" className="mt-1 text-xs text-gray-500">Absolute URL recommended</p>
              </label>
              <label className="block" htmlFor="ins-ga4">
                <span className="sr-only">GA4 Measurement ID</span>
                <input
                  id="ins-ga4"
                  aria-label="GA4 Measurement ID"
                  aria-describedby="ins-ga4-help"
                  className="border rounded px-3 py-2 w-full"
                  placeholder="G-XXXXXXXXXX"
                  value={ga4MeasurementId}
                  onChange={(e) => setGa4MeasurementId(e.target.value)}
                />
                <p id="ins-ga4-help" className="mt-1 text-xs text-gray-500">Format: G-XXXXXXXXXX</p>
              </label>
              <label className="block" htmlFor="ins-meta-pixel">
                <span className="sr-only">Meta Pixel ID</span>
                <input
                  id="ins-meta-pixel"
                  aria-label="Meta Pixel ID"
                  aria-describedby="ins-meta-pixel-help"
                  className="border rounded px-3 py-2 w-full"
                  placeholder="1234567890"
                  value={metaPixelId}
                  onChange={(e) => setMetaPixelId(e.target.value)}
                />
                <p id="ins-meta-pixel-help" className="mt-1 text-xs text-gray-500">Numeric Page ID</p>
              </label>
            </div>
            <div className="border rounded p-3">
              <h2 className="text-sm font-semibold mb-2">Inspector</h2>
              {selectedIndex !== null && sections[selectedIndex] ? (
                (() => {
                  const s = sections[selectedIndex]
                  if (s.type === 'hero') {
                    const heading = s.props?.heading ?? 'Welcome'
                    return (
                      <div className="space-y-2">
                        <label className="block" htmlFor="ins-hero-heading">
                          <span className="sr-only">Hero Heading</span>
                          <input
                            id="ins-hero-heading"
                            aria-label="Hero Heading"
                            aria-describedby="ins-hero-heading-help"
                            className="border rounded px-3 py-2 w-full"
                            value={heading}
                            onChange={(e) => {
                              const v = e.target.value
                              setSections((prev) => prev.map((it, idx) => {
                                if (idx !== selectedIndex) return it
                                return { ...it, props: { ...(it.props || {}), heading: v } } as Section
                              }))
                            }}
                          />
                        </label>
                        <p id="ins-hero-heading-help" className="mt-1 text-xs text-gray-500">Main heading displayed prominently</p>
                      </div>
                    )
                  }
                  if (s.type === 'cta') {
                    const text = s.props?.text ?? 'Get Started'
                    const href = s.props?.href ?? '#'
                    return (
                      <div className="space-y-2">
                        <label className="block" htmlFor="ins-cta-text">
                          <span className="sr-only">CTA Text</span>
                          <input
                            id="ins-cta-text"
                            aria-label="CTA Text"
                            aria-describedby="ins-cta-text-help"
                            className="border rounded px-3 py-2 w-full"
                            value={text}
                            onChange={(e) => {
                              const v = e.target.value
                              setSections((prev) => prev.map((it, idx) => {
                                if (idx !== selectedIndex) return it
                                return { ...it, props: { ...(it.props || {}), text: v } } as Section
                              }))
                            }}
                          />
                        </label>
                        <p id="ins-cta-text-help" className="mt-1 text-xs text-gray-500">Button label and link for primary action</p>
                        <label className="block" htmlFor="ins-cta-href">
                          <span className="sr-only">CTA Href</span>
                          <input
                            id="ins-cta-href"
                            aria-label="CTA Href"
                            aria-describedby="ins-cta-href-help"
                            className="border rounded px-3 py-2 w-full"
                            value={href}
                            onChange={(e) => {
                              const v = e.target.value
                              setSections((prev) => prev.map((it, idx) => {
                                if (idx !== selectedIndex) return it
                                return { ...it, props: { ...(it.props || {}), href: v } } as Section
                              }))
                            }}
                          />
                        </label>
                        <p id="ins-cta-href-help" className="mt-1 text-xs text-gray-500">Button label and link for primary action</p>
                      </div>
                    )
                  }
                  if (s.type === 'faq') {
                    const items: Array<{ q: string; a: string }> = Array.isArray(s.props?.items) ? (s.props.items as Array<{ q: string; a: string }>) : []
                    const addItem = () => {
                      const nextItems = [...items, { q: '', a: '' }]
                      setSections((prev) => prev.map((it, idx) => {
                        if (idx !== selectedIndex) return it
                        return { ...it, props: { ...(it.props || {}), items: nextItems } } as Section
                      }))
                    }
                    const updateItem = (i: number, patch: { q?: string; a?: string }) => {
                      const nextItems = items.map((it: { q: string; a: string }, idx: number) => (idx === i ? { ...it, ...patch } : it))
                      setSections((prev) => prev.map((it, idx) => {
                        if (idx !== selectedIndex) return it
                        return { ...it, props: { ...(it.props || {}), items: nextItems } } as Section
                      }))
                    }
                    const removeItem = (i: number) => {
                      const nextItems = items.filter((_: { q: string; a: string }, idx: number) => idx !== i)
                      setSections((prev) => prev.map((it, idx) => {
                        if (idx !== selectedIndex) return it
                        return { ...it, props: { ...(it.props || {}), items: nextItems } } as Section
                      }))
                    }
                    return (
                      <div className="space-y-2">
                        <button type="button" className="px-2 py-1 border rounded text-sm" title="Add FAQ Item" onClick={addItem}>Add FAQ Item</button>
                        <p id="ins-faq-help" className="text-xs text-gray-500">Add at least one FAQ item with Q and A</p>
                        <ul className="space-y-2">
                          {items.map((it: { q: string; a: string }, i: number) => (
                            <li key={i} className="flex items-start gap-2">
                              <div className="flex-1 grid grid-cols-2 gap-2">
                                <label className="block" htmlFor={`ins-faq-q-${i}`}>
                                  <span className="sr-only">FAQ Q {i}</span>
                                  <input
                                    id={`ins-faq-q-${i}`}
                                    aria-label={`FAQ Q ${i}`}
                                    aria-describedby="ins-faq-help"
                                    className="border rounded px-2 py-1 w-full"
                                    value={it.q ?? ''}
                                    onChange={(e) => updateItem(i, { q: e.target.value })}
                                  />
                                </label>
                                <label className="block" htmlFor={`ins-faq-a-${i}`}>
                                  <span className="sr-only">FAQ A {i}</span>
                                  <input
                                    id={`ins-faq-a-${i}`}
                                    aria-label={`FAQ A ${i}`}
                                    aria-describedby="ins-faq-help"
                                    className="border rounded px-2 py-1 w-full"
                                    value={it.a ?? ''}
                                    onChange={(e) => updateItem(i, { a: e.target.value })}
                                  />
                                </label>
                              </div>
                              <button type="button" aria-label={`Remove FAQ ${i}`} className="px-2 py-1 border rounded text-sm text-red-600" onClick={() => removeItem(i)}>Remove</button>
                            </li>
                          ))}
                        </ul>
                      </div>
                    )
                  }
                  if (s.type === 'features') {
                    const items: string[] = Array.isArray(s.props?.items) ? (s.props.items as string[]) : []
                    const addItem = () => {
                      const nextItems = [...items, '']
                      setSections((prev) => prev.map((it, idx) => (idx === selectedIndex ? ({ ...it, props: { ...(it.props || {}), items: nextItems } } as Section) : it)))
                    }
                    const updateItem = (i: number, v: string) => {
                      const nextItems = items.map((it, idx) => (idx === i ? v : it))
                      setSections((prev) => prev.map((it, idx) => (idx === selectedIndex ? ({ ...it, props: { ...(it.props || {}), items: nextItems } } as Section) : it)))
                    }
                    const removeItem = (i: number) => {
                      const nextItems = items.filter((_, idx) => idx !== i)
                      setSections((prev) => prev.map((it, idx) => (idx === selectedIndex ? ({ ...it, props: { ...(it.props || {}), items: nextItems } } as Section) : it)))
                    }
                    return (
                      <div className="space-y-2">
                        <button type="button" className="px-2 py-1 border rounded text-sm" title="Add Feature Item" onClick={addItem}>Add Feature Item</button>
                        <p id="ins-features-help" className="text-xs text-gray-500">Describe a single feature per line</p>
                        <ul className="space-y-2">
                          {items.map((val, i) => (
                            <li key={i} className="flex items-center gap-2">
                              <label className="block flex-1" htmlFor={`ins-feature-${i}`}>
                                <span className="sr-only">Feature {i}</span>
                                <input
                                  id={`ins-feature-${i}`}
                                  aria-label={`Feature ${i}`}
                                  aria-describedby="ins-features-help"
                                  className="border rounded px-2 py-1 w-full"
                                  value={val}
                                  onChange={(e) => updateItem(i, e.target.value)}
                                />
                              </label>
                              <button
                                type="button"
                                aria-label={`Remove Feature ${i}`}
                                title={`Remove Feature ${i}`}
                                className="px-2 py-1 border rounded text-sm text-red-600"
                                onClick={() => removeItem(i)}
                                onKeyDown={(e) => {
                                  if (e.key === 'Enter' || e.key === ' ') removeItem(i)
                                }}
                                onKeyUp={(e) => {
                                  if (e.key === 'Enter' || e.key === ' ') removeItem(i)
                                }}
                              >
                                Remove
                              </button>
                            </li>
                          ))}
                        </ul>
                      </div>
                    )
                  }
                  if (s.type === 'testimonials') {
                    const items: Array<{ author: string; quote: string }> = Array.isArray(s.props?.items)
                      ? (s.props.items as Array<{ author: string; quote: string }>)
                      : []
                    const addItem = () => {
                      const nextItems = [...items, { author: '', quote: '' }]
                      setSections((prev) => prev.map((it, idx) => (idx === selectedIndex ? ({ ...it, props: { ...(it.props || {}), items: nextItems } } as Section) : it)))
                    }
                    const updateItem = (i: number, patch: Partial<{ author: string; quote: string }>) => {
                      const nextItems = items.map((it, idx) => (idx === i ? { ...it, ...patch } : it))
                      setSections((prev) => prev.map((it, idx) => (idx === selectedIndex ? ({ ...it, props: { ...(it.props || {}), items: nextItems } } as Section) : it)))
                    }
                    const removeItem = (i: number) => {
                      const nextItems = items.filter((_, idx) => idx !== i)
                      setSections((prev) => prev.map((it, idx) => (idx === selectedIndex ? ({ ...it, props: { ...(it.props || {}), items: nextItems } } as Section) : it)))
                    }
                    return (
                      <div className="space-y-2">
                        <button type="button" className="px-2 py-1 border rounded text-sm" title="Add Testimonial" onClick={addItem}>Add Testimonial</button>
                        <p id="ins-testimonials-help" className="text-xs text-gray-500">Add author and quote for each testimonial</p>
                        <ul className="space-y-2">
                          {items.map((it, i) => (
                            <li key={i} className="grid grid-cols-2 gap-2 items-start">
                              <label className="block" htmlFor={`ins-author-${i}`}>
                                <span className="sr-only">Author {i}</span>
                                <input
                                  id={`ins-author-${i}`}
                                  aria-label={`Author ${i}`}
                                  aria-describedby="ins-testimonials-help"
                                  className="border rounded px-2 py-1 w-full"
                                  value={it.author}
                                  onChange={(e) => updateItem(i, { author: e.target.value })}
                                />
                              </label>
                              <label className="block" htmlFor={`ins-quote-${i}`}>
                                <span className="sr-only">Quote {i}</span>
                                <input
                                  id={`ins-quote-${i}`}
                                  aria-label={`Quote ${i}`}
                                  aria-describedby="ins-testimonials-help"
                                  className="border rounded px-2 py-1 w-full"
                                  value={it.quote}
                                  onChange={(e) => updateItem(i, { quote: e.target.value })}
                                />
                              </label>
                              <div className="col-span-2">
                                <button
                                  type="button"
                                  aria-label={`Remove Testimonial ${i}`}
                                  title={`Remove Testimonial ${i}`}
                                  className="px-2 py-1 border rounded text-sm text-red-600"
                                  onClick={() => removeItem(i)}
                                  onKeyDown={(e) => {
                                    if (e.key === 'Enter' || e.key === ' ') removeItem(i)
                                  }}
                                  onKeyUp={(e) => {
                                    if (e.key === 'Enter' || e.key === ' ') removeItem(i)
                                  }}
                                >
                                  Remove
                                </button>
                              </div>
                            </li>
                          ))}
                        </ul>
                      </div>
                    )
                  }
                  return <div className="text-xs text-gray-500">No editable fields</div>
                })()
              ) : (
                <div className="text-xs text-gray-500">Nothing selected</div>
              )}
            </div>
            <div className="border rounded p-3">
              <h2 className="text-sm font-semibold mb-2">{t('builder.preview')}</h2>
              <p id="preview-toggle-help" className="sr-only">Toggle preview viewport for the builder preview area</p>
              <div className="mb-2 flex gap-2">
                <button
                  type="button"
                  aria-label="Desktop"
                  aria-pressed={viewport === 'desktop'}
                  aria-controls="preview-container"
                  aria-describedby="preview-toggle-help"
                  className={`rounded px-2 py-1 text-sm transition-colors border focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-600 ${
                    viewport === 'desktop'
                      ? 'bg-sky-600 text-white border-sky-600'
                      : 'bg-white text-gray-700 border-gray-300 hover:bg-gray-50'
                  }`}
                  onClick={() => setViewport('desktop')}
                >
                  Desktop
                </button>
                <button
                  type="button"
                  aria-label="Mobile"
                  aria-pressed={viewport === 'mobile'}
                  aria-controls="preview-container"
                  aria-describedby="preview-toggle-help"
                  className={`rounded px-2 py-1 text-sm transition-colors border focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-600 ${
                    viewport === 'mobile'
                      ? 'bg-sky-600 text-white border-sky-600'
                      : 'bg-white text-gray-700 border-gray-300 hover:bg-gray-50'
                  }`}
                  onClick={() => setViewport('mobile')}
                >
                  Mobile
                </button>
              </div>
              <div className="prose max-w-none">
                <div
                  data-testid="preview-container"
                  className={`border rounded p-3 ${viewport === 'desktop' ? 'w-[768px]' : 'w-[375px]'} mx-auto`}
                >
                <div className="mb-2"><strong>Title:</strong> {pageData.title}</div>
                <div className="mb-2"><strong>Locale:</strong> {pageData.locale}</div>
                <div className="mb-2"><strong>SEO Title:</strong> <span data-testid="preview-seo-title">{pageData.seo.title}</span></div>
                <div className="mb-2"><strong>SEO Description:</strong> <span data-testid="preview-seo-description">{pageData.seo.description}</span></div>
                <div className="mb-2"><strong>OG Image:</strong> <span data-testid="preview-og-image">{pageData.seo.ogImage || ''}</span></div>
                <div className="mb-2"><strong>GA4:</strong> <span data-testid="preview-ga4">{pageData.analytics?.ga4MeasurementId || ''}</span></div>
                <div className="mb-2"><strong>Meta Pixel:</strong> <span data-testid="preview-meta-pixel">{pageData.analytics?.metaPixelId || ''}</span></div>
                <div className="space-y-2">
                  {sections.map((s: Section, i: number) => {
                    switch (s.type) {
                      case 'hero':
                        return (
                          <div key={i} className="text-sm text-gray-700">• hero — heading: {s.props?.heading || 'Welcome'}</div>
                        )
                      case 'faq': {
                        const count = s.props.items?.length || 0
                        return (
                          <div key={i} className="text-sm text-gray-700">• faq — items: {count}</div>
                        )
                      }
                      case 'cta': {
                        const text = s.props.text || 'CTA'
                        const href = s.props.href || '#'
                        return (
                          <div key={i} className="text-sm text-gray-700">• cta — {text} → {href}</div>
                        )
                      }
                      case 'features': {
                        const count = s.props.items?.length || 0
                        return (
                          <div key={i} className="text-sm text-gray-700">• features — items: {count}</div>
                        )
                      }
                      case 'testimonials': {
                        const count = s.props.items?.length || 0
                        return (
                          <div key={i} className="text-sm text-gray-700">• testimonials — items: {count}</div>
                        )
                      }
                      default:
                        return null
                    }
                  })}
                </div>
                <ul className="list-disc pl-5 mt-2">
                  {formState.fields.map((f, idx) => (
                    <li key={`${f.name}-${idx}`} className="text-sm">
                      {f.label} ({f.name}) {f.required ? 'required' : ''}
                    </li>
                  ))}
                </ul>
                </div>
              </div>
            </div>
          </div>
        }
      />
    </div>
  )
}
