/* shielded-mode.jsx — ported from RealAdii/shielded-mode-on.
 *
 * Renders the Starknet Shinobi PFP generator inline inside the
 * SHIELDED MODE tab. Calls /shielded/api/generate (handle or
 * uploaded image) and /shielded/api/status (live flag), both
 * rewritten by the site's vercel.json to the standalone deploy.
 *
 * No ES module imports — this file is loaded via
 * <script type="text/babel" src="shielded-mode.jsx"> by index.html.
 * React + ReactDOM are window globals (UMD from unpkg). The
 * component itself becomes a window global named ShieldedModeModule
 * so live-apps.jsx can reference it. */
const { useState, useRef, useEffect } = React;

const LOADING_MESSAGES = [
  'Initializing shielded protocol',
  'Locating target on Starknet',
  'Extracting identity signature',
  'Generating cryptographic mask',
  'Forging cyber katana',
  'Calibrating orange glow seams',
  'Compiling tactical suit layers',
  'Engraving Starknet headband',
  'Aligning shadow patterns',
  'Validating outfit overlay',
  'Hold tight, this is worth the wait',
  'Almost there, shielded mode activating',
  'Final stitching in progress',
  'Sword forged, suit fitted',
  'Just a few more seconds',
]

/* Calls the standalone PFP generator at strk20.starknet.io/shielded/*,
 * which Adi's vercel.json rewrites to the dedicated shielded-mode-on
 * Vercel project. That deploy owns the OPENAI_API_KEY and the
 * references/ images — no API runtime needs to live in this repo. */
async function callGenerate(payload) {
  const res = await fetch('/shielded/api/generate', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload),
  })
  const text = await res.text()
  let data
  try { data = JSON.parse(text) }
  catch { throw new Error(`Server returned ${res.status}: ${text.slice(0, 120)}`) }
  if (!res.ok) throw new Error(data.error ?? `Server error ${res.status}`)
  return data
}

function getImageSrc(result) {
  if (result.imageUrl) return result.imageUrl
  if (result.b64) return `data:image/png;base64,${result.b64}`
  throw new Error('No image in response')
}

function downloadImage(src, handle) {
  const a = document.createElement('a')
  a.href = src
  a.download = `${handle}-shielded.png`
  a.target = '_blank'
  a.click()
}

function readFileAsDataUrl(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()
    reader.onload = () => resolve(reader.result)
    reader.onerror = () => reject(new Error('Failed to read file'))
    reader.readAsDataURL(file)
  })
}

/* Styling helpers — keep the inline-style blocks readable by pulling
 * the recurring decorations out as constants. */
const MONO = {
  fontFamily: 'var(--mono)', textTransform: 'uppercase',
  letterSpacing: '0.14em',
}

/* Dark feature-card surface per the strk20 design system:
 * gradient face, 1px line border, squared corners. The 2px orange
 * top-accent is reserved for display cards (not interactive form
 * surfaces like this module), so we don't add it here. */
const PANEL = {
  background:
    'linear-gradient(165deg, #181818 0%, #141414 48%, #100c0a 100%)',
  border: '1px solid var(--line)',
  borderRadius: 0,
}

function ShieldedModeModule() {
  const [live, setLive] = useState(null)
  const [handle, setHandle] = useState('')
  const [uploadedDataUrl, setUploadedDataUrl] = useState(null)
  const [uploadedName, setUploadedName] = useState('image')
  const [dragOver, setDragOver] = useState(false)
  const [step, setStep] = useState('idle')
  const [result, setResult] = useState(null)
  const [generatedSrc, setGeneratedSrc] = useState(null)
  const [error, setError] = useState(null)
  const [loadingMsgIndex, setLoadingMsgIndex] = useState(0)
  const [elapsed, setElapsed] = useState(0)
  const inputRef = useRef(null)
  const fileInputRef = useRef(null)

  // Pull the live flag once on mount so we can show "Currently
  // Offline" without disabling the generator code path entirely.
  useEffect(() => {
    fetch('/shielded/api/status')
      .then(r => r.json())
      .then(d => setLive(d.live !== false))
      .catch(() => setLive(true))
  }, [])

  const clean = (raw) => raw.replace(/^@/, '').trim()

  async function acceptFile(file) {
    if (!file) return
    if (!/^image\/(jpeg|jpg|png|webp)$/.test(file.type)) {
      setError('Please use a JPEG, PNG, or WebP image.')
      return
    }
    if (file.size > 5 * 1024 * 1024) {
      setError('Image too large. Max 5MB.')
      return
    }
    setError(null)
    try {
      const dataUrl = await readFileAsDataUrl(file)
      setUploadedDataUrl(dataUrl)
      setUploadedName(file.name.replace(/\.[^.]+$/, ''))
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Failed to read file')
    }
  }

  // Loading-message rotator + elapsed counter — both reset when we
  // leave the generating step.
  useEffect(() => {
    if (step !== 'generating') return
    setLoadingMsgIndex(0)
    setElapsed(0)
    const msgTimer = setInterval(() => {
      setLoadingMsgIndex(i => (i + 1) % LOADING_MESSAGES.length)
    }, 2800)
    const elapsedTimer = setInterval(() => setElapsed(e => e + 1), 1000)
    return () => { clearInterval(msgTimer); clearInterval(elapsedTimer) }
  }, [step])

  async function handleGenerate() {
    let payload
    if (uploadedDataUrl) {
      payload = { image: uploadedDataUrl }
    } else {
      const username = clean(handle)
      if (!username) return
      payload = { handle: username }
    }

    setError(null)
    setResult(null)
    setGeneratedSrc(null)
    setStep('generating')

    try {
      const data = await callGenerate(payload)
      setResult(data)
      setGeneratedSrc(getImageSrc(data))
      setStep('done')
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Something went wrong')
      setStep('error')
    }
  }

  function reset() {
    setHandle('')
    setUploadedDataUrl(null)
    setUploadedName('image')
    setStep('idle')
    setResult(null)
    setGeneratedSrc(null)
    setError(null)
    setTimeout(() => inputRef.current?.focus(), 50)
  }

  const isLoading = step === 'generating'

  if (live === false) {
    return (
      <div style={{
        maxWidth: 560, margin: '0 auto', padding: '32px 28px',
        ...PANEL,
        textAlign: 'center',
      }}>
        <div style={{
          ...MONO, fontSize: 10.5, color: 'var(--orange)', marginBottom: 14,
        }}>// System Maintenance</div>
        <p style={{
          margin: 0, fontFamily: 'var(--mono)', fontSize: 13,
          lineHeight: 1.7, letterSpacing: '0.04em', color: 'var(--text)',
        }}>
          The shielded forge is being tuned. We'll be back online shortly
          with faster, sharper transformations.
        </p>
        <p style={{
          marginTop: 14, ...MONO, fontSize: 10.5, color: 'var(--dim)',
        }}>Check back soon</p>
      </div>
    )
  }

  return (
    <div style={{
      maxWidth: 720,
      margin: 'clamp(20px, 3vh, 36px) auto 0',
      position: 'relative',
    }}>
      {/* Eyebrow — strk20 design-system pattern: orange `◢` tick +
          mono uppercase label, 0.32em tracking, --faint. Sets a clear
          break between the SHIELDED MODE panel title and the input. */}
      <div style={{
        fontFamily: 'var(--mono)', fontSize: 11,
        letterSpacing: '0.32em', textTransform: 'uppercase',
        color: 'var(--faint)', marginBottom: 18,
      }}>
        <span style={{ color: 'var(--orange)', marginRight: 10 }}>◢</span>
        Activate from a handle or upload
      </div>
      {step !== 'done' && (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
          <input
            ref={fileInputRef}
            type="file"
            accept="image/jpeg,image/png,image/webp"
            style={{ display: 'none' }}
            onChange={e => acceptFile(e.target.files?.[0])}
          />

          {/* Handle input row OR uploaded-thumb row, switched by state. */}
          <div
            style={{
              ...PANEL,
              padding: '14px 18px',
              border: dragOver
                ? '1px solid var(--orange)'
                : '1px solid var(--line)',
              transition: 'border-color .18s',
            }}
            onDragOver={e => { e.preventDefault(); setDragOver(true) }}
            onDragLeave={() => setDragOver(false)}
            onDrop={e => {
              e.preventDefault()
              setDragOver(false)
              acceptFile(e.dataTransfer.files?.[0])
            }}
          >
            {!uploadedDataUrl ? (
              <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <span style={{
                  fontFamily: 'var(--mono)', fontSize: 18, fontWeight: 600,
                  color: 'var(--orange)',
                }}>@</span>
                <input
                  ref={inputRef}
                  type="text" placeholder="username" value={handle}
                  onChange={e => setHandle(e.target.value.replace(/^@+/, ''))}
                  onKeyDown={e => e.key === 'Enter' && !isLoading && handleGenerate()}
                  disabled={isLoading}
                  autoCapitalize="none" autoCorrect="off" spellCheck={false}
                  autoFocus
                  style={{
                    flex: 1, minWidth: 0,
                    background: 'transparent', border: 'none', outline: 'none',
                    color: 'var(--text)',
                    fontFamily: 'var(--mono)', fontSize: 16,
                    letterSpacing: '0.04em', padding: 0,
                  }}
                />
                <button
                  className="btn btn-orange"
                  onClick={handleGenerate}
                  disabled={isLoading || !clean(handle)}
                  style={{
                    height: 38, padding: '0 18px',
                    opacity: (isLoading || !clean(handle)) ? 0.55 : 1,
                    cursor: (isLoading || !clean(handle)) ? 'not-allowed' : 'pointer',
                  }}
                >{isLoading ? '...' : 'Activate'}</button>
              </div>
            ) : (
              <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
                <img
                  src={uploadedDataUrl} alt="Uploaded preview"
                  style={{
                    width: 56, height: 56, objectFit: 'cover',
                    borderRadius: 4, border: '1px solid var(--line)',
                    flexShrink: 0,
                  }}
                />
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{
                    fontFamily: 'var(--mono)', fontSize: 13,
                    color: 'var(--text)',
                    overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis',
                  }}>{uploadedName}</div>
                  <button
                    onClick={() => { setUploadedDataUrl(null); setUploadedName('image') }}
                    disabled={isLoading}
                    style={{
                      marginTop: 4, padding: 0, background: 'transparent',
                      border: 'none', cursor: isLoading ? 'not-allowed' : 'pointer',
                      ...MONO, fontSize: 10.5, color: 'var(--dim)',
                    }}
                  >Remove</button>
                </div>
                <button
                  className="btn btn-orange"
                  onClick={handleGenerate} disabled={isLoading}
                  style={{
                    height: 38, padding: '0 18px',
                    opacity: isLoading ? 0.55 : 1,
                    cursor: isLoading ? 'not-allowed' : 'pointer',
                  }}
                >{isLoading ? '...' : 'Activate'}</button>
              </div>
            )}
          </div>

          {/* "or" divider — only shown before an upload is staged. */}
          {!uploadedDataUrl && (
            <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
              <span style={{ flex: 1, height: 1, background: 'var(--line)' }} />
              <span style={{ ...MONO, fontSize: 10.5, color: 'var(--dim)' }}>or</span>
              <span style={{ flex: 1, height: 1, background: 'var(--line)' }} />
            </div>
          )}

          {/* Drop zone — only shown when no upload is staged. */}
          {!uploadedDataUrl && (
            <div
              onClick={() => fileInputRef.current?.click()}
              onDragOver={e => { e.preventDefault(); setDragOver(true) }}
              onDragLeave={() => setDragOver(false)}
              onDrop={e => {
                e.preventDefault()
                setDragOver(false)
                acceptFile(e.dataTransfer.files?.[0])
              }}
              style={{
                ...PANEL,
                padding: '22px 18px',
                display: 'flex', alignItems: 'center', gap: 14,
                cursor: 'pointer',
                border: dragOver
                  ? '1px dashed var(--orange)'
                  : '1px dashed var(--line)',
                transition: 'border-color .18s, background .18s',
              }}
            >
              <span style={{
                fontSize: 20, color: 'var(--orange)', lineHeight: 1,
              }} aria-hidden="true">↑</span>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
                <span style={{
                  fontFamily: 'var(--mono)', fontSize: 12.5,
                  letterSpacing: '0.04em', color: 'var(--text)',
                }}>Drop image here or click to upload</span>
                <span style={{
                  ...MONO, fontSize: 10, color: 'var(--dim)',
                }}>JPG · PNG · WebP · max 5MB</span>
              </div>
            </div>
          )}

          {step === 'error' && error && (
            <div style={{
              ...MONO, fontSize: 11, color: 'var(--orange)',
              padding: '8px 12px', border: '1px solid rgba(197,52,0,0.45)',
              borderRadius: 3,
            }}>{error}</div>
          )}
        </div>
      )}

      {step === 'done' && result && generatedSrc && (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 22 }}>
          <div style={{
            display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 14,
          }}>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              <span style={{ ...MONO, fontSize: 10.5, color: 'var(--dim)' }}>Original</span>
              <img src={result.pfpUrl} alt="Original PFP" style={resultImgStyle} />
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              <span style={{ ...MONO, fontSize: 10.5, color: 'var(--orange)' }}>// Shielded</span>
              <img src={generatedSrc} alt="Shielded mode" style={{
                ...resultImgStyle,
                boxShadow: '0 0 0 1px var(--orange), 0 20px 50px -22px rgba(197,52,0,0.55)',
              }} />
            </div>
          </div>

          <div style={{ display: 'flex', gap: 10, justifyContent: 'center', flexWrap: 'wrap' }}>
            <button
              className="btn btn-orange"
              onClick={() => downloadImage(generatedSrc, uploadedDataUrl ? uploadedName : clean(handle))}
              style={{ height: 38, padding: '0 18px' }}
            >Download</button>
            <button
              className="btn"
              onClick={reset}
              style={{ height: 38, padding: '0 18px' }}
            >New Target</button>
          </div>
        </div>
      )}

      {/* Loading overlay — covers just the SHIELDED MODE module so the
          rest of the tab panel stays visible behind it. */}
      {isLoading && (
        <div style={{
          position: 'absolute', inset: -20, zIndex: 10,
          background: 'rgba(10,10,10,0.86)',
          backdropFilter: 'blur(4px)',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          borderRadius: 6,
        }}>
          <div style={{
            ...PANEL,
            padding: '32px 36px',
            maxWidth: 520, width: '100%',
            display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 18,
            textAlign: 'center',
          }}>
            <div style={{
              ...MONO, fontSize: 10.5, color: 'var(--orange)',
            }}>// Shielded Mode Engaging</div>
            <div style={{
              ...MONO, fontSize: 12, color: 'var(--text)',
            }}>target: {uploadedDataUrl ? uploadedName : `@${clean(handle)}`}</div>

            <div style={{
              ...MONO, fontSize: 11.5, color: 'var(--dim)',
              minHeight: 18,
            }} key={loadingMsgIndex}>
              {LOADING_MESSAGES[loadingMsgIndex]}…
            </div>

            <div style={{
              width: '100%', height: 2, background: 'var(--line)',
              overflow: 'hidden', borderRadius: 1,
            }}>
              <div style={{
                width: '40%', height: '100%',
                background: 'linear-gradient(90deg, transparent, var(--orange), transparent)',
                animation: 'sm-progress 1.4s cubic-bezier(.4,.2,.6,1) infinite',
              }} />
            </div>

            <div style={{
              display: 'flex', justifyContent: 'space-between', width: '100%',
              ...MONO, fontSize: 10, color: 'var(--faint)',
            }}>
              <span>
                elapsed {String(Math.floor(elapsed / 60)).padStart(2, '0')}
                :{String(elapsed % 60).padStart(2, '0')}
              </span>
              <span>do not close window</span>
            </div>
          </div>

          <style>{`
            @keyframes sm-progress {
              0%   { transform: translateX(-100%); }
              100% { transform: translateX(350%);  }
            }
          `}</style>
        </div>
      )}
    </div>
  )
}

const resultImgStyle = {
  width: '100%', aspectRatio: '1 / 1', objectFit: 'cover',
  borderRadius: 0, border: '1px solid var(--line)',
  background: 'var(--bg-1)',
}
