// frontend/src/pages/melodies/archetypes/ArchetypeForm.jsx

import { useState, useEffect, useRef } from 'react'
import { useParams, useNavigate, Link } from 'react-router-dom'
import PageHeader from '@/components/ui/PageHeader'
import Card from '@/components/ui/Card'
import Button from '@/components/ui/Button'
import FormField from '@/components/ui/FormField'
import StatusBadge from '@/components/ui/StatusBadge'
import Spinner from '@/components/ui/Spinner'
import ConfirmDialog from '@/components/ui/ConfirmDialog'
import { useToast } from '@/components/ui/Toast'
import { useAuth } from '@/hooks/useAuth'
import api from '@/lib/api'
import { getLocalizedValue } from '@/lib/melodyUtils'
import PlaybackModal from '@/modals/bellcloud/melodies/PlaybackModal'

// ─── Helpers ─────────────────────────────────────────────────────────────────

function countSteps(stepsStr) {
  if (!stepsStr?.trim()) return 0
  return stepsStr.trim().split(',').length
}

function validateSteps(stepsStr) {
  if (!stepsStr?.trim()) return 'Steps are required.'
  const tokens = stepsStr.trim().split(',')
  for (const token of tokens) {
    const parts = token.split('+')
    for (const part of parts) {
      const trimmed = part.trim()
      if (trimmed === '') return `Invalid token near: "${token.trim()}" — empty part found.`
      const n = parseInt(trimmed, 10)
      if (isNaN(n)) return `Invalid step value: "${trimmed}" — must be a number 0–16.`
      if (n < 0 || n > 16) return `Step value "${trimmed}" is out of range (0–16).`
    }
  }
  return null
}

async function downloadBinary(binaryUrl, filename) {
  const token = localStorage.getItem('access_token')
  const res = await fetch(`/api${binaryUrl}`, {
    headers: token ? { Authorization: `Bearer ${token}` } : {},
  })
  if (!res.ok) throw new Error(`Download failed: ${res.statusText}`)
  const blob = await res.blob()
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = filename
  a.click()
  URL.revokeObjectURL(url)
}

function copyText(text, onSuccess) {
  if (navigator.clipboard) {
    navigator.clipboard.writeText(text).then(onSuccess).catch(() => fallbackCopy(text, onSuccess))
  } else {
    fallbackCopy(text, onSuccess)
  }
}

function fallbackCopy(text, onSuccess) {
  const ta = document.createElement('textarea')
  ta.value = text
  ta.style.cssText = 'position:fixed;top:0;left:0;opacity:0'
  document.body.appendChild(ta)
  ta.focus(); ta.select()
  try { document.execCommand('copy'); onSuccess?.() } catch (_) {}
  document.body.removeChild(ta)
}

// ─── Component ────────────────────────────────────────────────────────────────

export default function ArchetypeForm() {
  const { id }      = useParams()
  const isEdit      = Boolean(id)
  const navigate    = useNavigate()
  const toast       = useToast()
  const { hasPermission } = useAuth()
  const canEdit     = hasPermission('melodies', 'edit')

  // Form state
  const [name,       setName]       = useState('')
  const [pid,        setPid]        = useState('')
  const [steps,      setSteps]      = useState('')
  const [isBuiltin,  setIsBuiltin]  = useState(false)
  const [savedPid,   setSavedPid]   = useState('')

  // Load state
  const [loading,    setLoading]    = useState(false)
  const [saving,     setSaving]     = useState(false)
  const [error,      setError]      = useState('')

  // Build output
  const [binaryBuilt,    setBinaryBuilt]    = useState(false)
  const [binaryUrl,      setBinaryUrl]      = useState(null)
  const [progmemCode,    setProgmemCode]    = useState('')
  const [codeCopied,     setCodeCopied]     = useState(false)
  const [hasUnsaved,     setHasUnsaved]     = useState(false)

  // Assigned melodies
  const [assignedMelodies,  setAssignedMelodies]  = useState([])
  const [loadingAssigned,   setLoadingAssigned]   = useState(false)
  const [primaryLang,       setPrimaryLang]       = useState('en')

  // Delete
  const [showDelete,        setShowDelete]        = useState(false)
  const [showDeleteWarning, setShowDeleteWarning] = useState(false)

  // Playback
  const [showPlayback,       setShowPlayback]       = useState(false)
  const [currentBuiltMelody, setCurrentBuiltMelody] = useState(null)

  const codeRef = useRef(null)

  useEffect(() => {
    api.get('/settings/melody')
      .then((ms) => setPrimaryLang(ms.primary_language || 'en'))
      .catch(() => {})
    if (isEdit) loadArchetype()
  }, [id])

  const loadArchetype = async () => {
    setLoading(true)
    setError('')
    try {
      const data = await api.get(`/builder/melodies/${id}`)
      setName(data.name || '')
      setPid(data.pid || '')
      setSteps(data.steps || '')
      setIsBuiltin(Boolean(data.is_builtin))
      setSavedPid(data.pid || '')
      setBinaryBuilt(Boolean(data.binary_path))
      setBinaryUrl(data.binary_url || null)
      setProgmemCode(data.progmem_code || '')
      setCurrentBuiltMelody(data)
      setHasUnsaved(false)
      if (data.assigned_melody_ids?.length > 0) {
        fetchAssignedMelodies(data.assigned_melody_ids)
      }
    } catch (err) {
      setError(err.message || 'Failed to load archetype.')
    } finally {
      setLoading(false)
    }
  }

  const fetchAssignedMelodies = async (ids) => {
    setLoadingAssigned(true)
    try {
      const results = await Promise.allSettled(ids.map((mid) => api.get(`/melodies/${mid}`)))
      setAssignedMelodies(
        results
          .filter((r) => r.status === 'fulfilled' && r.value)
          .map((r) => ({ id: r.value.id, nameRaw: r.value.information?.name }))
      )
    } catch { /* best-effort */ }
    finally { setLoadingAssigned(false) }
  }

  const handleSave = async () => {
    setError('')
    if (!name.trim()) { setError('Name is required.'); return }
    if (!pid.trim())  { setError('PID is required.'); return }
    const stepsError = validateSteps(steps)
    if (stepsError)   { setError(stepsError); return }

    setSaving(true)
    try {
      // Duplicate check
      const { melodies: list = [] } = await api.get('/builder/melodies')
      const dupName = list.find((m) => m.id !== id && m.name.toLowerCase() === name.trim().toLowerCase())
      if (dupName) { setError(`An archetype named "${name.trim()}" already exists.`); return }
      const dupPid = list.find((m) => m.id !== id && m.pid?.toLowerCase() === pid.trim().toLowerCase())
      if (dupPid)  { setError(`A PID "${pid.trim()}" is already taken.`); return }

      const body = { name: name.trim(), pid: pid.trim(), steps: steps.trim(), is_builtin: isBuiltin }

      if (isEdit) {
        const updated = await api.put(`/builder/melodies/${id}`, body)
        setSavedPid(pid.trim())
        setBinaryBuilt(Boolean(updated.binary_path))
        setBinaryUrl(updated.binary_url || null)
        setProgmemCode(updated.progmem_code || '')
        setCurrentBuiltMelody(updated)
        setHasUnsaved(false)
        toast.success('Saved', 'Archetype rebuilt and saved.')
      } else {
        const created = await api.post('/builder/melodies', body)
        toast.success('Created', 'Archetype built successfully.')
        navigate(`/melodies/archetypes/${created.id}`)
      }
    } catch (err) {
      setError(err.message || 'Save failed.')
    } finally {
      setSaving(false)
    }
  }

  const handleDelete = async () => {
    try {
      await api.delete(`/builder/melodies/${id}`)
      toast.success('Deleted', 'Archetype removed.')
      navigate('/melodies/archetypes')
    } catch (err) {
      toast.danger('Error', err.message)
      setShowDelete(false)
      setShowDeleteWarning(false)
    }
  }

  const assignedCount = assignedMelodies.length

  const playbackBuiltMelody = currentBuiltMelody
    ? { ...currentBuiltMelody, steps }
    : { id: 'preview', name: name || 'Preview', pid: pid || '', steps, binary_url: null }

  // ── Loading ───────────────────────────────────────────────────────────────

  if (loading) {
    return (
      <div className="page-wrapper">
        <PageHeader title={isEdit ? 'Edit Archetype' : 'New Archetype'} />
        <div style={{ display: 'flex', justifyContent: 'center', padding: 'var(--space-16)' }}>
          <Spinner size="lg" />
        </div>
      </div>
    )
  }

  // ── Render ────────────────────────────────────────────────────────────────

  return (
    <div className="page-wrapper page-wrapper--centered" style={{ '--page-content-max-width': 'var(--content-max-width-md)' }}>

      <PageHeader
        title={isEdit ? name || 'Edit Archetype' : 'New Archetype'}
        subtitle={isEdit ? `PID: ${savedPid || pid}` : 'Define a step sequence that melodies can reference'}
      >
        {/* Built-in toggle */}
        <Button
          variant={isBuiltin ? 'info' : 'ghost'}
          onClick={() => { setIsBuiltin((v) => !v); setHasUnsaved(true) }}
          icon={
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/>
            </svg>
          }
        >
          {isBuiltin ? 'Built-in' : 'Built-in'}
        </Button>

        {/* Play preview */}
        <Button
          variant="success"
          disabled={!steps.trim()}
          onClick={() => setShowPlayback(true)}
          icon={
            <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
              <path d="M5 3l14 9-14 9V3z"/>
            </svg>
          }
        >
          Preview
        </Button>

        {/* Cancel */}
        <Button variant="ghost" onClick={() => navigate('/melodies/archetypes')}>
          Cancel
        </Button>

        {/* Delete */}
        {isEdit && canEdit && (
          <Button
            variant="danger"
            onClick={() => assignedCount > 0 ? setShowDeleteWarning(true) : setShowDelete(true)}
          >
            Delete
          </Button>
        )}

        {/* Save */}
        {canEdit && (
          <Button variant="primary" loading={saving} onClick={handleSave}>
            {isEdit ? 'Rebuild & Save' : 'Create & Build'}
          </Button>
        )}
      </PageHeader>

      {/* ── Error banner ──────────────────────────────────────────────────── */}
      {error && (
        <div style={{
          padding: 'var(--space-3) var(--space-4)',
          backgroundColor: 'var(--color-danger-bg)',
          border: '1px solid var(--color-danger)',
          borderRadius: 'var(--radius-lg)',
          color: 'var(--color-danger)',
          fontSize: 'var(--font-size-sm)',
        }}>
          {error}
        </div>
      )}

      {/* ── Archetype Info ────────────────────────────────────────────────── */}
      <Card title="Archetype Info">
        <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-4)' }}>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 'var(--space-4)' }}>
            <FormField
              label="Name"
              name="arch-name"
              required
              value={name}
              onChange={(e) => { setName(e.target.value); setHasUnsaved(true) }}
              placeholder="e.g. Doksologia_3k"
            />
            <FormField
              label="PID (Playback ID)"
              name="arch-pid"
              required
              value={pid}
              onChange={(e) => { setPid(e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, '')); setHasUnsaved(true) }}
              placeholder="e.g. builtin_doksologia_3k"
              hint="Lowercase letters, numbers, and underscores only. Must be unique."
            />
          </div>

          <div>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 'var(--space-1)' }}>
              <label style={{ fontSize: 'var(--font-size-sm)', fontWeight: 'var(--font-weight-medium)', color: 'var(--color-text-secondary)' }}>
                Steps <span style={{ color: 'var(--color-danger)' }}>*</span>
              </label>
              <span style={{ fontSize: 'var(--font-size-xs)', fontFamily: 'var(--font-family-mono)', color: 'var(--color-text-muted)' }}>
                {countSteps(steps)} steps
              </span>
            </div>
            <textarea
              value={steps}
              onChange={(e) => { setSteps(e.target.value); setHasUnsaved(true) }}
              rows={6}
              placeholder={'e.g. 1,2,2+1,1,2,3+1,0,3\n\n• Comma-separated values\n• Single bell: 1, 2, 3…\n• Multiple bells at once: 2+3+1\n• Silence: 0'}
              className="input"
              style={{ fontFamily: 'var(--font-family-mono)', resize: 'vertical', width: '100%' }}
            />
            <p style={{ marginTop: 'var(--space-1)', fontSize: 'var(--font-size-xs)', color: 'var(--color-text-muted)' }}>
              Each comma-separated value = one step. Bell numbers 1–16 (1 = highest pitch). Combine simultaneous bells with <code style={{ fontFamily: 'var(--font-family-mono)' }}>+</code>. Silence = 0.
            </p>
          </div>
        </div>
      </Card>

      {/* ── Build Output ─────────────────────────────────────────────────── */}
      {isEdit && (
        <Card
          title="Build Output"
          subtitle={
            hasUnsaved
              ? 'You have unsaved changes — binary reflects the last saved version.'
              : 'Binary and PROGMEM code are auto-rebuilt on every save.'
          }
        >
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 'var(--space-4)', marginBottom: progmemCode ? 'var(--space-4)' : 0 }}>

            {/* Binary */}
            <div style={{
              padding: 'var(--space-4)',
              backgroundColor: 'var(--color-bg-elevated)',
              border: '1px solid var(--color-border)',
              borderRadius: 'var(--radius-md)',
              display: 'flex', flexDirection: 'column', gap: 'var(--space-3)',
            }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
                <div>
                  <div style={{ fontSize: 'var(--font-size-sm)', fontWeight: 'var(--font-weight-semibold)', color: 'var(--color-text-primary)' }}>
                    Binary (.bsm)
                  </div>
                  <div style={{ fontSize: 'var(--font-size-xs)', color: 'var(--color-text-muted)', marginTop: 2 }}>
                    For SD card playback on the controller
                  </div>
                </div>
                <StatusBadge variant={binaryBuilt ? 'success' : 'neutral'} dot={false}>
                  {binaryBuilt ? 'Built' : 'Not built'}
                </StatusBadge>
              </div>
              {binaryUrl && (
                <Button
                  variant="ghost"
                  size="sm"
                  onClick={() => downloadBinary(binaryUrl, `${savedPid}.bsm`).catch((e) => setError(e.message))}
                  icon={
                    <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                      <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/>
                    </svg>
                  }
                >
                  Download {savedPid}.bsm
                </Button>
              )}
            </div>

            {/* PROGMEM */}
            <div style={{
              padding: 'var(--space-4)',
              backgroundColor: 'var(--color-bg-elevated)',
              border: '1px solid var(--color-border)',
              borderRadius: 'var(--radius-md)',
              display: 'flex', flexDirection: 'column', gap: 'var(--space-2)',
            }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
                <div>
                  <div style={{ fontSize: 'var(--font-size-sm)', fontWeight: 'var(--font-weight-semibold)', color: 'var(--color-text-primary)' }}>
                    PROGMEM Code
                  </div>
                  <div style={{ fontSize: 'var(--font-size-xs)', color: 'var(--color-text-muted)', marginTop: 2 }}>
                    For built-in firmware (ESP32 PROGMEM)
                  </div>
                </div>
                <StatusBadge variant={progmemCode ? 'success' : 'neutral'} dot={false}>
                  {progmemCode ? 'Generated' : 'Not generated'}
                </StatusBadge>
              </div>
            </div>
          </div>

          {/* PROGMEM code block */}
          {progmemCode && (
            <div style={{
              border: '1px solid var(--color-border)',
              borderRadius: 'var(--radius-md)',
              overflow: 'hidden',
            }}>
              <div style={{
                display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                padding: 'var(--space-2) var(--space-4)',
                backgroundColor: 'var(--color-bg-elevated)',
                borderBottom: '1px solid var(--color-border)',
              }}>
                <span style={{ fontSize: 'var(--font-size-xs)', fontFamily: 'var(--font-family-mono)', color: 'var(--color-text-muted)' }}>
                  PROGMEM C code — copy into firmware
                </span>
                <Button
                  variant={codeCopied ? 'success' : 'secondary'}
                  size="sm"
                  onClick={() => copyText(progmemCode, () => { setCodeCopied(true); setTimeout(() => setCodeCopied(false), 2000) })}
                >
                  {codeCopied ? 'Copied!' : 'Copy Code'}
                </Button>
              </div>
              <pre
                ref={codeRef}
                style={{
                  margin: 0, padding: 'var(--space-4)',
                  backgroundColor: 'var(--color-bg-abyss)',
                  fontSize: 'var(--font-size-sm)',
                  fontFamily: 'var(--font-family-mono)',
                  color: 'var(--color-text-primary)',
                  overflowX: 'auto', maxHeight: 400, overflowY: 'auto',
                  whiteSpace: 'pre',
                }}
              >
                {progmemCode}
              </pre>
            </div>
          )}
        </Card>
      )}

      {/* ── Assigned Melodies ─────────────────────────────────────────────── */}
      {isEdit && (
        <Card
          title="Melodies Using This Archetype"
          subtitle={`${assignedCount} ${assignedCount === 1 ? 'melody' : 'melodies'} reference this archetype`}
        >
          {loadingAssigned ? (
            <div style={{ display: 'flex', justifyContent: 'center', padding: 'var(--space-6)' }}>
              <Spinner />
            </div>
          ) : assignedMelodies.length === 0 ? (
            <p style={{ fontSize: 'var(--font-size-sm)', color: 'var(--color-text-muted)', margin: 0 }}>
              No melodies are currently using this archetype.
            </p>
          ) : (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-1)', maxHeight: 280, overflowY: 'auto' }}>
              {assignedMelodies.map((m) => (
                <Link
                  key={m.id}
                  to={`/melodies/${m.id}`}
                  target="_blank"
                  rel="noopener noreferrer"
                  style={{
                    display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                    padding: 'var(--space-2) var(--space-3)',
                    backgroundColor: 'var(--color-bg-elevated)',
                    border: '1px solid var(--color-border)',
                    borderRadius: 'var(--radius-md)',
                    textDecoration: 'none',
                    transition: 'background var(--transition-fast)',
                  }}
                  onMouseEnter={(e) => e.currentTarget.style.backgroundColor = 'var(--color-bg-island)'}
                  onMouseLeave={(e) => e.currentTarget.style.backgroundColor = 'var(--color-bg-elevated)'}
                >
                  <span style={{ fontSize: 'var(--font-size-sm)', fontWeight: 'var(--font-weight-medium)', color: 'var(--color-text-primary)' }}>
                    {getLocalizedValue(m.nameRaw, primaryLang, m.id)}
                  </span>
                  <span style={{ fontSize: 'var(--font-size-xs)', color: 'var(--color-primary)' }}>
                    Open →
                  </span>
                </Link>
              ))}
            </div>
          )}
        </Card>
      )}

      {/* ── New: build note ───────────────────────────────────────────────── */}
      {!isEdit && (
        <div style={{
          padding: 'var(--space-3) var(--space-4)',
          backgroundColor: 'var(--color-bg-elevated)',
          border: '1px solid var(--color-border)',
          borderRadius: 'var(--radius-lg)',
          fontSize: 'var(--font-size-sm)',
          color: 'var(--color-text-muted)',
        }}>
          Binary and PROGMEM code will be generated automatically when you click "Create &amp; Build".
        </div>
      )}

      {/* ── Modals ───────────────────────────────────────────────────────── */}

      {/* Delete warning — archetype is in use */}
      {showDeleteWarning && (
        <div style={{ position: 'fixed', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 50, backgroundColor: 'rgba(0,0,0,0.6)' }}>
          <div style={{
            width: '100%', maxWidth: 440,
            backgroundColor: 'var(--color-bg-surface)',
            border: '1px solid var(--color-border)',
            borderRadius: 'var(--radius-xl)',
            boxShadow: 'var(--shadow-xl)',
            padding: 'var(--space-6)',
            display: 'flex', flexDirection: 'column', gap: 'var(--space-4)',
          }}>
            <h3 style={{ fontSize: 'var(--font-size-base)', fontWeight: 'var(--font-weight-semibold)', color: 'var(--color-text-primary)', margin: 0 }}>
              Archetype In Use
            </h3>
            <div style={{
              padding: 'var(--space-3)',
              backgroundColor: 'var(--color-warning-bg)',
              border: '1px solid var(--color-warning)',
              borderRadius: 'var(--radius-md)',
              fontSize: 'var(--font-size-sm)',
              color: 'var(--color-warning)',
            }}>
              <strong>"{name}"</strong> is assigned to <strong>{assignedCount} {assignedCount === 1 ? 'melody' : 'melodies'}</strong>. Deleting it will flag those melodies as <strong>outdated</strong>.
            </div>
            <p style={{ fontSize: 'var(--font-size-sm)', color: 'var(--color-text-muted)', margin: 0 }}>
              Do you still want to delete this archetype?
            </p>
            <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 'var(--space-3)' }}>
              <Button variant="ghost" onClick={() => setShowDeleteWarning(false)}>Cancel</Button>
              <Button variant="danger" onClick={() => { setShowDeleteWarning(false); setShowDelete(true) }}>
                Yes, Delete Anyway
              </Button>
            </div>
          </div>
        </div>
      )}

      <ConfirmDialog
        open={showDelete}
        onClose={() => setShowDelete(false)}
        onConfirm={handleDelete}
        variant="danger"
        title="Delete Archetype"
        message={`Are you sure you want to permanently delete "${name}"? This will also delete the .bsm binary file. This action cannot be undone.`}
        confirmLabel="Delete"
      />

      <PlaybackModal
        open={showPlayback}
        onClose={() => setShowPlayback(false)}
        melody={null}
        builtMelody={playbackBuiltMelody}
        files={null}
        archetypeCsv={steps}
      />
    </div>
  )
}
