/* global React, Icon, IconBtn, db, relTime, PinButton */
/* Collection detail view + card form/modal.
 * Detail shows a dense sortable/searchable table of cards in the collection,
 * with bulk-select + study/import/edit-schema actions in the header.
 */
const { useState, useEffect, useMemo, useCallback, useRef } = React;

// =====================================================================
// Collection detail
// =====================================================================
function FlashcardDetail({ collection, cards, prefs, onBack, onEditSchema, onImport, onStudy }) {
  const { isDue, fieldPreview, dueStats } = window.flashcards;
  const [q, setQ] = useState('');
  const [filter, setFilter] = useState('all'); // all | due | new | learning
  const [sort, setSort] = useState('updated'); // updated | due | prompt
  const [selected, setSelected] = useState(new Set());
  const [editing, setEditing] = useState(null); // card | { __new: true } | null

  const stats = useMemo(() => dueStats(cards), [cards]);
  const promptF = collection.schema.find(f => f.id === collection.promptField);

  const visible = useMemo(() => {
    let list = cards;
    if (filter === 'due') list = list.filter(c => isDue(c));
    if (filter === 'new') list = list.filter(c => !c.fsrs || c.fsrs.state === 'new');
    if (filter === 'learning') list = list.filter(c => c.fsrs?.state === 'learning' || c.fsrs?.state === 'relearning');
    if (q.trim()) {
      const needle = q.trim().toLowerCase();
      list = list.filter(c => {
        for (const f of collection.schema) {
          const v = c.fields?.[f.id];
          if (v == null) continue;
          const text = Array.isArray(v) ? v.join(' ') : String(v);
          if (text.toLowerCase().includes(needle)) return true;
        }
        return false;
      });
    }
    if (sort === 'updated') list = [...list].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
    if (sort === 'due') list = [...list].sort((a, b) => {
      const ad = a.fsrs?.due ?? Infinity;
      const bd = b.fsrs?.due ?? Infinity;
      return ad - bd;
    });
    if (sort === 'prompt') {
      const pf = collection.promptField;
      list = [...list].sort((a, b) => String(a.fields?.[pf] || '').localeCompare(String(b.fields?.[pf] || '')));
    }
    return list;
  }, [cards, q, filter, sort, collection]);

  const allChecked = visible.length > 0 && visible.every(c => selected.has(c.id));
  const toggleAll = () => {
    if (allChecked) {
      const next = new Set(selected);
      visible.forEach(c => next.delete(c.id));
      setSelected(next);
    } else {
      const next = new Set(selected);
      visible.forEach(c => next.add(c.id));
      setSelected(next);
    }
  };
  const toggleOne = (id) => {
    setSelected(prev => {
      const next = new Set(prev);
      if (next.has(id)) next.delete(id);
      else next.add(id);
      return next;
    });
  };

  const bulkDelete = async () => {
    if (selected.size === 0) return;
    const ok = await window.lhDialog.confirm({
      title: `Delete ${selected.size} card${selected.size === 1 ? '' : 's'}?`,
      message: 'They will be permanently removed.',
      confirmLabel: 'Delete',
      danger: true,
      icon: 'delete',
    });
    if (!ok) return;
    for (const id of selected) await db.flashcardCards.delete(id);
    setSelected(new Set());
  };

  const bulkResetSrs = async () => {
    if (selected.size === 0) return;
    const ok = await window.lhDialog.confirm({
      title: `Reset SRS state for ${selected.size} card${selected.size === 1 ? '' : 's'}?`,
      message: 'Cards will be treated as brand-new on next review. Their content is not affected.',
      confirmLabel: 'Reset',
      icon: 'restart_alt',
    });
    if (!ok) return;
    for (const id of selected) {
      const c = cards.find(x => x.id === id);
      if (!c) continue;
      await db.flashcardCards.put({
        ...c,
        fsrs: { state: 'new', stability: 0, difficulty: 0, due: null, lastReview: null, reps: 0, lapses: 0 },
      });
    }
    setSelected(new Set());
  };

  return (
    <div className="fc-detail">
      <button className="fc-detail-back" onClick={onBack}>
        <Icon name="arrow_back" />All collections
      </button>

      <div className="fc-detail-head">
        <div style={{ display: 'flex', alignItems: 'flex-start', gap: 14 }}>
          <div className="fc-cc-icon" style={{ width: 44, height: 44 }}><Icon name="style" /></div>
          <div>
            <h2>{collection.name}</h2>
            <div className="fc-detail-sub">
              {(collection.srcLang || collection.tgtLang) && (
                <span>{collection.srcLang || '?'} → {collection.tgtLang || '?'}</span>
              )}
              <span>·</span>
              <span><b style={{ color: 'var(--text-hi)' }}>{stats.total}</b> cards</span>
              <span>·</span>
              <span style={{ color: stats.due > 0 ? 'var(--color-primary)' : 'inherit' }}>
                <b style={{ color: stats.due > 0 ? 'var(--color-primary)' : 'var(--text-hi)' }}>{stats.due}</b> due
              </span>
              {collection.lastStudiedAt && (
                <>
                  <span>·</span>
                  <span>Studied {relTime(collection.lastStudiedAt)}</span>
                </>
              )}
              {collection.archived && (
                <>
                  <span>·</span>
                  <span style={{ color: '#fcd34d' }}>Archived</span>
                </>
              )}
            </div>
            {collection.description && (
              <div style={{ marginTop: 6, fontSize: 12, color: 'var(--text-md)', maxWidth: 640 }}>
                {collection.description}
              </div>
            )}
          </div>
        </div>

        <div className="fc-detail-actions">
          <button className="lh-btn ghost" onClick={onEditSchema}>
            <Icon name="tune" />Schema
          </button>
          <button className="lh-btn ghost" onClick={onImport}>
            <Icon name="upload_file" />Import
          </button>
          <button className="lh-btn ghost" onClick={() => setEditing({ __new: true })}>
            <Icon name="add" />Add card
          </button>
          <button className="lh-btn primary" disabled={stats.total === 0} onClick={onStudy}>
            <Icon name="play_arrow" />
            {stats.due > 0 ? `Study ${stats.due}` : 'Study all'}
          </button>
        </div>
      </div>

      {/* Filter bar */}
      <div className="fc-detail-bar">
        <div className="fc-search">
          <Icon name="search" />
          <input
            placeholder="Search cards…"
            value={q}
            onChange={e => setQ(e.target.value)}
          />
        </div>
        <select className="fc-select" style={{ width: 130 }} value={filter} onChange={e => setFilter(e.target.value)}>
          <option value="all">All cards</option>
          <option value="due">Due only</option>
          <option value="new">New only</option>
          <option value="learning">Learning</option>
        </select>
        <select className="fc-select" style={{ width: 130 }} value={sort} onChange={e => setSort(e.target.value)}>
          <option value="updated">Most recent</option>
          <option value="due">Next due</option>
          <option value="prompt">Prompt A–Z</option>
        </select>
        {selected.size > 0 && (
          <div className="fc-bulk">
            <span><b>{selected.size}</b> selected</span>
            <button className="lh-btn ghost" onClick={() => setSelected(new Set())}>
              <Icon name="close" />Clear
            </button>
            <button className="lh-btn ghost" onClick={bulkResetSrs}>
              <Icon name="restart_alt" />Reset SRS
            </button>
            <button className="lh-btn danger" onClick={bulkDelete}>
              <Icon name="delete" />Delete
            </button>
          </div>
        )}
      </div>

      {/* Table */}
      {cards.length === 0 ? (
        <div className="fc-empty">
          <div className="icon-wrap"><Icon name="layers_clear" /></div>
          <h3>This collection is empty</h3>
          <p>Add your first card by hand, or bulk-import a CSV / Excel file you already have.</p>
          <div className="row">
            <button className="lh-btn primary" onClick={() => setEditing({ __new: true })}>
              <Icon name="add" />Add card
            </button>
            <button className="lh-btn ghost" onClick={onImport}>
              <Icon name="upload_file" />Import file
            </button>
          </div>
        </div>
      ) : visible.length === 0 ? (
        <div className="fc-empty">
          <div className="icon-wrap"><Icon name="search_off" /></div>
          <h3>No matching cards</h3>
          <p>Try a different search term or change the filter.</p>
        </div>
      ) : (
        <div className="fc-table-wrap">
          <table className={`fc-table ${prefs.cardListDensity === 'compact' ? 'is-compact' : ''}`}>
            <thead>
              <tr>
                <th className="fc-table-check">
                  <input type="checkbox" checked={allChecked} onChange={toggleAll} />
                </th>
                <th>Prompt ({promptF?.name || '?'})</th>
                {collection.schema.filter(f => f.id !== collection.promptField).slice(0, 3).map(f => (
                  <th key={f.id}>{f.name}</th>
                ))}
                <th style={{ width: 110 }}>Status</th>
                <th style={{ width: 100 }}>Updated</th>
                <th style={{ width: 60 }}></th>
              </tr>
            </thead>
            <tbody>
              {visible.map(card => (
                <CardRow
                  key={card.id}
                  card={card}
                  collection={collection}
                  selected={selected.has(card.id)}
                  onToggle={() => toggleOne(card.id)}
                  onEdit={() => setEditing(card)}
                  fieldPreview={fieldPreview}
                />
              ))}
            </tbody>
          </table>
        </div>
      )}

      {editing && (
        <CardEditor
          card={editing.__new ? null : editing}
          collection={collection}
          existingCards={cards}
          onClose={() => setEditing(null)}
          onSaved={(card, addAnother) => {
            if (addAnother) setEditing({ __new: true });
            else setEditing(null);
          }}
        />
      )}
    </div>
  );
}

window.FlashcardDetail = FlashcardDetail;

function CardRow({ card, collection, selected, onToggle, onEdit, fieldPreview }) {
  const { isDue } = window.flashcards;
  const due = isDue(card);
  const otherFields = collection.schema.filter(f => f.id !== collection.promptField).slice(0, 3);
  const promptVal = card.fields?.[collection.promptField];
  const promptType = collection.schema.find(f => f.id === collection.promptField)?.type;
  const state = card.fsrs?.state || 'new';

  let statusEl;
  if (state === 'new') statusEl = <span className="fc-due-pill is-new">New</span>;
  else if (due) statusEl = <span className="fc-due-pill">Due now</span>;
  else if (card.fsrs?.due) {
    const ms = card.fsrs.due - Date.now();
    const days = ms / 86400000;
    if (days < 1) statusEl = <span className="fc-due-pill is-soon">Today</span>;
    else if (days < 3) statusEl = <span className="fc-due-pill is-soon">In {Math.round(days)}d</span>;
    else statusEl = <span className="fc-due-pill is-later">In {Math.round(days)}d</span>;
  } else {
    statusEl = <span className="fc-due-pill is-later">—</span>;
  }

  return (
    <tr
      className={`${selected ? 'is-selected' : ''} ${due ? 'is-due' : ''}`}
      onClick={(e) => {
        if (e.target.tagName === 'INPUT' || e.target.closest('button')) return;
        onEdit();
      }}
      style={{ cursor: 'pointer' }}
    >
      <td className="fc-table-check" onClick={e => e.stopPropagation()}>
        <input type="checkbox" checked={selected} onChange={onToggle} />
      </td>
      <td className="fc-table-prompt">{fieldPreview(promptVal, promptType) || <span style={{ color: 'var(--text-faint)' }}>—</span>}</td>
      {otherFields.map(f => {
        const v = card.fields?.[f.id];
        return (
          <td key={f.id}>
            {v == null || v === '' ? <span style={{ color: 'var(--text-faint)' }}>—</span> : fieldPreview(v, f.type)}
          </td>
        );
      })}
      <td>{statusEl}</td>
      <td style={{ color: 'var(--text-muted)', fontSize: 11 }}>{relTime(card.updatedAt)}</td>
      <td className="fc-table-row-actions">
        <IconBtn name="edit" title="Edit" onClick={(e) => { e.stopPropagation(); onEdit(); }} />
      </td>
    </tr>
  );
}

// =====================================================================
// Card editor (modal)
// =====================================================================
function CardEditor({ card, collection, existingCards, onClose, onSaved }) {
  const isEdit = !!card;
  const [fields, setFields] = useState(() => {
    const init = {};
    for (const f of collection.schema) {
      if (card?.fields && card.fields[f.id] != null) init[f.id] = card.fields[f.id];
      else if (f.type === 'tags') init[f.id] = [];
      else init[f.id] = '';
    }
    return init;
  });
  const [saving, setSaving] = useState(false);

  const update = (id, value) => setFields(prev => ({ ...prev, [id]: value }));

  const errors = useMemo(() => {
    const out = [];
    for (const f of collection.schema) {
      if (f.required) {
        const v = fields[f.id];
        const empty = v == null || v === '' || (Array.isArray(v) && v.length === 0);
        if (empty) out.push(`"${f.name}" is required`);
      }
    }
    return out;
  }, [fields, collection.schema]);

  const promptVal = fields[collection.promptField];
  const dupCount = useMemo(() => {
    if (!promptVal) return 0;
    return existingCards.filter(c =>
      c.id !== card?.id &&
      String(c.fields?.[collection.promptField] || '').trim().toLowerCase() ===
      String(promptVal || '').trim().toLowerCase()
    ).length;
  }, [promptVal, existingCards, card, collection.promptField]);

  const save = async (addAnother) => {
    if (errors.length > 0) return;
    setSaving(true);
    const saved = await db.flashcardCards.put({
      ...(card || {}),
      collectionId: collection.id,
      fields,
      fsrs: card?.fsrs || { state: 'new', stability: 0, difficulty: 0, due: null, lastReview: null, reps: 0, lapses: 0 },
    });
    setSaving(false);
    onSaved(saved, !!addAnother);
  };

  const remove = async () => {
    if (!isEdit) return;
    const ok = await window.lhDialog.confirm({
      title: 'Delete this card?',
      message: 'It will be permanently removed.',
      confirmLabel: 'Delete',
      danger: true,
      icon: 'delete',
    });
    if (!ok) return;
    await db.flashcardCards.delete(card.id);
    onClose();
  };

  // Submit on Cmd/Ctrl + Enter
  useEffect(() => {
    const onKey = (e) => {
      if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') { e.preventDefault(); save(false); }
      if (e.key === 'Escape') onClose();
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [fields]);

  return (
    <div className="fc-modal-overlay" onClick={onClose}>
      <div className="fc-modal" onClick={e => e.stopPropagation()}>
        <div className="fc-modal-head">
          <Icon name={isEdit ? 'edit' : 'add'} />
          <h3>{isEdit ? 'Edit card' : 'New card'}</h3>
          <IconBtn name="close" onClick={onClose} />
        </div>
        <div className="fc-modal-body">
          <div className="fc-card-form">
            {collection.schema.map(f => (
              <FieldInput key={f.id} field={f} value={fields[f.id]} onChange={(v) => update(f.id, v)} />
            ))}
            {dupCount > 0 && (
              <div className="fc-callout is-warn">
                <Icon name="warning" />
                <span>Another card in this collection already has this prompt.</span>
              </div>
            )}
            {errors.length > 0 && (
              <div className="fc-callout is-warn">
                <Icon name="warning" />
                <ul>{errors.map((e, i) => <li key={i}>{e}</li>)}</ul>
              </div>
            )}
          </div>
        </div>
        <div className="fc-modal-foot">
          <div>
            {isEdit && (
              <button className="lh-btn danger" onClick={remove}>
                <Icon name="delete" />Delete
              </button>
            )}
          </div>
          <div style={{ display: 'flex', gap: 8 }}>
            <button className="lh-btn ghost" onClick={onClose}>Cancel</button>
            {!isEdit && (
              <button className="lh-btn ghost" disabled={errors.length > 0 || saving} onClick={() => save(true)} title="Save and immediately start a new card">
                <Icon name="add" />Save · add another
              </button>
            )}
            <button className="lh-btn primary" disabled={errors.length > 0 || saving} onClick={() => save(false)}>
              <Icon name="check" />Save
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}

// =====================================================================
// FieldInput — renders the right control per field type
// =====================================================================
function FieldInput({ field, value, onChange }) {
  return (
    <div className="fc-card-field">
      <div className="fc-card-field-head">
        <Icon name={window.flashcards.FIELD_TYPE_BY_ID[field.type]?.icon || 'short_text'} />
        <label className="fc-label" style={{ margin: 0 }}>{field.name}</label>
        {field.required && <span className="fc-field-required">*</span>}
      </div>
      {renderControl(field, value, onChange)}
      {field.hint && <div className="fc-card-field-hint">{field.hint}</div>}
    </div>
  );
}

function renderControl(field, value, onChange) {
  switch (field.type) {
    case 'long':   return <textarea className="fc-textarea" value={value || ''} onChange={e => onChange(e.target.value)} maxLength={2000} />;
    case 'tags':   return <TagsInput value={value || []} onChange={onChange} />;
    case 'number': return <input className="fc-input" type="number" value={value ?? ''} onChange={e => onChange(e.target.value === '' ? '' : Number(e.target.value))} />;
    case 'date':   return <input className="fc-input" type="date" value={fmtDateInput(value)} onChange={e => onChange(e.target.value ? new Date(e.target.value).getTime() : '')} />;
    case 'select':
      return (
        <select className="fc-select" value={value || ''} onChange={e => onChange(e.target.value)}>
          <option value="">— pick one —</option>
          {(field.options || []).map(o => <option key={o} value={o}>{o}</option>)}
        </select>
      );
    case 'image': return <ImageDrop value={value} onChange={onChange} />;
    case 'audio': return <AudioInput value={value} onChange={onChange} />;
    case 'text':
    default:      return <input className="fc-input" value={value || ''} onChange={e => onChange(e.target.value)} maxLength={400} />;
  }
}

function fmtDateInput(v) {
  if (!v) return '';
  const n = Number(v);
  if (!Number.isFinite(n)) return '';
  const d = new Date(n);
  if (isNaN(d.getTime())) return '';
  const m = String(d.getMonth() + 1).padStart(2, '0');
  const day = String(d.getDate()).padStart(2, '0');
  return `${d.getFullYear()}-${m}-${day}`;
}

function TagsInput({ value, onChange }) {
  const [draft, setDraft] = useState('');
  const list = Array.isArray(value) ? value : [];
  const add = () => {
    const v = draft.trim();
    if (!v) return;
    if (list.includes(v)) { setDraft(''); return; }
    onChange([...list, v]);
    setDraft('');
  };
  return (
    <div className="fc-tags-input" onClick={e => { if (e.target === e.currentTarget) e.currentTarget.querySelector('input')?.focus(); }}>
      {list.map((t, i) => (
        <span key={i} className="fc-tag-chip">
          {t}
          <button onClick={() => onChange(list.filter((_, j) => j !== i))}><Icon name="close" /></button>
        </span>
      ))}
      <input
        value={draft}
        onChange={e => setDraft(e.target.value)}
        placeholder={list.length ? 'Add tag…' : 'Type and press Enter'}
        onKeyDown={e => {
          if (e.key === 'Enter') { e.preventDefault(); add(); }
          else if (e.key === ',') { e.preventDefault(); add(); }
          else if (e.key === 'Backspace' && !draft && list.length) onChange(list.slice(0, -1));
        }}
      />
    </div>
  );
}

function ImageDrop({ value, onChange }) {
  const [over, setOver] = useState(false);
  const [urlDraft, setUrlDraft] = useState('');
  const inputRef = useRef(null);
  const onFile = (file) => {
    if (!file || !file.type.startsWith('image/')) return;
    const reader = new FileReader();
    reader.onload = () => onChange(reader.result);
    reader.readAsDataURL(file);
  };
  const submitUrl = () => {
    const v = urlDraft.trim();
    if (!v) return;
    onChange(v);
    setUrlDraft('');
  };
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
      <div
        className={`fc-image-drop ${over ? 'is-over' : ''}`}
        onClick={() => !value && inputRef.current?.click()}
        onDragOver={e => { e.preventDefault(); setOver(true); }}
        onDragLeave={() => setOver(false)}
        onDrop={e => { e.preventDefault(); setOver(false); onFile(e.dataTransfer.files?.[0]); }}
      >
        {value ? (
          <>
            <img src={value} alt="" />
            <div style={{ display: 'flex', gap: 6 }}>
              <button className="lh-btn ghost" onClick={(e) => { e.stopPropagation(); inputRef.current?.click(); }}>
                <Icon name="swap_horiz" />Replace
              </button>
              <button className="lh-btn ghost" onClick={(e) => { e.stopPropagation(); onChange(''); }}>
                <Icon name="close" />Remove
              </button>
            </div>
          </>
        ) : (
          <>
            <Icon name="image" />
            <div>Drop an image here, or click to choose a file</div>
          </>
        )}
        <input
          ref={inputRef}
          type="file"
          accept="image/*"
          hidden
          onChange={e => onFile(e.target.files?.[0])}
        />
      </div>
      {!value && (
        <div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
          <Icon name="link" style={{ fontSize: 14, color: 'var(--text-faint)' }} />
          <input
            className="fc-input"
            placeholder="…or paste an image URL"
            value={urlDraft}
            onChange={e => setUrlDraft(e.target.value)}
            onKeyDown={e => { if (e.key === 'Enter') { e.preventDefault(); submitUrl(); } }}
            style={{ flex: 1, fontSize: 12 }}
          />
          <button className="lh-btn ghost" disabled={!urlDraft.trim()} onClick={submitUrl}>
            Use
          </button>
        </div>
      )}
    </div>
  );
}

function AudioInput({ value, onChange }) {
  const inputRef = useRef(null);
  const [recording, setRecording] = useState(false);
  const recorderRef = useRef(null);
  const chunksRef = useRef([]);

  const onFile = (file) => {
    if (!file || !file.type.startsWith('audio/')) return;
    const reader = new FileReader();
    reader.onload = () => onChange(reader.result);
    reader.readAsDataURL(file);
  };

  const startRec = async () => {
    if (!navigator.mediaDevices?.getUserMedia) {
      await window.lhDialog.confirm({ title: 'Mic unavailable', message: 'Your browser does not allow microphone access here. Upload an audio file instead.', confirmLabel: 'OK' });
      return;
    }
    try {
      const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
      const mr = new MediaRecorder(stream);
      chunksRef.current = [];
      mr.ondataavailable = e => { if (e.data.size) chunksRef.current.push(e.data); };
      mr.onstop = () => {
        const blob = new Blob(chunksRef.current, { type: 'audio/webm' });
        const reader = new FileReader();
        reader.onload = () => onChange(reader.result);
        reader.readAsDataURL(blob);
        stream.getTracks().forEach(t => t.stop());
      };
      mr.start();
      recorderRef.current = mr;
      setRecording(true);
    } catch (e) {
      // user denied or no mic
    }
  };
  const stopRec = () => {
    recorderRef.current?.stop();
    setRecording(false);
  };

  return (
    <div className="fc-media-preview">
      {value ? (
        <>
          <audio controls src={value} />
          <button className="lh-btn ghost" onClick={() => onChange('')}>
            <Icon name="close" />Remove
          </button>
        </>
      ) : (
        <>
          {recording ? (
            <button className="lh-btn danger" onClick={stopRec}>
              <Icon name="stop_circle" />Stop recording
            </button>
          ) : (
            <button className="lh-btn ghost" onClick={startRec}>
              <Icon name="mic" />Record
            </button>
          )}
          <button className="lh-btn ghost" onClick={() => inputRef.current?.click()}>
            <Icon name="upload_file" />Upload
          </button>
          <input
            ref={inputRef}
            type="file"
            accept="audio/*"
            hidden
            onChange={e => onFile(e.target.files?.[0])}
          />
        </>
      )}
    </div>
  );
}

window.FlashcardFieldInput = FieldInput; // exported for import preview/edit
