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

import { useState, useEffect, useMemo } from 'react'
import { useNavigate, Link } from 'react-router-dom'
import api from '@/lib/api'
import { useAuth } from '@/hooks/useAuth'
import PageHeader from '@/components/ui/PageHeader'
import Button from '@/components/ui/Button'
import DataTable from '@/components/ui/DataTable'
import Pagination from '@/components/ui/Pagination'
import SearchBar from '@/components/ui/SearchBar'
import RowActions from '@/components/ui/RowActions'
import Icon from '@/components/ui/Icon'
import ConfirmDialog from '@/components/ui/ConfirmDialog'
import Modal from '@/components/ui/Modal'
import UploadArchetypeModal from '@/modals/bellcloud/melodies/UploadArchetypeModal'
import CodeSnippetModal from '@/modals/bellcloud/melodies/CodeSnippetModal'
import GenerateBuiltinModal from '@/modals/bellcloud/melodies/GenerateBuiltinModal'
import { fmtRelative, fmtDateTime as fmtDateTimeCentral } from '@/lib/formatters'

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

function countSteps(s) {
  if (!s) return 0
  return s.split(',').length
}

function formatRelativeTime(isoValue) { return fmtRelative(isoValue) }

function getLocalizedValue(value, lang, fallback = '') {
  if (!value) return fallback
  if (typeof value === 'object') return value[lang] || value['en'] || Object.values(value)[0] || fallback
  try {
    const parsed = JSON.parse(value)
    if (typeof parsed === 'object') return parsed[lang] || parsed['en'] || Object.values(parsed)[0] || fallback
  } catch { return value || fallback }
  return fallback
}

// ─── Column definitions ───────────────────────────────────────────────────────

const ALL_COLUMNS = [
  { key: 'name',     label: 'Name',       defaultOn: true,  alwaysOn: true, sortable: true },
  { key: 'pid',      label: 'PID',        defaultOn: true,  sortable: true  },
  { key: 'steps',    label: 'Steps',      defaultOn: true,  sortable: true,  align: 'center' },
  { key: 'builtin',  label: 'Built-in',   defaultOn: true,  align: 'center' },
  { key: 'binary',   label: 'Binary',     defaultOn: true,  align: 'center' },
  { key: 'progmem',  label: 'C++ Code',   defaultOn: true,  align: 'center' },
  { key: 'assigned', label: 'Assigned',   defaultOn: true,  sortable: true,  align: 'center' },
  { key: 'updated',  label: 'Updated',    defaultOn: true,  sortable: true  },
]

const STORAGE_KEY = 'archetypeListColumnPrefs_v2'

function loadColumnPrefs() {
  try {
    const raw = localStorage.getItem(STORAGE_KEY)
    if (raw) {
      const prefs = JSON.parse(raw)
      if (prefs.order && prefs.visible) return prefs
    }
  } catch { /* ignore */ }
  return {
    order:   ALL_COLUMNS.map((c) => c.key),
    visible: ALL_COLUMNS.filter((c) => c.defaultOn).map((c) => c.key),
  }
}

function saveColumnPrefs(order, visible) {
  try { localStorage.setItem(STORAGE_KEY, JSON.stringify({ order, visible })) } catch { /* ignore */ }
}

// ─── Assigned Melodies Modal ──────────────────────────────────────────────────

function AssignedMelodiesModal({ data, primaryLang, onClose }) {
  if (!data) return null
  const { archetype, melodyDetails } = data
  return (
    <Modal
      open={Boolean(data)}
      onClose={onClose}
      title={`Melodies using "${archetype.name}"`}
      size="sm"
      footer={<Button variant="secondary" onClick={onClose}>Close</Button>}
    >
      {melodyDetails.length === 0 ? (
        <p style={{ fontSize: 'var(--font-size-sm)', color: 'var(--color-text-muted)', textAlign: 'center', padding: 'var(--space-8) 0' }}>
          No melodies assigned.
        </p>
      ) : (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-2)', maxHeight: '320px', overflowY: 'auto' }}>
          {melodyDetails.map((m) => (
            <Link
              key={m.id}
              to={`/melodies/${m.id}`}
              onClick={onClose}
              style={{
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'space-between',
                padding: 'var(--space-3)',
                borderRadius: 'var(--radius-md)',
                border: '1px solid var(--color-border)',
                backgroundColor: 'var(--color-bg-surface)',
                textDecoration: 'none',
                transition: 'background var(--transition-fast)',
              }}
              onMouseEnter={(e) => e.currentTarget.style.backgroundColor = 'var(--color-bg-elevated)'}
              onMouseLeave={(e) => e.currentTarget.style.backgroundColor = 'var(--color-bg-surface)'}
            >
              <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-text-accent)' }}>View →</span>
            </Link>
          ))}
        </div>
      )}
    </Modal>
  )
}

// ─── ArchetypeList ────────────────────────────────────────────────────────────

export default function ArchetypeList() {
  const navigate = useNavigate()
  const { hasPermission } = useAuth()
  const canEdit = hasPermission('melodies', 'edit')

  // Data
  const [archetypes, setArchetypes]   = useState([])
  const [loading, setLoading]         = useState(true)
  const [error, setError]             = useState('')
  const [primaryLang, setPrimaryLang] = useState('en')

  // Filters / sort / pagination
  const [search, setSearch]     = useState('')
  const [sortKey, setSortKey]   = useState('updated')
  const [sortDir, setSortDir]   = useState('desc')
  const [page, setPage]         = useState(1)
  const [pageSize, setPageSize] = useState(20)

  // Columns
  const [colPrefs, setColPrefs] = useState(loadColumnPrefs)

  // Modals / actions
  const [deleteTarget, setDeleteTarget]       = useState(null)
  const [assignedModal, setAssignedModal]     = useState(null)
  const [codeSnippetTarget, setCodeSnippetTarget] = useState(null)
  const [showUpload, setShowUpload]           = useState(false)
  const [showGenerate, setShowGenerate]       = useState(false)
  const [togglingBuiltin, setTogglingBuiltin] = useState(null)
  const [loadingAssigned, setLoadingAssigned] = useState(false)

  // ── Load ───────────────────────────────────────────────────────────────────
  useEffect(() => {
    api.get('/settings/melody').then((ms) => setPrimaryLang(ms?.primary_language || 'en')).catch(() => {})
    loadArchetypes({ verify: true })
  }, []) // eslint-disable-line react-hooks/exhaustive-deps

  const loadArchetypes = async ({ verify = false } = {}) => {
    setLoading(true)
    setError('')
    try {
      const data = await api.get('/builder/melodies')
      const list = data.melodies || []
      setArchetypes(list)

      if (verify) {
        let didPrune = false
        await Promise.all(list.map(async (archetype) => {
          const ids = archetype.assigned_melody_ids || []
          if (!ids.length) return
          const results = await Promise.allSettled(ids.map((mid) => api.get(`/melodies/${mid}`)))
          const missingIds = results.map((r, i) => (!r.value || r.status === 'rejected' ? ids[i] : null)).filter(Boolean)
          for (const mid of missingIds) {
            try { await api.post(`/builder/melodies/${archetype.id}/unassign?firestore_melody_id=${mid}`); didPrune = true }
            catch { /* best-effort */ }
          }
        }))
        if (didPrune) {
          const refreshed = await api.get('/builder/melodies')
          setArchetypes(refreshed.melodies || [])
        }
      }
    } catch (err) {
      setError(err.message || 'Failed to load archetypes')
    } finally {
      setLoading(false)
    }
  }

  // ── Actions ────────────────────────────────────────────────────────────────
  const handleDelete = async () => {
    if (!deleteTarget) return
    try {
      await api.delete(`/builder/melodies/${deleteTarget.id}`)
      setDeleteTarget(null)
      loadArchetypes()
    } catch (err) {
      setError(err.message)
      setDeleteTarget(null)
    }
  }

  const handleToggleBuiltin = async (e, archetype) => {
    e.stopPropagation()
    if (togglingBuiltin === archetype.id) return
    setTogglingBuiltin(archetype.id)
    try {
      const updated = await api.post(`/builder/melodies/${archetype.id}/toggle-builtin`)
      setArchetypes((prev) => prev.map((a) => a.id === archetype.id ? { ...a, is_builtin: updated.is_builtin } : a))
    } catch (err) {
      setError(err.message)
    } finally {
      setTogglingBuiltin(null)
    }
  }

  const handleShowAssigned = async (e, archetype) => {
    e.stopPropagation()
    const ids = archetype.assigned_melody_ids || []
    if (!ids.length) return
    setLoadingAssigned(true)
    try {
      const results = await Promise.allSettled(ids.map((mid) => api.get(`/melodies/${mid}`)))
      const melodyDetails = results
        .filter((r) => r.status === 'fulfilled' && r.value)
        .map((r) => ({ id: r.value.id, nameRaw: r.value.information?.name }))
      const missingIds = results.map((r, i) => (r.status === 'rejected' || !r.value ? ids[i] : null)).filter(Boolean)
      for (const mid of missingIds) {
        try { await api.post(`/builder/melodies/${archetype.id}/unassign?firestore_melody_id=${mid}`) }
        catch { /* best-effort */ }
      }
      if (missingIds.length) loadArchetypes()
      setAssignedModal({ archetype, melodyDetails })
    } catch (err) {
      setError(err.message)
    } finally {
      setLoadingAssigned(false)
    }
  }

  const handleDownloadBinary = async (e, archetype) => {
    e.stopPropagation()
    const token = localStorage.getItem('access_token')
    try {
      const res = await fetch(`/api/builder/melodies/${archetype.id}/download`, {
        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 = `${archetype.name || archetype.id}.bsm`; a.click()
      URL.revokeObjectURL(url)
    } catch (err) {
      setError(err.message)
    }
  }

  // ── Column management ──────────────────────────────────────────────────────
  const handleColumnChange = (nextVisible) => {
    setColPrefs(() => {
      const next = { order: nextVisible, visible: nextVisible }
      saveColumnPrefs(next.order, next.visible)
      return next
    })
  }

  // ── Sort & filter ──────────────────────────────────────────────────────────
  const handleSort = (key, dir) => { setSortKey(key); setSortDir(dir) }

  const filteredRows = useMemo(() => {
    let rows = archetypes
    if (search) {
      const q = search.toLowerCase()
      rows = rows.filter((a) => (a.name || '').toLowerCase().includes(q) || (a.pid || '').toLowerCase().includes(q))
    }
    return [...rows].sort((a, b) => {
      let av, bv
      switch (sortKey) {
        case 'name':     av = (a.name || '').toLowerCase(); bv = (b.name || '').toLowerCase(); break
        case 'pid':      av = (a.pid  || '').toLowerCase(); bv = (b.pid  || '').toLowerCase(); break
        case 'steps':    av = countSteps(a.steps); bv = countSteps(b.steps); break
        case 'assigned': av = (a.assigned_melody_ids || []).length; bv = (b.assigned_melody_ids || []).length; break
        case 'updated':
        default:         av = a.updated_at || ''; bv = b.updated_at || ''
      }
      if (av < bv) return sortDir === 'asc' ? -1 : 1
      if (av > bv) return sortDir === 'asc' ?  1 : -1
      return 0
    })
  }, [archetypes, search, sortKey, sortDir])

  const total      = filteredRows.length
  const totalPages = Math.max(1, Math.ceil(total / pageSize))
  const safePage   = Math.min(page, totalPages)
  const pagedRows  = filteredRows.slice((safePage - 1) * pageSize, safePage * pageSize)

  // ── Cell renderer ──────────────────────────────────────────────────────────
  const renderCell = (key, row) => {
    switch (key) {
      case 'name':
        return (
          <span style={{ fontWeight: 'var(--font-weight-medium)', color: 'var(--color-text-primary)' }}>
            {row.name || '—'}
          </span>
        )

      case 'pid':
        return (
          <span style={{
            fontFamily: 'var(--font-family-mono)',
            fontSize: 'var(--font-size-sm)',
            padding: '2px 8px',
            borderRadius: 'var(--radius-sm)',
            backgroundColor: 'var(--color-bg-elevated)',
            color: 'var(--color-text-muted)',
          }}>
            {row.pid || '—'}
          </span>
        )

      case 'steps':
        return (
          <span style={{ fontFamily: 'var(--font-family-mono)', fontSize: 'var(--font-size-sm)', color: 'var(--color-text-secondary)' }}>
            {countSteps(row.steps)}
          </span>
        )

      case 'builtin':
        return (
          <div onClick={(e) => e.stopPropagation()}>
            <button
              onClick={(e) => handleToggleBuiltin(e, row)}
              disabled={togglingBuiltin === row.id}
              title={row.is_builtin ? 'Click to unmark as built-in' : 'Click to mark as built-in'}
              style={{
                padding: '2px 10px',
                borderRadius: 'var(--radius-full)',
                fontSize: 'var(--font-size-xs)',
                fontFamily: 'var(--font-family-base)',
                cursor: 'pointer',
                border: row.is_builtin ? '1px solid rgba(139,92,246,0.35)' : '1px solid var(--color-border)',
                backgroundColor: row.is_builtin ? 'rgba(139,92,246,0.12)' : 'var(--color-bg-elevated)',
                color: row.is_builtin ? '#a78bfa' : 'var(--color-text-muted)',
                opacity: togglingBuiltin === row.id ? 0.5 : 1,
                transition: 'opacity var(--transition-fast)',
              }}
            >
              {row.is_builtin ? 'Built-in' : '—'}
            </button>
          </div>
        )

      case 'binary':
        if (!row.binary_path) return <span style={{ color: 'var(--color-text-muted)' }}>—</span>
        return (
          <Button
            variant="success"
            size="sm"
            onClick={(e) => handleDownloadBinary(e, row)}
          >
            Download
          </Button>
        )

      case 'progmem':
        if (!row.progmem_code) return <span style={{ color: 'var(--color-text-muted)' }}>—</span>
        return (
          <Button
            variant="secondary"
            size="sm"
            onClick={(e) => { e.stopPropagation(); setCodeSnippetTarget(row) }}
          >
            View Code
          </Button>
        )

      case 'assigned': {
        const count = (row.assigned_melody_ids || []).length
        if (count === 0) return <span style={{ color: 'var(--color-text-muted)' }}>—</span>
        return (
          <Button
            variant="ghost"
            size="sm"
            loading={loadingAssigned}
            onClick={(e) => handleShowAssigned(e, row)}
          >
            {count} {count === 1 ? 'melody' : 'melodies'}
          </Button>
        )
      }

      case 'updated':
        return (
          <span style={{ fontSize: 'var(--font-size-sm)', color: 'var(--color-text-muted)' }} title={row.updated_at ? fmtDateTimeCentral(row.updated_at) : ''}>
            {formatRelativeTime(row.updated_at)}
          </span>
        )

      default:
        return <span style={{ color: 'var(--color-text-muted)' }}>—</span>
    }
  }

  // ── Table columns ──────────────────────────────────────────────────────────
  const tableColumns = [
    ...colPrefs.visible.map((key) => {
      const col = ALL_COLUMNS.find((c) => c.key === key)
      return col ? { ...col, render: (row) => renderCell(key, row) } : null
    }).filter(Boolean),
    {
      key:    '__actions',
      label:  '',
      width:  '90px',
      align:  'right',
      render: (row) => (
        <RowActions
          actions={[
            {
              label:   'Edit',
              icon:    <Icon name="edit" size={14} />,
              onClick: () => navigate(`/melodies/archetypes/${row.id}`),
            },
            {
              label:   'Load to Composer',
              icon:    <Icon name="settings" size={14} />,
              onClick: () => navigate('/melodies/composer', { state: { archetype: row } }),
            },
            {
              label:   'Download Binary',
              icon:    <Icon name="download" size={14} />,
              onClick: (e) => handleDownloadBinary(e || { stopPropagation: () => {} }, row),
            },
            ...(canEdit ? [
              {
                label:   'Delete',
                icon:    <Icon name="delete" size={14} />,
                color:   'var(--color-danger)',
                onClick: () => setDeleteTarget(row),
                divider: true,
              },
            ] : []),
          ]}
        />
      ),
    },
  ]

  // ─────────────────────────────────────────────────────────────────────────

  return (
    <div className="page-wrapper">
      <PageHeader
        title="Archetype Manager"
        subtitle={search ? `${total} result${total !== 1 ? 's' : ''} of ${archetypes.length}` : `${archetypes.length} archetype${archetypes.length !== 1 ? 's' : ''}`}
      >
        <Button variant="secondary" onClick={() => setShowGenerate(true)}>
          Generate Built-in List
        </Button>
        <Button variant="secondary" onClick={() => setShowUpload(true)}>
          Upload Archetype
        </Button>
        {canEdit && (
          <Button variant="primary" onClick={() => navigate('/melodies/archetypes/new')}>
            <Icon name="add" size={14} />
            New Archetype
          </Button>
        )}
      </PageHeader>

      {/* Filter toolbar */}
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 'var(--space-3)', alignItems: 'center', minWidth: 0 }}>
        <div style={{ flex: '1 1 0', minWidth: '180px' }}>
          <SearchBar
            value={search}
            onChange={setSearch}
            placeholder="Search by name or PID…"
          />
        </div>
        {search && (
          <Button variant="ghost" size="sm" onClick={() => setSearch('')}>
            Clear
          </Button>
        )}
      </div>

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

      {/* Table */}
      <DataTable
        columns={tableColumns}
        data={pagedRows}
        loading={loading}
        keyField="id"
        emptyMessage="No archetypes found"
        emptyDescription={search ? 'Try adjusting your search.' : 'Create your first archetype to get started.'}
        sortKey={sortKey}
        sortDir={sortDir}
        onSort={handleSort}
        onRowClick={(row) => navigate(`/melodies/archetypes/${row.id}`)}
        allColumns={[...ALL_COLUMNS, { key: '__actions', label: '', pickerLabel: 'Actions', alwaysOn: true }]}
        visibleKeys={colPrefs.visible}
        onColumnChange={handleColumnChange}
        footer={
          <Pagination
            page={safePage}
            pageSize={pageSize}
            total={total}
            onPageChange={setPage}
            onSizeChange={(size) => { setPageSize(size); setPage(1) }}
          />
        }
      />

      {/* Modals */}
      <UploadArchetypeModal
        open={showUpload}
        existingArchetypes={archetypes}
        onClose={() => setShowUpload(false)}
        onUploaded={() => loadArchetypes()}
      />

      <GenerateBuiltinModal
        open={showGenerate}
        onClose={() => setShowGenerate(false)}
      />

      <CodeSnippetModal
        archetype={codeSnippetTarget}
        onClose={() => setCodeSnippetTarget(null)}
      />

      <AssignedMelodiesModal
        data={assignedModal}
        primaryLang={primaryLang}
        onClose={() => setAssignedModal(null)}
      />

      <ConfirmDialog
        open={Boolean(deleteTarget)}
        variant="danger"
        title="Delete Archetype"
        message={
          deleteTarget
            ? `"${deleteTarget.name}" will be permanently deleted. Any melodies assigned to this archetype will lose their binary reference.`
            : ''
        }
        confirmLabel="Delete"
        onConfirm={handleDelete}
        onClose={() => setDeleteTarget(null)}
      />
    </div>
  )
}
