// frontend/src/pages/melodies/MelodyDetail.jsx

import { useState, useEffect } from 'react'
import { useParams, useNavigate } from 'react-router-dom'
import PageHeader from '@/components/ui/PageHeader'
import Card from '@/components/ui/Card'
import Button from '@/components/ui/Button'
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,
  getLanguageName,
  normalizeColor,
  formatDuration,
} from '@/lib/melodyUtils'
import RowActions from '@/components/ui/RowActions'
import PillButton from '@/components/ui/PillButton'
import SpeedCalculatorModal from '@/modals/bellcloud/melodies/SpeedCalculatorModal'
import SpeedIcon from '@/assets/global-icons/speed.svg?react'
import PlaybackModal from '@/modals/bellcloud/melodies/PlaybackModal'
import BinaryTableModal from '@/modals/bellcloud/melodies/BinaryTableModal'

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

function formatBpm(ms) {
  const v = Number(ms)
  if (!v || v <= 0) return null
  return Math.round(60000 / v)
}

function mapPercentageToStepDelay(percent, minSpeed, maxSpeed) {
  const p = Math.max(0, Math.min(100, Number(percent || 0)))
  const t = p / 100
  const a = Number(minSpeed)
  const b = Number(maxSpeed)
  if (a <= 0 || b <= 0) return Math.round(a + (b - a) * t)
  return Math.round(a * Math.pow(b / a, t))
}

function normalizeFileUrl(url) {
  if (!url || typeof url !== 'string') return null
  if (url.startsWith('http') || url.startsWith('/api')) return url
  if (url.startsWith('/')) return `/api${url}`
  return `/api/${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)
}

// ─── Sub-components ──────────────────────────────────────────────────────────

function DetailField({ label, children, span = 1 }) {
  return (
    <div style={{ gridColumn: `span ${span}` }}>
      <div style={{ fontSize: 'var(--font-size-xs)', fontWeight: 'var(--font-weight-semibold)', textTransform: 'uppercase', letterSpacing: 'var(--tracking-wide)', color: 'var(--color-text-more-muted)', marginBottom: 'var(--space-1)' }}>
        {label}
      </div>
      <div style={{ fontSize: 'var(--font-size-sm)', color: 'var(--color-text-primary)' }}>
        {children ?? <span style={{ color: 'var(--color-text-muted)' }}>—</span>}
      </div>
    </div>
  )
}

function MonoField({ label, value, span = 1 }) {
  const [copied, setCopied] = useState(false)
  if (!value) return <DetailField label={label} span={span} />
  return (
    <div style={{ gridColumn: `span ${span}` }}>
      <div style={{ fontSize: 'var(--font-size-xs)', fontWeight: 'var(--font-weight-semibold)', textTransform: 'uppercase', letterSpacing: 'var(--tracking-wide)', color: 'var(--color-text-more-muted)', marginBottom: 'var(--space-1)' }}>
        {label}
      </div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-2)', flexWrap: 'wrap' }}>
        <span style={{ fontFamily: 'var(--font-family-mono)', fontSize: 'var(--font-size-sm)', color: 'var(--color-text-primary)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', minWidth: 0 }}>
          {value}
        </span>
        <button
          type="button"
          onClick={() => copyText(value, () => { setCopied(true); setTimeout(() => setCopied(false), 2000) })}
          style={{
            flexShrink: 0,
            fontSize: '10px',
            padding: '1px var(--space-2)',
            borderRadius: 'var(--radius-full)',
            backgroundColor: copied ? 'var(--color-success-bg)' : 'var(--color-bg-island)',
            color: copied ? 'var(--color-success)' : 'var(--color-text-muted)',
            border: `1px solid ${copied ? 'rgba(74,222,128,0.3)' : 'var(--color-border)'}`,
            cursor: 'pointer',
            transition: 'background 120ms, color 120ms',
            lineHeight: 1.6,
          }}
        >
          {copied ? 'Copied' : 'Copy'}
        </button>
      </div>
    </div>
  )
}

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

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

  const [melody,         setMelody]         = useState(null)
  const [files,          setFiles]          = useState({})
  const [builtMelody,    setBuiltMelody]    = useState(null)
  const [melodySettings, setMelodySettings] = useState(null)
  const [loading,        setLoading]        = useState(true)
  const [error,          setError]          = useState('')
  const [actionLoading,  setActionLoading]  = useState(false)
  const [displayLang,    setDisplayLang]    = useState('en')

  // Modal state
  const [showDelete,      setShowDelete]      = useState(false)
  const [showUnpublish,   setShowUnpublish]   = useState(false)
  const [showSpeedCalc,   setShowSpeedCalc]   = useState(false)
  const [showPlayback,    setShowPlayback]    = useState(false)
  const [showBinaryView,  setShowBinaryView]  = useState(false)
  const [codeCopied,      setCodeCopied]      = useState(false)

  useEffect(() => {
    api.get('/settings/melody').then((ms) => {
      setMelodySettings(ms)
      setDisplayLang(ms.primary_language || 'en')
    }).catch(() => {})
  }, [])

  useEffect(() => { loadData() }, [id])

  const loadData = async () => {
    setLoading(true)
    setError('')
    try {
      const [m, f] = await Promise.all([
        api.get(`/melodies/${id}`),
        api.get(`/melodies/${id}/files`),
      ])
      setMelody(m)
      setFiles(f)
      try {
        const bm = await api.get(`/builder/melodies/for-melody/${id}`)
        setBuiltMelody(bm || null)
      } catch { setBuiltMelody(null) }
    } catch (err) {
      setError(err.message || 'Failed to load melody.')
    } finally {
      setLoading(false)
    }
  }

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

  const handlePublish = async () => {
    setActionLoading(true)
    try {
      await api.post(`/melodies/${id}/publish`)
      await loadData()
      toast.success('Published', 'Melody is now live.')
    } catch (err) {
      toast.danger('Error', err.message)
    } finally {
      setActionLoading(false)
    }
  }

  const handleUnpublish = async () => {
    setActionLoading(true)
    try {
      await api.post(`/melodies/${id}/unpublish`)
      setShowUnpublish(false)
      await loadData()
      toast.success('Unpublished', 'Melody reverted to draft.')
    } catch (err) {
      toast.danger('Error', err.message)
      setShowUnpublish(false)
    } finally {
      setActionLoading(false)
    }
  }

  // ── Loading / error states ────────────────────────────────────────────────

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

  if (error || !melody) {
    return (
      <div className="page-wrapper">
        <PageHeader title="Melody" />
        <div style={{ padding: 'var(--space-4)', backgroundColor: 'var(--color-danger-bg)', border: '1px solid var(--color-danger)', borderRadius: 'var(--radius-md)', color: 'var(--color-danger)', fontSize: 'var(--font-size-sm)' }}>
          {error || 'Melody not found.'}
        </div>
      </div>
    )
  }

  // ── Derived values ────────────────────────────────────────────────────────

  const info       = melody.information || {}
  const settings   = melody.default_settings || {}
  const languages  = melodySettings?.available_languages || ['en']
  const displayName = getLocalizedValue(info.name, displayLang, 'Untitled Melody')
  const isPublished = melody.status === 'published'
  const speedMs    = mapPercentageToStepDelay(settings.speed, info.minSpeed, info.maxSpeed)
  const speedBpm   = formatBpm(speedMs)
  const minBpm     = formatBpm(info.minSpeed)
  const maxBpm     = formatBpm(info.maxSpeed)
  const missingArchetype = Boolean(melody.pid) && !builtMelody?.id

  const binaryUrl = normalizeFileUrl(
    (builtMelody?.binary_url ? `/api${builtMelody.binary_url}` : null) ||
    melody.url ||
    files.binary_url ||
    null
  )
  const binaryPid = builtMelody?.pid || melody.pid || 'binary'
  const binaryFilename = `${binaryPid}.bsm`

  const handleBinaryDownload = async (e) => {
    e.preventDefault()
    const endpoint = `/api/melodies/${melody.id}/download/binary`
    try {
      const token = localStorage.getItem('access_token')
      const res = await fetch(endpoint, { headers: token ? { Authorization: `Bearer ${token}` } : {} })
      if (!res.ok) throw new Error(res.statusText)
      const blob = await res.blob()
      const url = URL.createObjectURL(blob)
      const a = document.createElement('a'); a.href = url; a.download = binaryFilename; a.click()
      URL.revokeObjectURL(url)
    } catch {
      if (binaryUrl) { const a = document.createElement('a'); a.href = binaryUrl; a.download = binaryFilename; a.rel = 'noopener noreferrer'; document.body.appendChild(a); a.click(); document.body.removeChild(a) }
    }
  }

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

  return (
    <div className="page-wrapper">

      {/* ── Header ──────────────────────────────────────────────────────── */}
      <PageHeader
        title={
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 'var(--space-3)' }}>
            {displayName}
            <StatusBadge variant={isPublished ? 'success' : 'neutral'}>
              {isPublished ? 'Live' : 'Draft'}
            </StatusBadge>
            {languages.length > 1 && (
              <PillButton
                variant="neutral"
                value={displayLang}
                options={languages.map((l) => ({ label: getLanguageName(l), value: l }))}
                onChange={(val) => setDisplayLang(val)}
              >
                {getLanguageName(displayLang)}
              </PillButton>
            )}
          </span>
        }
      >

        {canEdit && (
          <>
            {/* Playback — success icon-only */}
            <Button
              variant="success"
              size="md"
              aria-label="Playback"
              onClick={() => setShowPlayback(true)}
              icon={
                <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" style={{ transform: 'scale(1.150)', transformOrigin: 'center' }}>
                  <path d="M5 3l14 9-14 9V3z"/>
                </svg>
              }
            />

            {/* Speed Calc — info icon-only (rabbit) */}
            <Button
              variant="info"
              size="md"
              aria-label="Speed Calculator"
              onClick={() => setShowSpeedCalc(true)}
              icon={
                <SpeedIcon width="16" height="16" aria-hidden="true" style={{ transform: 'scale(1.25)', transformOrigin: 'center' }} />
              }
            />

            {/* Actions dropdown — Edit, Publish/Unpublish, Delete */}
            <RowActions
              variant="primary"
              size="md"
              actions={[
                {
                  label: 'Edit',
                  icon: <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>,
                  onClick: () => navigate(`/melodies/${id}/edit`),
                },
                isPublished
                  ? {
                      label: 'Unpublish',
                      icon: <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><line x1="4.93" y1="4.93" x2="19.07" y2="19.07"/></svg>,
                      color: 'var(--color-warning)',
                      onClick: () => setShowUnpublish(true),
                    }
                  : {
                      label: 'Publish',
                      icon: <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 2v20M2 12h20"/></svg>,
                      color: 'var(--color-success)',
                      onClick: handlePublish,
                    },
                {
                  label: 'Delete',
                  icon: <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6M14 11v6"/><path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/></svg>,
                  color: 'var(--color-danger)',
                  divider: true,
                  onClick: () => setShowDelete(true),
                },
              ]}
            />
          </>
        )}
      </PageHeader>

      {/* ── Outdated archetype warning ───────────────────────────────────── */}
      {info.outdated_archetype && (
        <div style={{
          display: 'flex', alignItems: 'flex-start', gap: 'var(--space-3)',
          padding: 'var(--space-3) var(--space-4)',
          backgroundColor: 'var(--color-warning-bg)',
          border: '1px solid var(--color-warning)',
          borderRadius: 'var(--radius-lg)',
          color: 'var(--color-warning)',
          fontSize: 'var(--font-size-sm)',
        }}>
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0, marginTop: 2 }}>
            <path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/>
          </svg>
          <span>
            <strong>Outdated Archetype</strong> — The archetype assigned to this melody has changed or been removed. Re-assign one in the editor to clear this warning.
          </span>
        </div>
      )}

      {/* ── Main grid — masonry layout ──────────────────────────────────── */}
      <div className="masonry-grid masonry-grid--2">

        {/* Melody Information — masonry item 1 */}
        <Card title="Melody Information">
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 'var(--space-4)' }}>
            {/* Row 1: Color | Tone | Steps */}
            <DetailField label="Color" span={1}>
              {info.color ? (
                <span style={{ display: 'inline-flex', alignItems: 'center', gap: 'var(--space-2)' }}>
                  <span style={{ width: 16, height: 16, borderRadius: 'var(--radius-sm)', backgroundColor: normalizeColor(info.color), border: '1px solid var(--color-border)', display: 'inline-block', flexShrink: 0 }} />
                  <span style={{ fontFamily: 'var(--font-family-mono)', fontSize: 'var(--font-size-xs)' }}>{info.color}</span>
                </span>
              ) : null}
            </DetailField>
            <DetailField label="Tone">
              <span style={{ textTransform: 'capitalize' }}>{info.melodyTone ?? '—'}</span>
            </DetailField>
            <DetailField label="Steps">{info.steps}</DetailField>

            {/* Row 2: Min Speed | Max Speed | Unique Bells */}
            <DetailField label="Min Speed">
              {info.minSpeed ? (
                <span>{minBpm} bpm <span style={{ color: 'var(--color-text-muted)', fontSize: 'var(--font-size-xs)' }}>· {info.minSpeed} ms</span></span>
              ) : null}
            </DetailField>
            <DetailField label="Max Speed">
              {info.maxSpeed ? (
                <span>{maxBpm} bpm <span style={{ color: 'var(--color-text-muted)', fontSize: 'var(--font-size-xs)' }}>· {info.maxSpeed} ms</span></span>
              ) : null}
            </DetailField>
            <DetailField label="Unique Bells">{info.totalActiveBells}</DetailField>

            {/* Row 3: True Ring | Type | Tags */}
            <DetailField label="True Ring">
              <StatusBadge variant={info.isTrueRing ? 'success' : 'neutral'}>{info.isTrueRing ? 'Yes' : 'No'}</StatusBadge>
            </DetailField>
            <DetailField label="Type">
              <span style={{ textTransform: 'capitalize' }}>{melody.type ?? '—'}</span>
            </DetailField>
            {info.customTags?.length > 0 ? (
              <DetailField label="Tags">
                <div style={{ display: 'flex', flexWrap: 'wrap', gap: 'var(--space-1)', marginTop: 'var(--space-1)' }}>
                  {info.customTags.map((tag) => (
                    <span key={tag} style={{
                      fontSize: 'var(--font-size-xs)', padding: '2px var(--space-2)',
                      borderRadius: 'var(--radius-full)',
                      backgroundColor: 'var(--color-primary-subtle)',
                      color: 'var(--color-primary)',
                      border: '1px solid var(--color-primary)',
                    }}>{tag}</span>
                  ))}
                </div>
              </DetailField>
            ) : (
              <DetailField label="Tags">
                <span style={{ color: 'var(--color-text-muted)' }}>—</span>
              </DetailField>
            )}

            {/* Row 4: Description (full width) */}
            <DetailField label="Description" span={3}>
              {getLocalizedValue(info.description, displayLang) || null}
            </DetailField>
          </div>
        </Card>

        {/* Files — masonry item 2 */}
        <Card title="Files">
            <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-4)' }}>

              {/* Binary */}
              <div>
                <div style={{ fontSize: 'var(--font-size-xs)', fontWeight: 'var(--font-weight-semibold)', textTransform: 'uppercase', letterSpacing: 'var(--tracking-wide)', color: 'var(--color-text-more-muted)', marginBottom: 'var(--space-2)' }}>
                  Binary File
                </div>
                {binaryUrl ? (
                  <div>
                    {/* Main row: name + pills + note count */}
                    <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-2)', flexWrap: 'wrap' }}>
                      <span style={{ fontSize: 'var(--font-size-sm)', fontWeight: 'var(--font-weight-semibold)', color: 'var(--color-text-primary)' }}>
                        {builtMelody?.name || binaryFilename}
                      </span>
                      {missingArchetype && (
                        <span title="This binary does not exist in the Archetypes" style={{ color: 'var(--color-warning)', fontSize: 'var(--font-size-xs)', cursor: 'help' }}>⚠ Missing</span>
                      )}
                      {/* Download — blue pill */}
                      <button
                        type="button"
                        onClick={handleBinaryDownload}
                        style={{
                          padding: '2px 10px',
                          borderRadius: 'var(--radius-full)',
                          fontSize: 'var(--font-size-xs)',
                          fontWeight: 'var(--font-weight-medium)',
                          color: 'var(--color-primary)',
                          backgroundColor: 'var(--color-primary-subtle)',
                          border: 'none',
                          cursor: 'pointer',
                        }}
                      >
                        Download
                      </button>
                      {/* View — neutral pill */}
                      <button
                        type="button"
                        onClick={() => setShowBinaryView(true)}
                        style={{
                          padding: '2px 10px',
                          borderRadius: 'var(--radius-full)',
                          fontSize: 'var(--font-size-xs)',
                          fontWeight: 'var(--font-weight-medium)',
                          color: 'var(--color-text-secondary)',
                          backgroundColor: 'var(--color-bg-elevated)',
                          border: '1px solid var(--color-border)',
                          cursor: 'pointer',
                        }}
                      >
                        View
                      </button>
                      <span style={{ fontSize: 'var(--font-size-xs)', color: 'var(--color-text-muted)' }}>
                        {info.totalNotes ?? 0} active notes
                      </span>
                    </div>
                    {/* Subtitle: file: archetypename.bsm */}
                    <div style={{ marginTop: 'var(--space-1)', fontSize: 'var(--font-size-xs)', color: 'var(--color-text-muted)', fontFamily: 'var(--font-family-mono)' }}>
                      file: {binaryFilename}
                    </div>
                  </div>
                ) : (
                  <span style={{ fontSize: 'var(--font-size-sm)', color: 'var(--color-text-muted)' }}>Not uploaded</span>
                )}
              </div>

              {/* Audio preview */}
              <div>
                <div style={{ fontSize: 'var(--font-size-xs)', fontWeight: 'var(--font-weight-semibold)', textTransform: 'uppercase', letterSpacing: 'var(--tracking-wide)', color: 'var(--color-text-more-muted)', marginBottom: 'var(--space-2)' }}>
                  Audio Preview
                </div>
                {normalizeFileUrl(files.preview_url) ? (
                  <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-2)' }}>
                    <audio controls src={normalizeFileUrl(files.preview_url)} style={{ height: 32, width: '100%' }} />
                    <a href={normalizeFileUrl(files.preview_url)} target="_blank" rel="noopener noreferrer"
                      style={{ fontSize: 'var(--font-size-xs)', color: 'var(--color-primary)', textDecoration: 'underline' }}>
                      Download preview
                    </a>
                  </div>
                ) : (
                  <span style={{ fontSize: 'var(--font-size-sm)', color: 'var(--color-text-muted)' }}>Not uploaded</span>
                )}
              </div>
            </div>
        </Card>

        {/* Default Settings — masonry item 3 */}
        <Card title="Default Settings">
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 'var(--space-4)' }}>
              {/* Row 1: Speed | Duration | Total Run Duration */}
              <DetailField label="Speed">
                {settings.speed != null ? (
                  <span>
                    {settings.speed}%
                    {speedBpm && <span style={{ color: 'var(--color-text-muted)', fontSize: 'var(--font-size-xs)' }}> · {speedBpm} bpm</span>}
                    {speedMs  && <span style={{ color: 'var(--color-text-muted)', fontSize: 'var(--font-size-xs)' }}> · {speedMs} ms</span>}
                  </span>
                ) : null}
              </DetailField>
              <DetailField label="Duration">{formatDuration(settings.duration ?? 0)}</DetailField>
              <DetailField label="Total Run Duration">{settings.totalRunDuration}</DetailField>

              {/* Row 2: Pause Duration | Infinite Loop */}
              <DetailField label="Pause Duration">{settings.pauseDuration}</DetailField>
              <DetailField label="Infinite Loop">
                <StatusBadge variant={settings.infiniteLoop ? 'success' : 'neutral'}>{settings.infiniteLoop ? 'Yes' : 'No'}</StatusBadge>
              </DetailField>

              {/* Row 3: Echo Ring */}
              <DetailField label="Echo Ring">
                {settings.echoRing?.length ? (
                  <span style={{ fontFamily: 'var(--font-family-mono)', fontSize: 'var(--font-size-xs)' }}>
                    {settings.echoRing.join(', ')}
                  </span>
                ) : null}
              </DetailField>
            </div>

            {/* Note assignments */}
            {settings.noteAssignments?.length > 0 && (
              <div style={{ marginTop: 'var(--space-4)', paddingTop: 'var(--space-4)', borderTop: '1px solid var(--color-border)' }}>
                <div style={{ fontSize: 'var(--font-size-xs)', fontWeight: 'var(--font-weight-semibold)', textTransform: 'uppercase', letterSpacing: 'var(--tracking-wide)', color: 'var(--color-text-more-muted)', marginBottom: 'var(--space-2)' }}>
                  Note Assignments
                </div>
                <div style={{ display: 'flex', flexWrap: 'wrap', gap: 'var(--space-1)' }}>
                  {settings.noteAssignments.map((bell, i) => (
                    <div key={i} style={{
                      display: 'flex', flexDirection: 'column', alignItems: 'center',
                      minWidth: 36, padding: 'var(--space-1) var(--space-2)',
                      backgroundColor: 'var(--color-bg-elevated)',
                      border: '1px solid var(--color-border)',
                      borderRadius: 'var(--radius-md)',
                    }}>
                      <span style={{ fontSize: 'var(--font-size-xs)', fontWeight: 'var(--font-weight-semibold)', color: 'var(--color-text-secondary)' }}>
                        {String.fromCharCode(65 + i)}
                      </span>
                      <div style={{ width: '100%', height: 1, backgroundColor: 'var(--color-border)', margin: '2px 0' }} />
                      <span style={{ fontSize: 'var(--font-size-xs)', color: bell > 0 ? 'var(--color-text-primary)' : 'var(--color-text-muted)', fontFamily: 'var(--font-family-mono)' }}>
                        {bell > 0 ? bell : '—'}
                      </span>
                    </div>
                  ))}
                </div>
                <p style={{ marginTop: 'var(--space-1)', fontSize: 'var(--font-size-xs)', color: 'var(--color-text-muted)' }}>
                  Top = Note label · Bottom = Assigned bell number
                </p>
              </div>
            )}
        </Card>

        {/* Identifiers — masonry item 4 */}
        <Card title="Identifiers">
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 'var(--space-4)' }}>
            <MonoField label="Document ID" value={melody.id} />
            <MonoField label="PID" value={melody.pid} />
            {melody.url && <MonoField label="URL" value={melody.url} span={2} />}
          </div>
        </Card>

        {/* History — masonry item 5 */}
        {melody.metadata && (
          <Card title="History">
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 'var(--space-4)' }}>
              {melody.metadata.dateCreated && (
                <DetailField label="Created">{melody.metadata.dateCreated}</DetailField>
              )}
              {melody.metadata.createdBy && (
                <DetailField label="Created By">{melody.metadata.createdBy}</DetailField>
              )}
              {melody.metadata.dateEdited && (
                <DetailField label="Last Edited">{melody.metadata.dateEdited}</DetailField>
              )}
              {melody.metadata.lastEditedBy && (
                <DetailField label="Last Edited By">{melody.metadata.lastEditedBy}</DetailField>
              )}
            </div>
          </Card>
        )}

        {/* Admin Notes — masonry item 5 */}
        <Card title="Admin Notes">
          {(melody.metadata?.adminNotes?.length || 0) > 0 ? (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-2)' }}>
              {melody.metadata.adminNotes.map((note, i) => (
                <div key={i} style={{
                  padding: 'var(--space-3)',
                  backgroundColor: 'var(--color-bg-elevated)',
                  border: '1px solid var(--color-border)',
                  borderRadius: 'var(--radius-md)',
                  fontSize: 'var(--font-size-sm)',
                  color: 'var(--color-text-primary)',
                }}>
                  {note}
                </div>
              ))}
            </div>
          ) : (
            <p style={{ fontSize: 'var(--font-size-sm)', color: 'var(--color-text-muted)', margin: 0 }}>
              No admin notes yet. Add them in the editor.
            </p>
          )}
        </Card>

      </div>

      {/* ── Firmware code ────────────────────────────────────────────────── */}
      {builtMelody?.progmem_code && (
        <Card
          title="Firmware Code"
          subtitle={`PROGMEM code for built-in firmware playback · PID: ${builtMelody.pid}`}
        >
          <div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 'var(--space-2)' }}>
            <Button
              variant={codeCopied ? 'success' : 'secondary'}
              size="sm"
              onClick={() => copyText(builtMelody.progmem_code, () => { setCodeCopied(true); setTimeout(() => setCodeCopied(false), 2000) })}
            >
              {codeCopied ? 'Copied!' : 'Copy Code'}
            </Button>
          </div>
          <pre style={{
            margin: 0, padding: 'var(--space-4)',
            backgroundColor: 'var(--color-bg-abyss)',
            border: '1px solid var(--color-border)',
            borderRadius: 'var(--radius-md)',
            fontSize: 'var(--font-size-sm)',
            fontFamily: 'var(--font-family-mono)',
            color: 'var(--color-text-primary)',
            overflowX: 'auto', maxHeight: 360, overflowY: 'auto',
            whiteSpace: 'pre',
          }}>
            {builtMelody.progmem_code}
          </pre>
        </Card>
      )}

      {/* ── Modals ───────────────────────────────────────────────────────── */}
      <ConfirmDialog
        open={showDelete}
        onClose={() => setShowDelete(false)}
        onConfirm={handleDelete}
        variant="danger"
        title="Delete Melody"
        message={`Are you sure you want to delete "${displayName}"? This action cannot be undone.`}
        confirmLabel="Delete"
      />

      <ConfirmDialog
        open={showUnpublish}
        onClose={() => setShowUnpublish(false)}
        onConfirm={handleUnpublish}
        variant="danger"
        title="Unpublish Melody"
        message={`Unpublish "${displayName}"? It will revert to draft and become unavailable to devices.`}
        confirmLabel="Unpublish"
        loading={actionLoading}
      />

      <SpeedCalculatorModal
        open={showSpeedCalc}
        onClose={() => setShowSpeedCalc(false)}
        melody={melody}
        builtMelody={builtMelody}
        archetypeCsv={info.archetype_csv || null}
        onSaved={() => { setShowSpeedCalc(false); loadData() }}
      />

      <PlaybackModal
        open={showPlayback}
        onClose={() => setShowPlayback(false)}
        melody={melody}
        builtMelody={builtMelody}
        files={files}
        archetypeCsv={info.archetype_csv || null}
      />

      <BinaryTableModal
        open={showBinaryView}
        onClose={() => setShowBinaryView(false)}
        melody={melody}
        builtMelody={builtMelody}
        files={files}
        archetypeCsv={info.archetype_csv || null}
      />
    </div>
  )
}
