// frontend/src/pages/manufacturing/firmware/FlashAssetManager.jsx

import { useState, useEffect, useCallback } from 'react'
import api from '@/lib/api'
import { useAuth } from '@/hooks/useAuth'
import PageHeader from '@/components/ui/PageHeader'
import Button from '@/components/ui/Button'
import StatusBadge from '@/components/ui/StatusBadge'
import Spinner from '@/components/ui/Spinner'
import Icon from '@/components/ui/Icon'
import Modal from '@/components/ui/Modal'
import FormField from '@/components/ui/FormField'
import ConfirmDialog from '@/components/ui/ConfirmDialog'
import UploadAssetModal from '@/modals/engineering/manufacturing/UploadAssetModal'
import { fmtDateTimeMedium } from '@/lib/formatters'

// ─── Constants ────────────────────────────────────────────────────────────────

const KNOWN_BOARD_TYPES = [
  { value: 'vesper',      label: 'Vesper',       family: 'vesper'  },
  { value: 'vesper_plus', label: 'Vesper+',      family: 'vesper'  },
  { value: 'vesper_pro',  label: 'Vesper Pro',   family: 'vesper'  },
  { value: 'agnus',       label: 'Agnus',        family: 'agnus'   },
  { value: 'agnus_mini',  label: 'Agnus Mini',   family: 'agnus'   },
  { value: 'chronos',     label: 'Chronos',      family: 'chronos' },
  { value: 'chronos_pro', label: 'Chronos Pro',  family: 'chronos' },
]

const FAMILY_ACCENT = {
  vesper:  'var(--color-info)',
  agnus:   'var(--color-warning)',
  chronos: 'var(--color-danger)',
  bespoke: 'var(--color-primary)',
}

const BOARD_LABELS = Object.fromEntries(KNOWN_BOARD_TYPES.map((b) => [b.value, b.label]))

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

function formatBytes(bytes) {
  if (!bytes) return '—'
  if (bytes < 1024) return `${bytes} B`
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
  return `${(bytes / (1024 * 1024)).toFixed(2)} MB`
}

function formatDate(iso) { return fmtDateTimeMedium(iso) }

function boardLabel(hwType, isBespoke) {
  if (isBespoke) return hwType
  return BOARD_LABELS[hwType] || hwType
}

function familyOf(hwType, isBespoke) {
  if (isBespoke) return 'bespoke'
  return KNOWN_BOARD_TYPES.find((b) => b.value === hwType)?.family ?? 'vesper'
}

// ─── Note Modal ───────────────────────────────────────────────────────────────

function NoteModal({ open, hwType, currentNote, onClose, onSaved }) {
  const [note, setNote]     = useState(currentNote || '')
  const [saving, setSaving] = useState(false)
  const [error, setError]   = useState('')

  const handleSave = async () => {
    setSaving(true); setError('')
    try {
      await api.put(`/manufacturing/flash-assets/${hwType}/note`, { note })
      onSaved(note)
      onClose()
    } catch (err) {
      setError(err.message || 'Failed to save note')
    } finally {
      setSaving(false)
    }
  }

  return (
    <Modal
      open={open}
      onClose={onClose}
      title="Asset Note"
      size="sm"
      footer={
        <div style={{ display: 'flex', gap: 'var(--space-3)' }}>
          <Button variant="ghost" onClick={onClose}>Cancel</Button>
          <Button variant="primary" onClick={handleSave} loading={saving}>Save Note</Button>
        </div>
      }
    >
      <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-3)' }}>
        <p style={{ fontSize: 'var(--font-size-xs)', color: 'var(--color-text-muted)' }}>
          Board: <span style={{ fontFamily: 'var(--font-family-mono)', color: 'var(--color-text-secondary)' }}>{hwType}</span>
          {' '}— stored alongside binaries as{' '}
          <span style={{ fontFamily: 'var(--font-family-mono)', color: 'var(--color-text-secondary)' }}>note.txt</span>
        </p>
        {error && (
          <div style={{ padding: 'var(--space-2)', borderRadius: 'var(--radius-md)', backgroundColor: 'var(--color-danger-bg)', border: '1px solid var(--color-danger)', color: 'var(--color-danger)', fontSize: 'var(--font-size-sm)' }}>
            {error}
          </div>
        )}
        <FormField
          label="Note"
          name="asset_note"
          type="textarea"
          value={note}
          onChange={(e) => setNote(e.target.value)}
          placeholder="e.g. Built from PlatformIO env:vesper_plus_v2, commit abc1234…"
          hint="Leave blank to clear the note."
        />
      </div>
    </Modal>
  )
}

// ─── Asset Pill ───────────────────────────────────────────────────────────────

function AssetPill({ label, info, canDelete, onUpload, onDelete }) {
  const exists = info?.exists
  return (
    <div style={{
      display: 'flex',
      alignItems: 'center',
      gap: 'var(--space-2)',
      padding: 'var(--space-2) var(--space-3)',
      borderRadius: 'var(--radius-md)',
      border: `1px solid ${exists ? 'var(--color-border)' : 'var(--color-danger)'}`,
      backgroundColor: exists ? 'var(--color-bg-elevated)' : 'var(--color-danger-bg)',
    }}>
      <Icon
        name={exists ? 'check' : 'warning'}
        size={14}
        color={exists ? 'var(--color-success)' : 'var(--color-danger)'}
      />
      <div style={{ flex: 1, minWidth: 0 }}>
        <p style={{ fontFamily: 'var(--font-family-mono)', fontSize: 'var(--font-size-xs)', fontWeight: 'var(--font-weight-semibold)', color: exists ? 'var(--color-text-secondary)' : 'var(--color-danger)', margin: 0 }}>
          {label}
        </p>
        {exists ? (
          <p style={{ fontSize: 'var(--font-size-xs)', color: 'var(--color-text-muted)', margin: 0 }}>
            {formatBytes(info.size_bytes)}{info.uploaded_at ? ` · ${formatDate(info.uploaded_at)}` : ''}
          </p>
        ) : (
          <p style={{ fontSize: 'var(--font-size-xs)', color: 'var(--color-danger)', margin: 0, opacity: 0.8 }}>Not uploaded</p>
        )}
      </div>
      <div style={{ display: 'flex', gap: 'var(--space-1)', flexShrink: 0 }}>
        <Button variant="ghost" size="sm" onClick={onUpload} aria-label={exists ? 'Re-upload' : 'Upload'}>
          <Icon name="upload" size={12} />
        </Button>
        {exists && canDelete && (
          <Button variant="danger" size="sm" onClick={onDelete} aria-label="Delete">
            <Icon name="delete" size={12} />
          </Button>
        )}
      </div>
    </div>
  )
}

// ─── Board Card ───────────────────────────────────────────────────────────────

function BoardCard({ entry, canDelete, canEdit, onUpload, onDelete, onNote, onDeleteNote }) {
  const { hw_type, bootloader, partitions, note, is_bespoke } = entry
  const family = familyOf(hw_type, is_bespoke)
  const accent = FAMILY_ACCENT[family] || 'var(--color-primary)'
  const label  = boardLabel(hw_type, is_bespoke)

  const hasBootloader = bootloader?.exists
  const hasPartitions = partitions?.exists
  const hasAll  = hasBootloader && hasPartitions
  const hasNone = !hasBootloader && !hasPartitions
  const readyVariant = hasAll ? 'success' : hasNone ? 'danger' : 'warning'
  const readyLabel   = hasAll ? 'Ready'   : hasNone ? 'Missing' : 'Partial'

  const [noteHovered, setNoteHovered] = useState(false)

  return (
    <div style={{
      backgroundColor: 'var(--color-bg-surface)',
      border: '1px solid var(--color-border)',
      borderLeft: `3px solid ${accent}`,
      borderRadius: 'var(--radius-lg)',
      boxShadow: 'var(--shadow-card)',
      overflow: 'hidden',
    }}>
      {/* Header */}
      <div style={{ padding: 'var(--space-3) var(--space-4)', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 'var(--space-2)', borderBottom: '1px solid var(--color-border)' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-2)', minWidth: 0 }}>
          <span style={{ fontSize: 'var(--font-size-sm)', fontWeight: 'var(--font-weight-semibold)', color: 'var(--color-text-primary)', fontFamily: 'var(--font-family-display)', letterSpacing: 'var(--tracking-tight)' }}>
            {label}
          </span>
          {is_bespoke && <StatusBadge variant="info">bespoke</StatusBadge>}
          <span style={{ fontFamily: 'var(--font-family-mono)', fontSize: 'var(--font-size-xs)', color: 'var(--color-text-muted)' }}>
            {hw_type}
          </span>
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-2)', flexShrink: 0 }}>
          <StatusBadge variant={readyVariant}>{readyLabel}</StatusBadge>
          {canEdit && (
            <Button variant="ghost" size="sm" onClick={() => onNote(entry)}>
              <Icon name="note" size={12} />
              {note ? 'Edit note' : 'Add note'}
            </Button>
          )}
        </div>
      </div>

      {/* Asset pills */}
      <div style={{ padding: 'var(--space-3) var(--space-4)', display: 'flex', flexDirection: 'column', gap: 'var(--space-2)' }}>
        <AssetPill label="bootloader.bin" info={bootloader} canDelete={canDelete}
          onUpload={() => onUpload(hw_type, 'bootloader.bin')}
          onDelete={() => onDelete(hw_type, 'bootloader.bin')} />
        <AssetPill label="partitions.bin" info={partitions} canDelete={canDelete}
          onUpload={() => onUpload(hw_type, 'partitions.bin')}
          onDelete={() => onDelete(hw_type, 'partitions.bin')} />
      </div>

      {/* Note — shown below the files, hover-X to delete */}
      {note && (
        <div
          onMouseEnter={() => setNoteHovered(true)}
          onMouseLeave={() => setNoteHovered(false)}
          style={{
            padding: 'var(--space-2) var(--space-4)',
            backgroundColor: 'var(--color-bg-elevated)',
            borderTop: '1px solid var(--color-border)',
            display: 'flex', alignItems: 'flex-start', gap: 'var(--space-2)',
          }}
        >
          <p style={{ flex: 1, fontSize: 'var(--font-size-xs)', color: 'var(--color-text-muted)', fontStyle: 'italic', lineHeight: 1.5, margin: 0 }}>
            {note}
          </p>
          {canEdit && noteHovered && (
            <button
              aria-label="Delete note"
              onClick={() => onDeleteNote(entry)}
              style={{
                flexShrink: 0, background: 'none', border: 'none', cursor: 'pointer',
                color: 'var(--color-text-muted)', padding: 'var(--space-1)',
                borderRadius: 'var(--radius-sm)', lineHeight: 1,
                display: 'flex', alignItems: 'center',
              }}
              onMouseEnter={(e) => { e.currentTarget.style.color = 'var(--color-danger)' }}
              onMouseLeave={(e) => { e.currentTarget.style.color = 'var(--color-text-muted)' }}
            >
              <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
                <line x1="2" y1="2" x2="10" y2="10"/><line x1="10" y1="2" x2="2" y2="10"/>
              </svg>
            </button>
          )}
        </div>
      )}
    </div>
  )
}

// ─── Main Page ────────────────────────────────────────────────────────────────

export default function FlashAssetManager({ embedded = false }) {
  const { hasPermission } = useAuth()
  const canDelete = hasPermission('manufacturing', 'delete')
  const canEdit   = hasPermission('manufacturing', 'edit')

  const [assets,  setAssets]  = useState([])
  const [loading, setLoading] = useState(true)
  const [error,   setError]   = useState('')
  const [filter,  setFilter]  = useState('all')

  const [uploadTarget,   setUploadTarget]   = useState(null)
  const [deleteTarget,   setDeleteTarget]   = useState(null)
  const [noteTarget,     setNoteTarget]     = useState(null)
  const [noteDelTarget,  setNoteDelTarget]  = useState(null)
  const [showConfirmDel, setShowConfirmDel] = useState(false)

  const fetchAssets = useCallback(async () => {
    setLoading(true); setError('')
    try {
      const data = await api.get('/manufacturing/flash-assets')
      setAssets(data.assets || [])
    } catch (err) {
      setError(err.message || 'Failed to load flash assets')
    } finally {
      setLoading(false)
    }
  }, [])

  useEffect(() => { fetchAssets() }, [fetchAssets])

  const handleDelete = async () => {
    if (!deleteTarget) return
    try {
      const token = localStorage.getItem('access_token')
      const res = await fetch(`/api/manufacturing/flash-assets/${deleteTarget.hwType}/${deleteTarget.assetName}`, {
        method: 'DELETE',
        headers: { Authorization: `Bearer ${token}` },
      })
      if (!res.ok) throw new Error('Delete failed')
      setShowConfirmDel(false); setDeleteTarget(null)
      fetchAssets()
    } catch (err) {
      setError(err.message)
    }
  }

  const handleNoteUpdate = (hwType, newNote) => {
    setAssets((prev) => prev.map((a) => a.hw_type === hwType ? { ...a, note: newNote } : a))
  }

  const handleDeleteNote = async (entry) => {
    try {
      await api.put(`/manufacturing/flash-assets/${entry.hw_type}/note`, { note: '' })
      handleNoteUpdate(entry.hw_type, '')
    } catch (err) {
      setError(err.message || 'Failed to delete note')
    } finally {
      setNoteDelTarget(null)
    }
  }

  const filteredAssets = assets.filter((e) => {
    const hasAll  = e.bootloader?.exists && e.partitions?.exists
    const hasNone = !e.bootloader?.exists && !e.partitions?.exists
    if (filter === 'ready')   return hasAll
    if (filter === 'missing') return hasNone
    if (filter === 'partial') return !hasAll && !hasNone
    return true
  })

  const readyCount   = assets.filter((e) => e.bootloader?.exists && e.partitions?.exists).length
  const missingCount = assets.filter((e) => !e.bootloader?.exists && !e.partitions?.exists).length
  const partialCount = assets.length - readyCount - missingCount

  const FILTER_OPTIONS = [
    { value: 'all',     label: `All (${assets.length})` },
    { value: 'ready',   label: `Ready (${readyCount})` },
    { value: 'partial', label: `Partial (${partialCount})` },
    { value: 'missing', label: `Missing (${missingCount})` },
  ]

  const content = (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-4)' }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 'var(--space-3)', flexWrap: 'wrap' }}>
        <div style={{ display: 'flex', gap: 'var(--space-3)', flexWrap: 'wrap' }}>
          {[
            { label: 'Total',   value: assets.length, variant: 'neutral' },
            { label: 'Ready',   value: readyCount,    variant: 'success' },
            { label: 'Partial', value: partialCount,  variant: 'warning' },
            { label: 'Missing', value: missingCount,  variant: 'danger'  },
          ].map(({ label, value, variant }) => (
            <div key={label} style={{
              display: 'flex', alignItems: 'center', gap: 'var(--space-2)',
              padding: 'var(--space-2) var(--space-3)',
              backgroundColor: 'var(--color-bg-surface)',
              border: '1px solid var(--color-border)',
              borderRadius: 'var(--radius-lg)',
              boxShadow: 'var(--shadow-card)',
            }}>
              <span style={{ fontSize: 'var(--font-size-xs)', color: 'var(--color-text-muted)', fontWeight: 'var(--font-weight-semibold)' }}>
                {label}
              </span>
              <StatusBadge variant={variant}>{value}</StatusBadge>
            </div>
          ))}
        </div>
        <Button variant="ghost" size="sm" onClick={fetchAssets}>
          <Icon name="refresh" size={14} />
          Refresh
        </Button>
      </div>

      <div style={{ display: 'flex', gap: 'var(--space-2)' }}>
        {FILTER_OPTIONS.map(({ value, label }) => (
          <Button key={value} variant={filter === value ? 'secondary' : 'ghost'} size="sm" onClick={() => setFilter(value)}>
            {label}
          </Button>
        ))}
      </div>

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

      {loading ? (
        <div style={{ display: 'flex', justifyContent: 'center', padding: 'var(--space-16)' }}>
          <Spinner size="lg" />
        </div>
      ) : filteredAssets.length === 0 ? (
        <div style={{ padding: 'var(--space-12)', textAlign: 'center' }}>
          <Icon name="box" size={40} color="var(--color-text-muted)" />
          <p style={{ marginTop: 'var(--space-3)', fontSize: 'var(--font-size-md)', color: 'var(--color-text-secondary)' }}>
            No assets match the current filter.
          </p>
        </div>
      ) : (
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(320px, 1fr))', gap: 'var(--space-4)' }}>
          {filteredAssets.map((entry) => (
            <BoardCard
              key={entry.hw_type}
              entry={entry}
              canDelete={canDelete}
              canEdit={canEdit}
              onUpload={(hwType, assetName) => setUploadTarget({ hwType, assetName })}
              onDelete={(hwType, assetName) => { setDeleteTarget({ hwType, assetName }); setShowConfirmDel(true) }}
              onNote={(e) => setNoteTarget(e)}
              onDeleteNote={(e) => setNoteDelTarget(e)}
            />
          ))}
        </div>
      )}

      <UploadAssetModal
        open={!!uploadTarget}
        hwType={uploadTarget?.hwType}
        assetName={uploadTarget?.assetName}
        onClose={() => setUploadTarget(null)}
        onSaved={() => { setUploadTarget(null); fetchAssets() }}
      />
      <NoteModal
        open={!!noteTarget}
        hwType={noteTarget?.hw_type}
        currentNote={noteTarget?.note}
        onClose={() => setNoteTarget(null)}
        onSaved={(newNote) => { if (noteTarget) handleNoteUpdate(noteTarget.hw_type, newNote); setNoteTarget(null) }}
      />
      <ConfirmDialog
        open={!!noteDelTarget}
        variant="danger"
        title="Delete note?"
        message={noteDelTarget ? `Remove the note for ${boardLabel(noteDelTarget.hw_type, noteDelTarget.is_bespoke)}?` : ''}
        confirmLabel="Delete"
        onConfirm={() => handleDeleteNote(noteDelTarget)}
        onCancel={() => setNoteDelTarget(null)}
      />
      <ConfirmDialog
        open={showConfirmDel}
        variant="danger"
        title="Delete flash asset?"
        message={deleteTarget ? `Delete ${deleteTarget.assetName} for ${deleteTarget.hwType}? This cannot be undone. Flashing will fail until the file is re-uploaded.` : ''}
        confirmLabel="Delete"
        onConfirm={handleDelete}
        onCancel={() => { setShowConfirmDel(false); setDeleteTarget(null) }}
      />
    </div>
  )

  if (embedded) return content

  return (
    <div className="page-wrapper">
      <PageHeader title="Flash Asset Manager" subtitle="Manage bootloader and partition binaries for each board type" />
      {content}
    </div>
  )
}
