import { useState, useEffect, useRef, useCallback } from 'react'
import { Link, Route, Routes } from 'react-router-dom'
import JSZip from 'jszip'
import Gallery from './Gallery'
import ImageLightbox from './ImageLightbox'

const API_BASE = '/api'
const SESSION_STORAGE_KEY = 'hawbeez_last_run'
const AUTH_TOKEN_KEY = 'hawbeez_auth_token'

/** Use backend proxy for image URLs to avoid CORS when loading from R2. */
function proxyImageUrl(url) {
  return url ? `${API_BASE}/proxy-image?url=${encodeURIComponent(url)}` : ''
}

const TARGET_AUDIENCE_OPTIONS = [
  "Parents of toddlers (1–2.5 yrs)",
  "Parents of 3–5 yrs",
  "Gift buyers (kids)",
  "Montessori / alternative education",
  "Urban Tier 1 parents",
  "Urban Tier 2 parents",
  "First-time parents",
  "Eco-conscious parents",
  "Value-seeking parents (combos/deals)",
  "Grandparents (gifting)",
  "Screen-time worriers",
  "Learning-through-play seekers",
  "Educational toy buyers",
  "Wooden toy fans",
  "Birthday / party gifting",
  "Festive gifting (Diwali, Children's Day)",
  "COD / UPI-first",
  "Instagram / Facebook parents",
  "D2C toy buyers",
  "₹1k–₹3k toy buyers",
  "Repeat Hawbeez buyers",
  "Desk organizer / school supplies",
  "Bilingual (Hindi/English) families",
  "Preschool / nursery parents",
  "KG / early primary parents",
]

function authHeaders() {
  const token = typeof localStorage !== 'undefined' ? localStorage.getItem(AUTH_TOKEN_KEY) : null
  if (!token) return {}
  return { Authorization: `Bearer ${token}` }
}

function LoginPage({ onLogin }) {
  const [username, setUsername] = useState('')
  const [password, setPassword] = useState('')
  const [error, setError] = useState(null)
  const [loading, setLoading] = useState(false)

  async function handleSubmit(e) {
    e.preventDefault()
    setError(null)
    setLoading(true)
    try {
      const res = await fetch(`${API_BASE}/auth/login`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ username: username.trim(), password }),
      })
      const data = await res.json().catch(() => ({}))
      if (!res.ok) {
        setError(data.detail || 'Login failed')
        return
      }
      if (data.access_token) {
        localStorage.setItem(AUTH_TOKEN_KEY, data.access_token)
        onLogin(data.access_token)
      }
    } catch (_) {
      setError('Network error')
    } finally {
      setLoading(false)
    }
  }

  return (
    <div className="app login-page">
      <div className="promo-strip">Ad Creative Studio — Hawbeez</div>
      <header className="header header--centered">
        <div className="header-brand">
          <img src="/api/logo" alt="Hawbeez" className="header-logo" />
          <h1>Hawbeez</h1>
          <p className="tagline">Eco-friendly Toys · Ad Creative Studio</p>
        </div>
      </header>
      <section className="card login-card">
        <h2>Sign in</h2>
        <p className="login-hint">Use your Hawbeez studio account.</p>
        <form onSubmit={handleSubmit} className="login-form">
          <label>
            <span className="label-text">Username</span>
            <input
              type="text"
              autoComplete="username"
              value={username}
              onChange={(e) => setUsername(e.target.value)}
              placeholder="Username"
              required
            />
          </label>
          <label>
            <span className="label-text">Password</span>
            <input
              type="password"
              autoComplete="current-password"
              value={password}
              onChange={(e) => setPassword(e.target.value)}
              placeholder="Password"
              required
            />
          </label>
          {error && <p className="login-error">{error}</p>}
          <button type="submit" className="btn-login" disabled={loading}>
            {loading ? 'Signing in…' : 'Sign in'}
          </button>
        </form>
      </section>
      <footer className="footer">
        <p>Because love deserves a little extra ❤️</p>
      </footer>
    </div>
  )
}

export default function App() {
  const [token, setToken] = useState(() => (typeof localStorage !== 'undefined' ? localStorage.getItem(AUTH_TOKEN_KEY) : null))
  const [url, setUrl] = useState('')
  const [loading, setLoading] = useState(false)
  const [streamStep, setStreamStep] = useState(null)
  const [error, setError] = useState(null)
  const [productData, setProductData] = useState(null)
  const [analysis, setAnalysis] = useState(null)
  const [adCreatives, setAdCreatives] = useState(null)
  const [selectedForAds, setSelectedForAds] = useState([])
  const [imageModels, setImageModels] = useState([])
  const [selectedImageModel, setSelectedImageModel] = useState('nano-banana')
  const [generatedAds, setGeneratedAds] = useState([])
  const [generatingAds, setGeneratingAds] = useState(false)
  const [generatingAdsTotal, setGeneratingAdsTotal] = useState(0)
  const [generateAdsError, setGenerateAdsError] = useState(null)
  const [editedCreatives, setEditedCreatives] = useState({}) // creative id -> full edited creative object
  const [referenceImageUrl, setReferenceImageUrl] = useState(null) // user custom/upload; null = use product image
  const [selectedReferenceUrls, setSelectedReferenceUrls] = useState([]) // up to 3 product image URLs user selected for ad generation; order = ref order
  const [targetAudiences, setTargetAudiences] = useState([]) // selected audience segments for analysis/creatives (multi-select)
  const [targetAudienceOpen, setTargetAudienceOpen] = useState(false)
  const targetAudienceDropdownRef = useRef(null)
  const [hasStoredSession, setHasStoredSession] = useState(false)
  const [storedSession, setStoredSession] = useState(null)
  const [lightboxImage, setLightboxImage] = useState(null)
  const [backendOpenAIReady, setBackendOpenAIReady] = useState(null) // null = not checked yet, true/false = from /api/health
  const generatedAdsSectionRef = useRef(null)
  const prevGeneratingRef = useRef(false)

  // Close target audience dropdown on outside click
  useEffect(() => {
    if (!targetAudienceOpen) return
    const el = targetAudienceDropdownRef.current
    function handleClick(e) {
      if (el && !el.contains(e.target)) setTargetAudienceOpen(false)
    }
    document.addEventListener('mousedown', handleClick)
    return () => document.removeEventListener('mousedown', handleClick)
  }, [targetAudienceOpen])

  const handleLogout = useCallback(() => {
    localStorage.removeItem(AUTH_TOKEN_KEY)
    setToken(null)
  }, [])

  // Computed for any code that still expects the old single-result shape
  const result =
    productData != null || analysis != null || (adCreatives && adCreatives.length > 0)
      ? {
          product_data: productData,
          analysis,
          ad_creatives: adCreatives || [],
        }
      : null

  useEffect(() => {
    if (!adCreatives?.length) return
    fetch(`${API_BASE}/image-models`, { headers: authHeaders() })
      .then((r) => {
        if (r.status === 401) { localStorage.removeItem(AUTH_TOKEN_KEY); setToken(null); return null }
        return r.json()
      })
      .then((data) => {
        if (!data) return
        setImageModels(data.models || [])
        if (data.default && !selectedImageModel) setSelectedImageModel(data.default)
      })
      .catch(() => {})
  }, [adCreatives?.length])

  // Check backend OpenAI config (so we can show a clear message before user clicks Generate).
  // 404 or errors (e.g. old backend without /api/health) → leave null (don't show warning).
  useEffect(() => {
    fetch(`${API_BASE}/health`)
      .then((r) => {
        if (!r.ok) return null
        return r.json()
      })
      .then((data) => {
        if (data && typeof data.openai_configured === 'boolean') {
          setBackendOpenAIReady(data.openai_configured)
        }
        // else leave backendOpenAIReady as null (unknown)
      })
      .catch(() => { /* ignore: backend may not have /api/health */ })
  }, [])

  // Restore session offer on load
  useEffect(() => {
    try {
      const raw = sessionStorage.getItem(SESSION_STORAGE_KEY)
      if (raw) {
        const data = JSON.parse(raw)
        if (data?.product_data && data?.ad_creatives?.length) {
          setHasStoredSession(true)
          setStoredSession(data)
        }
      }
    } catch (_) {}
  }, [])

  // Persist run to session when we have product + analysis + creatives
  useEffect(() => {
    if (!productData || !analysis || !adCreatives?.length) return
    try {
      sessionStorage.setItem(
        SESSION_STORAGE_KEY,
        JSON.stringify({
          product_data: productData,
          analysis,
          ad_creatives: adCreatives,
        })
      )
    } catch (_) {}
  }, [productData, analysis, adCreatives])

  // Scroll generated ads section into view when generation completes
  useEffect(() => {
    if (prevGeneratingRef.current && !generatingAds && generatedAds.length > 0) {
      generatedAdsSectionRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' })
    }
    prevGeneratingRef.current = generatingAds
  }, [generatingAds, generatedAds.length])

  async function handleRun() {
    const trimmed = url.trim()
    if (!trimmed) {
      setError('Please enter a Hawbeez product URL.')
      return
    }
    setError(null)
    setStreamStep(null)
    setProductData(null)
    setAnalysis(null)
    setAdCreatives(null)
    setSelectedForAds([])
    setGeneratedAds([])
    setGenerateAdsError(null)
    setEditedCreatives({})
    setReferenceImageUrl(null)
    setSelectedReferenceUrls([])
    setHasStoredSession(false)
    setStoredSession(null)
    try {
      sessionStorage.removeItem(SESSION_STORAGE_KEY)
    } catch (_) {}
    setLoading(true)
    try {
      const res = await fetch(`${API_BASE}/run/stream`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', ...authHeaders() },
        body: JSON.stringify({ url: trimmed, target_audience: targetAudiences.length ? targetAudiences : undefined }),
      })
      if (res.status === 401) {
        localStorage.removeItem(AUTH_TOKEN_KEY)
        setToken(null)
        return
      }
      if (!res.ok) {
        const data = await res.json().catch(() => ({}))
        throw new Error(data.detail || res.statusText)
      }
      const reader = res.body.getReader()
      const decoder = new TextDecoder()
      let buffer = ''
      while (true) {
        const { done, value } = await reader.read()
        if (done) break
        buffer += decoder.decode(value, { stream: true })
        const lines = buffer.split('\n\n')
        buffer = lines.pop() || ''
        for (const chunk of lines) {
          const dataLine = chunk.split('\n').find((l) => l.startsWith('data: '))
          if (!dataLine) continue
          try {
            const payload = JSON.parse(dataLine.slice(6))
            if (payload.event === 'step') {
              setStreamStep(payload.message || payload.step)
            } else if (payload.event === 'scrape_done') {
              setProductData(payload.data)
            } else if (payload.event === 'analysis_done') {
              setAnalysis(payload.data)
            } else if (payload.event === 'creatives_done') {
              setAdCreatives(payload.data || [])
            } else if (payload.event === 'done') {
              setStreamStep(null)
            } else if (payload.event === 'error') {
              setError(payload.message || 'Something went wrong.')
              setStreamStep(null)
            }
          } catch (_) {}
        }
      }
    } catch (e) {
      setError(e.message || 'Something went wrong.')
      setStreamStep(null)
    } finally {
      setLoading(false)
    }
  }

  if (!token) {
    return <LoginPage onLogin={(t) => setToken(t)} />
  }

  return (
    <div className="app">
      {lightboxImage && (
        <ImageLightbox
          src={lightboxImage.src}
          alt={lightboxImage.alt}
          onClose={() => setLightboxImage(null)}
        />
      )}
      <header className="header">
        <div className="header-brand">
          <img src="/api/logo" alt="Hawbeez" className="header-logo" />
          <h1>Hawbeez</h1>
          <p className="tagline">Eco-friendly Toys · Ad Creative Studio</p>
        </div>
        <div className="header-actions">
          <nav className="header-nav">
            <Link to="/" className="header-nav-link">Studio</Link>
            <Link to="/gallery" className="header-nav-link">Gallery</Link>
          </nav>
          <button
            type="button"
            className="btn-logout"
            onClick={() => {
              localStorage.removeItem(AUTH_TOKEN_KEY)
              setToken(null)
            }}
          >
            Sign out
          </button>
        </div>
      </header>

      <Routes>
        <Route path="/gallery" element={<Gallery onLogout={handleLogout} />} />
        <Route path="/" element={(
          <>
      <div className="promo-strip">
        Scrape, analyze & generate creatives for Hawbeez
      </div>

      {hasStoredSession && storedSession && (
        <div className="session-restore-bar">
          <span>You have a previous session saved.</span>
          <button
            type="button"
            className="btn-restore-session"
            onClick={() => {
              setProductData(storedSession.product_data)
              setAnalysis(storedSession.analysis)
              setAdCreatives(storedSession.ad_creatives)
              setHasStoredSession(false)
              setStoredSession(null)
            }}
          >
            Restore last session
          </button>
          <button type="button" className="btn-dismiss-session" onClick={() => { setHasStoredSession(false); setStoredSession(null) }}>
            Dismiss
          </button>
        </div>
      )}

      <section className="hero">
        {backendOpenAIReady === false && (
          <p className="error" style={{ marginBottom: '0.75rem' }}>
            Backend is missing OPENAI_API_KEY. Set it in backend/.env and restart the backend.
          </p>
        )}
        <p className="hero-desc">
          Paste a Hawbeez product page URL. We’ll scrape the product, run a deep marketing analysis, and generate ad creative packages (product & no-product) for Meta.
        </p>
        <div className="input-row">
          <input
            type="url"
            placeholder="https://hawbeez.com/products/..."
            value={url}
            onChange={(e) => setUrl(e.target.value)}
            onKeyDown={(e) => e.key === 'Enter' && handleRun()}
            disabled={loading}
            className="url-input"
          />
          <button
            type="button"
            onClick={handleRun}
            disabled={loading}
            className="btn-run"
          >
            {loading ? 'Generating…' : 'Generate'}
          </button>
        </div>
        <div className="target-audience-row" ref={targetAudienceDropdownRef}>
          <span className="target-audience-label">Target audiences (optional)</span>
          <div className="target-audience-multiselect">
            <button
              type="button"
              onClick={() => !loading && setTargetAudienceOpen((o) => !o)}
              disabled={loading}
              className="target-audience-trigger"
              aria-expanded={targetAudienceOpen}
              aria-haspopup="listbox"
            >
              {targetAudiences.length === 0
                ? 'Select target audiences…'
                : `${targetAudiences.length} selected`}
            </button>
            {targetAudienceOpen && (
              <div className="target-audience-dropdown" role="listbox">
                <div className="target-audience-actions">
                  <button
                    type="button"
                    className="target-audience-action-btn"
                    onClick={() => setTargetAudiences(TARGET_AUDIENCE_OPTIONS.slice())}
                  >
                    Select all
                  </button>
                  <button
                    type="button"
                    className="target-audience-action-btn"
                    onClick={() => setTargetAudiences([])}
                  >
                    Clear
                  </button>
                </div>
                <div className="target-audience-list">
                  {TARGET_AUDIENCE_OPTIONS.map((opt) => (
                    <label key={opt} className="target-audience-option">
                      <input
                        type="checkbox"
                        checked={targetAudiences.includes(opt)}
                        onChange={() => {
                          setTargetAudiences((prev) =>
                            prev.includes(opt) ? prev.filter((x) => x !== opt) : [...prev, opt]
                          )
                        }}
                      />
                      <span>{opt}</span>
                    </label>
                  ))}
                </div>
              </div>
            )}
          </div>
        </div>
        {error && <p className="error">{error}</p>}
      </section>

      {loading && (
        <section className="loading">
          <div className="spinner" />
          <p className="loading-step">{streamStep || 'Starting…'}</p>
          <p className="loading-hint">Results will appear below as each step completes.</p>
        </section>
      )}

      {(productData || analysis || (adCreatives && adCreatives.length > 0)) && (
        <div className="results">
          {productData && (
            <ProductCard
              data={productData}
              selectedReferenceUrls={selectedReferenceUrls}
              onToggleReference={(url) => {
                setSelectedReferenceUrls((prev) => {
                  const has = prev.includes(url)
                  if (has) return prev.filter((u) => u !== url)
                  if (prev.length >= 3) return prev
                  return [...prev, url]
                })
              }}
              onImageClick={(src, alt) => setLightboxImage({ src, alt })}
            />
          )}
          {analysis && (
            <AnalysisSection
              data={analysis}
              defaultCollapsed={!!(adCreatives && adCreatives.length > 0)}
            />
          )}
          {adCreatives && adCreatives.length > 0 && (
            <CreativesSection
              productName={productData?.product_name}
              creatives={adCreatives}
              selectedForAds={selectedForAds}
              onToggleSelect={(id) => {
                setSelectedForAds((prev) =>
                  prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]
                )
              }}
              onSelectAll={() => setSelectedForAds((adCreatives || []).map((c) => c.id))}
              onDeselectAll={() => setSelectedForAds([])}
              productImageUrl={(selectedReferenceUrls && selectedReferenceUrls[0]) || (productData?.product_images || '').split(',')[0]?.trim() || null}
              selectedReferenceUrls={selectedReferenceUrls}
              referenceImageUrl={referenceImageUrl}
              onReferenceImageUrlChange={setReferenceImageUrl}
              imageModels={imageModels}
              selectedImageModel={selectedImageModel}
              onModelChange={setSelectedImageModel}
              onGenerateAds={async () => {
                const list = adCreatives || []
                const selected = list.filter((c) => selectedForAds.includes(c.id))
                if (!selected.length) return
                const productName = productData?.product_name ?? null
                setGenerateAdsError(null)
                setGeneratingAds(true)
                setGeneratingAdsTotal(selected.length)
                setGeneratedAds([])
                const imageUrls = (productData?.product_images || '').split(',').map((s) => s.trim()).filter(Boolean)
                const refs = (selectedReferenceUrls && selectedReferenceUrls.length > 0)
                  ? selectedReferenceUrls.filter((u) => u && imageUrls.includes(u)).slice(0, 3)
                  : imageUrls.slice(0, 3)
                const primary = refs[0] || null
                const body = {
                  creatives: selected.map((c) => {
                    const base = editedCreatives[c.id] ?? c
                    const prompt = (base.scene_prompt ?? base.image_prompt ?? '').trim() || base.scene_prompt || base.image_prompt || ''
                    return { ...base, scene_prompt: prompt }
                  }),
                  model_key: selectedImageModel,
                  product_image_url: primary,
                  product_image_urls: refs.length ? refs : null,
                  reference_image_url: referenceImageUrl || null,
                  product_name: productName,
                }
                try {
                  const res = await fetch(`${API_BASE}/generate-ads/stream`, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json', ...authHeaders() },
                    body: JSON.stringify(body),
                  })
                  if (res.status === 401) {
                    localStorage.removeItem(AUTH_TOKEN_KEY)
                    setToken(null)
                    return
                  }
                  if (!res.ok) {
                    const data = await res.json().catch(() => ({}))
                    throw new Error(data.detail || res.statusText)
                  }
                  const reader = res.body.getReader()
                  const decoder = new TextDecoder()
                  let buffer = ''
                  while (true) {
                    const { done, value } = await reader.read()
                    if (done) break
                    buffer += decoder.decode(value, { stream: true })
                    const lines = buffer.split('\n\n')
                    buffer = lines.pop() || ''
                    for (const chunk of lines) {
                      const dataLine = chunk.split('\n').find((l) => l.startsWith('data: '))
                      if (!dataLine) continue
                      try {
                        const payload = JSON.parse(dataLine.slice(6))
                        if (payload.event === 'result' && payload.data) {
                          setGeneratedAds((prev) => {
                            const next = prev.filter((r) => r.creative_id !== payload.data.creative_id)
                            return [...next, payload.data]
                          })
                        } else if (payload.event === 'done') {
                          // stream finished
                        } else if (payload.event === 'error') {
                          setGenerateAdsError(payload.message || 'Generation failed')
                        }
                      } catch (_) {}
                    }
                  }
                } catch (e) {
                  setGenerateAdsError(e.message || 'Failed to generate ads')
                } finally {
                  setGeneratingAds(false)
                  setGeneratingAdsTotal(0)
                }
              }}
              generatingAds={generatingAds}
              generatingAdsTotal={generatingAdsTotal}
              generateAdsError={generateAdsError}
              generatedAds={generatedAds}
              editedCreatives={editedCreatives}
              onEditedCreativeChange={(id, creative) => setEditedCreatives((prev) => ({ ...prev, [id]: creative }))}
              generatedAdsSectionRef={generatedAdsSectionRef}
              onImageClick={(src, alt) => setLightboxImage({ src, alt })}
            />
          )}
        </div>
      )}

      <footer className="footer">
        <p>Because love deserves a little extra ❤️</p>
      </footer>
          </>
        )} />
      </Routes>
    </div>
  )
}

function ProductCard({ data, selectedReferenceUrls = [], onToggleReference, onImageClick }) {
  const imageUrls = (data?.product_images || '')
    .split(',')
    .map((s) => s.trim())
    .filter(Boolean)
  const multi = imageUrls.length > 1
  const many = imageUrls.length >= 6
  const refList = Array.isArray(selectedReferenceUrls) ? selectedReferenceUrls : []
  return (
    <section className="card product-card">
      <h2>Product</h2>
      <div className="product-grid">
        <div className={`product-gallery ${multi ? 'product-gallery--multi' : ''} ${many ? 'product-gallery--many' : ''}`}>
          {imageUrls.map((imgUrl, i) => {
            const refIndex = refList.indexOf(imgUrl)
            const isSelected = refIndex >= 0
            const refLabel = isSelected ? `Ref ${refIndex + 1}` : null
            return (
              <button
                key={i}
                type="button"
                className={`product-img-wrap ${isSelected ? 'product-img-wrap--selected' : ''}`}
                onClick={() => onToggleReference?.(imgUrl)}
                onDoubleClick={() => onImageClick?.(imgUrl, data?.product_name)}
                title={isSelected ? `Reference ${refIndex + 1} for ad generation (click to remove, double-click to enlarge)` : `Select as reference for ad generation (up to 3; double-click to enlarge)`}
              >
                <img
                  src={imgUrl}
                  alt={data?.product_name ? `${data.product_name} (${i + 1})` : ''}
                  className={`product-img ${onImageClick ? 'img-expandable' : ''}`}
                  draggable={false}
                />
                {refLabel && <span className="product-img-badge">{refLabel}</span>}
              </button>
            )
          })}
          {imageUrls.length > 0 && (
            <p className="product-gallery-note">Click images to select up to 3 as reference for ad generation (order = ref order). Used in marketing analysis.</p>
          )}
        </div>
        <div className="product-meta">
          <h3>{data?.product_name || '—'}</h3>
          <p className="product-price">{data?.price || '—'}</p>
          <p className="product-category">{data?.brand} · {data?.category || 'Jewellery'}</p>
          {data?.description && (
            <p className="product-desc">{data.description}</p>
          )}
        </div>
      </div>
    </section>
  )
}

function AnalysisSection({ data, defaultCollapsed = false }) {
  const [isExpanded, setIsExpanded] = useState(!defaultCollapsed)
  if (!data) return null
  const ideal = data.ideal_customer || {}
  const priceAnalysis = data.price_analysis || {}
  const copyDir = data.copy_direction || {}
  const MAX_PREVIEW_LEN = 180
  const positioningPreview = data.positioning
    ? (data.positioning.length <= MAX_PREVIEW_LEN
        ? data.positioning
        : data.positioning.slice(0, MAX_PREVIEW_LEN).trim() + '…')
    : ''
  const parts = []
  if (data.product_visual_notes) parts.push('product visual')
  if (data.tagline_options?.length) parts.push('taglines')
  if (ideal.age_range || ideal.lifestyle) parts.push('ideal customer')
  if (data.emotional_triggers?.length) parts.push('emotional triggers')
  if (priceAnalysis.perception || priceAnalysis.value_framing) parts.push('price')
  if (data.ad_angles?.length) parts.push('ad angles')
  if (data.shooting_angles?.length) parts.push('shooting angles')
  if (data.color_worlds?.length) parts.push('color worlds')
  if (data.unique_selling_points?.length) parts.push('USPs')
  if (copyDir.tone) parts.push('copy direction')
  const summaryLabel = parts.length ? parts.join(', ') : ''

  return (
    <section className={`card analysis-card ${isExpanded ? 'analysis-card--expanded' : 'analysis-card--collapsed'}`}>
      <h2>Marketing Analysis</h2>
      {!isExpanded ? (
        <div className="analysis-preview">
          {positioningPreview && <p className="analysis-preview-text">{positioningPreview}</p>}
          {summaryLabel && <p className="analysis-preview-meta">{summaryLabel}</p>}
          <button
            type="button"
            className="analysis-expand-btn"
            onClick={() => setIsExpanded(true)}
            aria-expanded="false"
          >
            Show full analysis
          </button>
        </div>
      ) : (
        <>
          <div className="analysis-grid">
            {data.product_visual_notes && (
              <div className="analysis-block analysis-block--product-visual">
                <h4>Product visual (from images)</h4>
                <p>{data.product_visual_notes}</p>
              </div>
            )}
            {data.positioning && (
              <div className="analysis-block">
                <h4>Positioning</h4>
                <p>{data.positioning}</p>
              </div>
            )}
            {data.tagline_options?.length > 0 && (
              <div className="analysis-block">
                <h4>Taglines</h4>
                <ul>{data.tagline_options.map((t, i) => <li key={i}>{t}</li>)}</ul>
              </div>
            )}
            {ideal.age_range && (
              <div className="analysis-block">
                <h4>Ideal customer</h4>
                <p><strong>Age:</strong> {ideal.age_range} · <strong>Platform:</strong> {ideal.platform}</p>
                <p>{ideal.lifestyle}</p>
                {ideal.pain_points?.length > 0 && (
                  <ul className="pain-points">
                    {ideal.pain_points.map((p, i) => <li key={i}>{p}</li>)}
                  </ul>
                )}
              </div>
            )}
            {data.emotional_triggers?.length > 0 && (
              <div className="analysis-block">
                <h4>Emotional triggers</h4>
                <ul>{data.emotional_triggers.map((t, i) => <li key={i}>{t}</li>)}</ul>
              </div>
            )}
            {(priceAnalysis.perception || priceAnalysis.value_framing) && (
              <div className="analysis-block">
                <h4>Price</h4>
                <p>{priceAnalysis.perception}</p>
                <p>{priceAnalysis.value_framing}</p>
              </div>
            )}
            {data.ad_angles?.length > 0 && (
              <div className="analysis-block">
                <h4>Ad angles</h4>
                <ul className="ad-angles">
                  {data.ad_angles.slice(0, 5).map((a, i) => (
                    <li key={i}>
                      <strong>{a.angle_name}</strong>: {a.hook} — {a.why_it_works}
                    </li>
                  ))}
                </ul>
              </div>
            )}
            {data.shooting_angles?.length > 0 && (
              <div className="analysis-block">
                <h4>Shooting angles</h4>
                <ul>{data.shooting_angles.map((a, i) => <li key={i}>{a}</li>)}</ul>
              </div>
            )}
            {data.color_worlds?.length > 0 && (
              <div className="analysis-block">
                <h4>Color worlds</h4>
                <ul>{data.color_worlds.map((c, i) => <li key={i}>{c}</li>)}</ul>
              </div>
            )}
            {data.unique_selling_points?.length > 0 && (
              <div className="analysis-block">
                <h4>USPs</h4>
                <ul>{data.unique_selling_points.map((u, i) => <li key={i}>{u}</li>)}</ul>
              </div>
            )}
            {copyDir.tone && (
              <div className="analysis-block">
                <h4>Copy direction</h4>
                <p><strong>Tone:</strong> {copyDir.tone} · <strong>Style:</strong> {copyDir.headline_style}</p>
              </div>
            )}
          </div>
          <button
            type="button"
            className="analysis-collapse-btn"
            onClick={() => setIsExpanded(false)}
            aria-expanded="true"
          >
            Show less
          </button>
        </>
      )}
    </section>
  )
}

function CreativesSection({
  productName = null,
  creatives,
  selectedForAds = [],
  onToggleSelect,
  onSelectAll,
  onDeselectAll,
  productImageUrl = null,
  referenceImageUrl = null,
  onReferenceImageUrlChange,
  imageModels = [],
  selectedImageModel,
  onModelChange,
  onGenerateAds,
  generatingAds,
  generatingAdsTotal = 0,
  generateAdsError,
  generatedAds = [],
  editedCreatives = {},
  onEditedCreativeChange,
  generatedAdsSectionRef,
  onImageClick,
}) {
  const list = Array.isArray(creatives) ? creatives : []
  const zipBase = (productName || 'Hawbeez').replace(/[^a-z0-9]+/gi, '-').replace(/^-|-$/g, '') || 'Hawbeez'
  const [expandedId, setExpandedId] = useState(null)
  const [customUrlInput, setCustomUrlInput] = useState('')
  const [uploadingRef, setUploadingRef] = useState(false)
  const [refError, setRefError] = useState(null)
  const [refPreviewBlobUrl, setRefPreviewBlobUrl] = useState(null) // immediate preview while uploading
  const [copiedId, setCopiedId] = useState(null)
  const [jsonEditDraft, setJsonEditDraft] = useState({}) // id -> raw string
  const [jsonParseError, setJsonParseError] = useState(null) // per-section parse error
  const [jsonEditorOpenId, setJsonEditorOpenId] = useState(null) // which creative's JSON editor is open
  const [downloadingZip, setDownloadingZip] = useState(false)
  const [selectedForDownload, setSelectedForDownload] = useState([]) // creative_id of generated ads to download
  const creativeMap = list.reduce((acc, c) => ({ ...acc, [c.id]: c }), {})

  function getEffectiveCreative(c) {
    const base = editedCreatives[c.id] ?? c
    const prompt = (base.scene_prompt ?? base.image_prompt ?? '').trim() || base.scene_prompt || base.image_prompt || ''
    return { ...base, scene_prompt: prompt }
  }

  // Use pathname for display when URL is from another origin so img loads via current origin (proxy)
  function refUrlForDisplay(url) {
    if (!url) return null
    try {
      const u = new URL(url, window.location.origin)
      if (u.origin !== window.location.origin) return u.pathname + u.search
      return url
    } catch {
      return url
    }
  }
  const displayRefUrl = refPreviewBlobUrl || refUrlForDisplay(referenceImageUrl) || productImageUrl
  const isUsingProduct = !referenceImageUrl

  async function uploadImageBlob(blob) {
    setRefError(null)
    // Show preview immediately so user sees their image
    const prevBlob = refPreviewBlobUrl
    const blobUrl = URL.createObjectURL(blob)
    setRefPreviewBlobUrl(blobUrl)
    if (prevBlob) URL.revokeObjectURL(prevBlob)
    setUploadingRef(true)
    try {
      const form = new FormData()
      form.append('file', blob, blob.name || 'image.png')
      const res = await fetch('/api/upload-reference', { method: 'POST', headers: authHeaders(), body: form })
      const data = await res.json()
      if (!res.ok) throw new Error(data.detail || res.statusText)
      onReferenceImageUrlChange?.(data.url)
      setRefPreviewBlobUrl(null)
      URL.revokeObjectURL(blobUrl)
    } catch (e) {
      setRefError(e.message || 'Upload failed')
      setRefPreviewBlobUrl(null)
      URL.revokeObjectURL(blobUrl)
    } finally {
      setUploadingRef(false)
    }
  }

  function handleFileSelect(e) {
    const file = e.target?.files?.[0]
    if (file && file.type.startsWith('image/')) uploadImageBlob(file)
    e.target.value = ''
  }

  return (
    <section className="card creatives-card">
      <h2>Ad Creatives ({list.length})</h2>
      <p className="creatives-hint">
        Select creatives below, choose an image model, then click Generate ads to create images via Replicate.
      </p>
      {generatedAds.length > 0 && (
        <div className="generated-ads-section" ref={generatedAdsSectionRef}>
          <div className="generated-ads-section-header">
            <h3>Generated ads</h3>
            {generatedAds.some((r) => r.image_url) && (
              <div className="generated-ads-download-actions">
                <button
                  type="button"
                  className="btn-download-selected"
                  disabled={downloadingZip || selectedForDownload.length === 0}
                  onClick={async () => {
                    const toDownload = generatedAds.filter((r) => r.image_url && selectedForDownload.includes(r.creative_id))
                    if (!toDownload.length) return
                    setDownloadingZip(true)
                    try {
                      const zip = new JSZip()
                      await Promise.all(
                        toDownload.map(async (r) => {
                          const creative = creativeMap[r.creative_id]
                          const id = String(r.creative_id).padStart(2, '0')
                          const slug = (creative?.concept_name || 'image').replace(/[^a-z0-9]+/gi, '-').toLowerCase().replace(/^-|-$/g, '') || 'image'
                          const fileName = `Hawbeez-ad-${id}-${slug}.png`
                          const res = await fetch(proxyImageUrl(r.image_url))
                          const blob = await res.blob()
                          zip.file(fileName, blob)
                        })
                      )
                      const blob = await zip.generateAsync({ type: 'blob' })
                      const url = URL.createObjectURL(blob)
                      const a = document.createElement('a')
                      a.href = url
                      a.download = `${zipBase}-selected-ads.zip`
                      a.click()
                      URL.revokeObjectURL(url)
                    } catch (_) {}
                    setDownloadingZip(false)
                  }}
                >
                  {downloadingZip ? 'Preparing…' : `Download selected (${selectedForDownload.length})`}
                </button>
                <button
                  type="button"
                  className="btn-download-zip"
                  disabled={downloadingZip}
                  onClick={async () => {
                    setDownloadingZip(true)
                    try {
                      const zip = new JSZip()
                      const withUrls = generatedAds.filter((r) => r.image_url)
                      await Promise.all(
                        withUrls.map(async (r) => {
                          const creative = creativeMap[r.creative_id]
                          const id = String(r.creative_id).padStart(2, '0')
                          const slug = (creative?.concept_name || 'image').replace(/[^a-z0-9]+/gi, '-').toLowerCase().replace(/^-|-$/g, '') || 'image'
                          const fileName = `Hawbeez-ad-${id}-${slug}.png`
                          const res = await fetch(proxyImageUrl(r.image_url))
                          const blob = await res.blob()
                          zip.file(fileName, blob)
                        })
                      )
                      const blob = await zip.generateAsync({ type: 'blob' })
                      const url = URL.createObjectURL(blob)
                      const a = document.createElement('a')
                      a.href = url
                      a.download = `${zipBase}-all-ads.zip`
                      a.click()
                      URL.revokeObjectURL(url)
                    } catch (_) {}
                    setDownloadingZip(false)
                  }}
                >
                  {downloadingZip ? 'Preparing ZIP…' : 'Download all as ZIP'}
                </button>
              </div>
            )}
          </div>
          <div className="generated-ads-grid">
            {generatedAds.map((r) => {
              const creative = creativeMap[r.creative_id]
              const id = String(r.creative_id).padStart(2, '0')
              const slug = (creative?.concept_name || 'image').replace(/[^a-z0-9]+/gi, '-').toLowerCase().replace(/^-|-$/g, '') || 'image'
              const fileName = `Hawbeez-ad-${id}-${slug}.png`
              return (
                <div key={r.creative_id} className="generated-ad-card">
                  {r.error ? (
                    <div className="generated-ad-error">
                      <span>#{r.creative_id}</span>
                      <p>{r.error}</p>
                    </div>
                  ) : r.image_url ? (
                    <>
                      <div
                        className="generated-ad-img-wrap"
                        onMouseEnter={(e) => e.currentTarget.classList.add('is-hover')}
                        onMouseLeave={(e) => e.currentTarget.classList.remove('is-hover')}
                      >
                        <img
                          src={proxyImageUrl(r.image_url)}
                          alt={creative?.concept_name || `Ad ${r.creative_id}`}
                          className={`generated-ad-img ${onImageClick ? 'img-expandable' : ''}`}
                          onClick={(e) => { e.stopPropagation(); onImageClick?.(proxyImageUrl(r.image_url), creative?.concept_name || `Ad ${r.creative_id}`) }}
                          role={onImageClick ? 'button' : undefined}
                          tabIndex={onImageClick ? 0 : undefined}
                          onKeyDown={(e) => onImageClick && (e.key === 'Enter' || e.key === ' ') && (e.preventDefault(), onImageClick(proxyImageUrl(r.image_url), creative?.concept_name || `Ad ${r.creative_id}`))}
                        />
                        <label className="generated-ad-select-checkbox">
                          <input
                            type="checkbox"
                            checked={selectedForDownload.includes(r.creative_id)}
                            onChange={(e) => {
                              e.stopPropagation()
                              setSelectedForDownload((prev) =>
                                e.target.checked
                                  ? [...prev, r.creative_id]
                                  : prev.filter((id) => id !== r.creative_id)
                              )
                            }}
                            onClick={(e) => e.stopPropagation()}
                          />
                          <span className="generated-ad-select-label">Select</span>
                        </label>
                      </div>
                      <div className="generated-ad-card-footer">
                        <p className="generated-ad-label">
                          #{r.creative_id} {creative?.concept_name || ''}
                        </p>
                      </div>
                    </>
                  ) : null}
                </div>
              )
            })}
          </div>
        </div>
      )}
      <div className="reference-image-section">
        <h4>Reference image for generation</h4>
        <p className="reference-hint">Used by the model when supported (e.g. nano-banana). Product image or your own.</p>
        <div className="reference-image-row">
          {displayRefUrl ? (
            <img
              src={displayRefUrl}
              alt="Reference"
              className={`reference-thumb ${onImageClick ? 'img-expandable' : ''}`}
              onClick={() => onImageClick?.(displayRefUrl, 'Reference')}
              role={onImageClick ? 'button' : undefined}
              tabIndex={onImageClick ? 0 : undefined}
              onKeyDown={(e) => onImageClick && (e.key === 'Enter' || e.key === ' ') && (e.preventDefault(), onImageClick(displayRefUrl, 'Reference'))}
            />
          ) : (
            <div className="reference-thumb reference-thumb-placeholder">No image</div>
          )}
          <div className="reference-controls">
            {referenceImageUrl ? (
              <button type="button" className="btn-reference-use-product" onClick={() => onReferenceImageUrlChange?.(null)}>
                Use product image
              </button>
            ) : null}
            <input
              type="url"
              placeholder="Custom image URL"
              value={customUrlInput}
              onChange={(e) => setCustomUrlInput(e.target.value)}
              onBlur={() => {
                const v = customUrlInput.trim()
                if (v) onReferenceImageUrlChange?.(v)
              }}
              onKeyDown={(e) => e.key === 'Enter' && (customUrlInput.trim() ? onReferenceImageUrlChange?.(customUrlInput.trim()) : null)}
              className="reference-url-input"
            />
            <label className="reference-upload-btn">
              <span>Upload file</span>
              <input type="file" accept="image/*" onChange={handleFileSelect} disabled={uploadingRef} style={{ display: 'none' }} />
            </label>
          </div>
        </div>
        {refError && <p className="error reference-error">{refError}</p>}
      </div>
      <div className="generate-ads-toolbar">
        <label className="toolbar-label">
          Image model
          <select
            value={selectedImageModel}
            onChange={(e) => onModelChange(e.target.value)}
            disabled={generatingAds}
            className="toolbar-select"
          >
            {imageModels.map((m) => (
              <option key={m.key} value={m.key}>
                {m.key}
              </option>
            ))}
          </select>
        </label>
        <button
          type="button"
          onClick={onGenerateAds}
          disabled={selectedForAds.length === 0 || generatingAds}
          className="btn-generate-ads"
        >
          {generatingAds
            ? `Generating ${generatedAds.length}/${generatingAdsTotal}…`
            : `Generate ads (${selectedForAds.length} selected)`}
        </button>
      </div>
      {generateAdsError && <p className="error">{generateAdsError}</p>}
      <div className="creatives-select-actions">
        <button type="button" className="btn-select-all" onClick={onSelectAll} disabled={generatingAds || list.length === 0}>
          Select all
        </button>
        <button type="button" className="btn-deselect-all" onClick={onDeselectAll} disabled={generatingAds || selectedForAds.length === 0}>
          Deselect all
        </button>
      </div>
      <div className="creatives-list">
        {list.map((c) => (
          <div
            key={c.id}
            className={`creative-item ${c.creative_type === 'NO_PRODUCT' ? 'no-product' : 'product'}`}
          >
            <div className="creative-header" onClick={() => {
              const next = expandedId === c.id ? null : c.id
              setExpandedId(next)
              if (next === null) setJsonEditorOpenId(null)
            }}>
              <input
                type="checkbox"
                checked={selectedForAds.includes(c.id)}
                onChange={(e) => {
                  e.stopPropagation()
                  onToggleSelect(c.id)
                }}
                onClick={(e) => e.stopPropagation()}
                className="creative-checkbox"
                title="Select to generate ad image"
              />
              <span className="creative-id">#{c.id}</span>
              <span className="creative-concept">{c.concept_name}</span>
              <span className="creative-type">{c.creative_type}</span>
              <span className="creative-category">{c.category}</span>
              <span className="creative-platform">{c.best_platform}</span>
            </div>
            {expandedId === c.id && (() => {
              const effective = getEffectiveCreative(c)
              return (
              <div className="creative-detail">
                <div className="copy-block">
                  <div className="copy-block-header">
                    <h5>Copy</h5>
                    <button
                      type="button"
                      className="btn-copy-copy"
                      onClick={async () => {
                        const text = JSON.stringify(effective, null, 2)
                        try {
                          await navigator.clipboard.writeText(text)
                          setCopiedId(c.id)
                          setTimeout(() => setCopiedId(null), 2000)
                        } catch (_) {}
                      }}
                    >
                      {copiedId === c.id ? 'Copied!' : 'Copy to clipboard'}
                    </button>
                  </div>
                  <p className="headline-serif">{effective.ad_copy?.headline_serif}</p>
                  <p className="headline-script">{effective.ad_copy?.headline_script}</p>
                  <p className="body-copy">{effective.ad_copy?.body}</p>
                  {effective.includes_price && (
                    <p className="price-copy">
                      <span
                        className={`price-original price-original-${['strikethrough', 'slant', 'faded', 'small', 'double'].includes(effective.ad_copy?.price_original_style) ? effective.ad_copy.price_original_style : 'strikethrough'}`}
                      >
                        {effective.ad_copy?.price_original}
                      </span>{' '}
                      <span className="price-final">{effective.ad_copy?.price_final}</span>
                    </p>
                  )}
                  <p className={`cta-copy${effective.cta_position ? ` cta-position-${effective.cta_position}` : ''}`}>
                    {effective.ad_copy?.cta}
                    {effective.cta_position && (
                      <span className="cta-position-badge" title="CTA placement in the ad image">
                        {' '}({effective.cta_position.replace(/_/g, ' ')})
                      </span>
                    )}
                  </p>
                </div>
                <div className="prompt-block">
                  <h5>Scene prompt</h5>
                  <p className="prompt-block-hint">The full creative JSON above is sent to the image model; this field shows the scene/visual description only.</p>
                  <textarea
                    value={effective.scene_prompt ?? effective.image_prompt ?? ''}
                    readOnly
                    placeholder="Scene / visual description for the ad image…"
                    className="prompt-textarea prompt-textarea-readonly"
                    rows={4}
                  />
                </div>
                {jsonEditorOpenId !== c.id ? (
                  <button
                    type="button"
                    className="btn-open-edit-json"
                    onClick={() => setJsonEditorOpenId(c.id)}
                  >
                    Edit full JSON
                  </button>
                ) : (
                  <div className="edit-json-block">
                    <div className="edit-json-header">
                      <h5>Edit full JSON</h5>
                      <button type="button" className="btn-close-edit-json" onClick={() => setJsonEditorOpenId(null)}>
                        Close
                      </button>
                    </div>
                    <textarea
                      className="edit-json-textarea"
                      value={jsonEditDraft[c.id] ?? JSON.stringify(effective, null, 2)}
                      onChange={(e) => {
                        setJsonParseError(null)
                        setJsonEditDraft((prev) => ({ ...prev, [c.id]: e.target.value }))
                      }}
                      placeholder="Full creative JSON…"
                      rows={12}
                      spellCheck={false}
                    />
                    <div className="edit-json-actions">
                      <button
                        type="button"
                        className="btn-apply-json"
                        onClick={() => {
                          const raw = jsonEditDraft[c.id] ?? JSON.stringify(effective, null, 2)
                          setJsonParseError(null)
                          try {
                            const parsed = JSON.parse(raw)
                            if (parsed && typeof parsed.id === 'number') {
                              onEditedCreativeChange?.(c.id, parsed)
                              setJsonEditDraft((prev) => { const next = { ...prev }; delete next[c.id]; return next })
                            } else {
                              setJsonParseError('JSON must include an "id" number.')
                            }
                          } catch (e) {
                            setJsonParseError(e.message || 'Invalid JSON')
                          }
                        }}
                      >
                        Apply
                      </button>
                      {jsonParseError && <span className="edit-json-error">{jsonParseError}</span>}
                    </div>
                  </div>
                )}
                {effective.layout_notes && (
                  <p className="layout-notes"><strong>Layout:</strong> {effective.layout_notes}</p>
                )}
              </div>
              )
            })()}
          </div>
        ))}
      </div>
    </section>
  )
}
