import { useState, useCallback, useMemo } from 'react';
import { useAppState, useAppDispatch } from '../context/AppContext.jsx';
import Header from '../components/Header.jsx';
import Loader from '../components/Loader.jsx';
import ColFilterDropdown from '../components/ColFilterDropdown.jsx';
import { showWarnToast, showInfoToast } from '../components/Toast.jsx';
import { parseCSVWithProgress, fmtSize, pKey } from '../utils/csvParser.js';
import { parsePastXLSX } from '../utils/pastResultParser.js';

const UploadIcon = () => (
  <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round">
    <rect x="3" y="2" width="12" height="16" rx="2"/><path d="M6 7h8M6 10.5h8M6 14h5"/>
  </svg>
);
const PanelIcon = () => (
  <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round">
    <rect x="2" y="4" width="16" height="12" rx="2"/><path d="M2 8h16M7 8v8M13 8v8"/>
  </svg>
);
const DownloadIcon = () => (
  <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round">
    <path d="M3 14v2a1 1 0 001 1h12a1 1 0 001-1v-2M10 3v9M7 9l3 3 3-3"/>
  </svg>
);

function FileCard({ id, label, color, hint, accept, file, onFile, onRemove, children }) {
  const [drag, setDrag] = useState(false);
  const [err, setErr] = useState(false);

  function handleFile(f) {
    setErr(false);
    if (id === 'r' && !f.name.match(/\.xlsx$/i)) { setErr(true); return; }
    if (id !== 'r' && !f.name.toLowerCase().endsWith('.csv')) { setErr(true); return; }
    onFile(f);
  }

  return (
    <div className="card">
      <div className="card-hd">
        <div className="card-hd-t"><span className="dot" style={{ background: color }} />{label}</div>
        <span className={`tag ${id === 'r' ? 'tag-gy' : 'tag-r'}`}>{id === 'r' ? 'Optional' : 'Required'}</span>
      </div>
      <div className="up-item">
        {!file ? (
          <div
            className={`dz${drag ? ' drag' : ''}`}
            onDragOver={e => { e.preventDefault(); setDrag(true); }}
            onDragLeave={() => setDrag(false)}
            onDrop={e => { e.preventDefault(); setDrag(false); const f = e.dataTransfer.files[0]; if (f) handleFile(f); }}
            onClick={() => document.getElementById(`inp-${id}`).click()}
          >
            <input
              type="file" id={`inp-${id}`} accept={accept} style={{ display: 'none' }}
              onChange={e => { const f = e.target.files[0]; if (f) handleFile(f); e.target.value = ''; }}
            />
            <div className="dz-ico">
              {id === 'd' ? <UploadIcon /> : id === 'p' ? <PanelIcon /> : <DownloadIcon />}
            </div>
            <div className="dz-main">Drop {id === 'r' ? 'Excel' : 'CSV'} or browse</div>
            <div className="dz-sub">{hint}</div>
          </div>
        ) : (
          <div className="file-chip">
            <svg width="11" height="11" viewBox="0 0 12 12" fill="none" stroke={id === 'r' ? 'var(--or)' : 'var(--g)'} strokeWidth="2.2" strokeLinecap="round"><path d="M1 6l4 4 6-6"/></svg>
            <span className="fc-name">{file === true ? '(from past result)' : file.name}</span>
            <span className="fc-size">{file === true ? '' : fmtSize(file.size)}</span>
            <button className="fc-rm" onClick={onRemove}>×</button>
          </div>
        )}
        {err && <div className="file-err">Only {id === 'r' ? '.xlsx' : '.csv'} accepted</div>}
        {children}
      </div>
    </div>
  );
}

export default function Step1Setup({ onNext, onNav }) {
  const state = useAppState();
  const dispatch = useAppDispatch();
  const { files, dictCSV, panelCSV, selectedPanels, p1ColFilters, fromPastResult } = state;

  const [loading, setLoading] = useState(false);
  const [loadMsg, setLoadMsg] = useState('');
  const [loadPct, setLoadPct] = useState(null);
  const [restoreMsg, setRestoreMsg] = useState(null);
  const [ddCol, setDdCol] = useState(null);
  const [ddAnchor, setDdAnchor] = useState(null);
  const [panelSearch, setPanelSearch] = useState('');

  // cascading filter options for P1
  function cascadeVals(col) {
    if (!panelCSV) return [];
    const others = Object.entries(p1ColFilters).filter(([c]) => c !== col);
    return [...new Set(
      panelCSV.rows
        .filter(row => others.every(([c, vals]) => !vals || !vals.size || vals.has((row[c] || '').trim())))
        .map(row => (row[col] || '').trim())
        .filter(Boolean)
    )].sort();
  }

  function panelRowPasses(row) {
    for (const [col, vals] of Object.entries(p1ColFilters)) {
      if (vals && vals.size && !vals.has((row[col] || '').trim())) return false;
    }
    if (panelSearch) {
      const q = panelSearch.toLowerCase();
      if (!panelCSV.headers.some(h => String(row[h] || '').toLowerCase().includes(q))) return false;
    }
    return true;
  }

  const filteredRows = useMemo(() => {
    if (!panelCSV) return [];
    return panelCSV.rows.filter(panelRowPasses);
  }, [panelCSV, p1ColFilters, panelSearch]);

  async function handleDictFile(file) {
    setLoadMsg('Reading ' + file.name + '…'); setLoading(true); setLoadPct(0);
    const parsed = await parseCSVWithProgress(await file.text(), (pct) => setLoadPct(pct));
    setLoading(false);
    // Validate
    const required = ['VARIABLE','PRIMARY_METRICS','SN_ACTIVITY_LEVEL_1'];
    const missing = required.filter(c => !parsed.headers.includes(c));
    if (missing.length) showWarnToast('Dictionary missing columns: ' + missing.join(', '));
    dispatch({ type: 'SET_DICT_CSV', payload: parsed });
    dispatch({ type: 'SET_FILE', key: 'd', value: file });
    // Apply restored vars if pending
    if (state._rVars) {
      const rows = parsed.rows.map(r => ({ ...r, _sel: state._rVars.has((r.VARIABLE || '').trim()) }));
      const token = rows.length + '|' + parsed.headers.join(',');
      dispatch({ type: 'SET_ALL_ROWS', payload: rows, token });
      dispatch({ type: 'SET_RESTORE_BUFFERS', payload: { _rVars: null } });
    }
  }

  async function handlePanelFile(file) {
    setLoadMsg('Reading ' + file.name + '…'); setLoading(true); setLoadPct(0);
    const parsed = await parseCSVWithProgress(await file.text(), (pct) => setLoadPct(pct));
    setLoading(false);

    let newSelected = new Set();
    if (state._rPanels && state._rPanels.length) {
      const pHeaders = parsed.headers;
      const firstCol = pHeaders[0] || '';
      parsed.rows.forEach(row => {
        const k = pKey(row);
        const bestScore = state._rPanels.reduce((best, rp) => {
          const rv = (row[firstCol] || '').trim().toLowerCase();
          const rpv = (rp[firstCol] || '').toLowerCase();
          if (firstCol && rpv && rv !== rpv) return best;
          const nonEmpty = pHeaders.filter(h => rp[h]);
          if (!nonEmpty.length) return best;
          const matches = nonEmpty.filter(h => (row[h] || '').trim().toLowerCase() === rp[h].toLowerCase());
          return Math.max(best, matches.length / nonEmpty.length);
        }, 0);
        if (bestScore >= 0.5) newSelected.add(k);
      });
      if (newSelected.size === 0) showWarnToast('No panels matched past result — check your Panel CSV.');
      dispatch({ type: 'SET_RESTORE_BUFFERS', payload: { _rPanels: null } });
    }

    dispatch({ type: 'SET_PANEL_CSV', payload: parsed });
    dispatch({ type: 'SET_SELECTED_PANELS', payload: newSelected });
    dispatch({ type: 'SET_P1_COL_FILTERS', payload: {} });
    dispatch({ type: 'SET_FILE', key: 'p', value: file });
  }

  async function handleResultFile(file) {
    setLoadMsg('Reading past result…'); setLoading(true);
    const ab = await file.arrayBuffer();
    setLoading(false);
    const result = parsePastXLSX(ab);
    if (!result) { showWarnToast('Failed to read past result file.'); return; }

    dispatch({ type: 'SET_FILE', key: 'r', value: file });
    const buffers = {};
    if (result.panels) buffers._rPanels = result.panels;
    if (result.vars) buffers._rVars = result.vars;
    if (result.contributions) buffers._rC = result.contributions;
    if (result.mapping) buffers._rMap = result.mapping;
    if (result.fixedVars) buffers._rFix = result.fixedVars;
    dispatch({ type: 'SET_RESTORE_BUFFERS', payload: buffers });

    if (result.config) dispatch({ type: 'SET_CONFIG', payload: result.config });

    if (result.synthPanelCSV) {
      dispatch({ type: 'SET_PANEL_CSV', payload: result.synthPanelCSV });
      dispatch({ type: 'SET_SELECTED_PANELS', payload: new Set(result.synthPanelCSV.rows.map(r => String(r.__id))) });
      dispatch({ type: 'SET_FILE', key: 'p', value: true });
      dispatch({ type: 'SET_FROM_PAST_RESULT', payload: true });
    }

    if (result.synthDictCSV) {
      dispatch({ type: 'SET_DICT_CSV', payload: result.synthDictCSV });
      const token = result.synthDictCSV.rows.length + '|' + result.synthDictCSV.headers.join(',');
      dispatch({ type: 'SET_ALL_ROWS', payload: result.synthDictCSV.rows, token });
      dispatch({ type: 'SET_FILE', key: 'd', value: true });
      // _sel flags already applied in synthDictCSV rows — clear _rVars buffer to avoid double-apply
      dispatch({ type: 'SET_RESTORE_BUFFERS', payload: { _rVars: null } });
    }

    const ok = !!(result.panels || result.vars);
    const hasBoth = !!(result.synthPanelCSV && result.synthDictCSV);
    setRestoreMsg({ ok, text: ok
      ? hasBoth
        ? '✓ All data restored — proceed directly or upload new files to override'
        : '✓ Pre-fill ready — upload Dictionary & Panel List CSVs to apply'
      : '⚠ Could not read past result'
    });
    if (ok) showInfoToast('Past result loaded');
  }

  function removeFile(id) {
    dispatch({ type: 'SET_FILE', key: id, value: null });
    if (id === 'd') dispatch({ type: 'SET_DICT_CSV', payload: null });
    if (id === 'p') {
      dispatch({ type: 'SET_PANEL_CSV', payload: null });
      dispatch({ type: 'SET_SELECTED_PANELS', payload: new Set() });
      dispatch({ type: 'SET_P1_COL_FILTERS', payload: {} });
    }
    if (id === 'r') {
      setRestoreMsg(null);
      if (fromPastResult) {
        dispatch({ type: 'SET_FROM_PAST_RESULT', payload: false });
        dispatch({ type: 'SET_PANEL_CSV', payload: null });
        dispatch({ type: 'SET_DICT_CSV', payload: null });
        dispatch({ type: 'SET_SELECTED_PANELS', payload: new Set() });
        dispatch({ type: 'SET_P1_COL_FILTERS', payload: {} });
        dispatch({ type: 'SET_FILE', key: 'd', value: null });
        dispatch({ type: 'SET_FILE', key: 'p', value: null });
        dispatch({ type: 'SET_RESTORE_BUFFERS', payload: { _rPanels: null, _rVars: null, _rC: null, _rMap: null, _rFix: null } });
      }
    }
  }

  function togglePanel(k) {
    const next = new Set(selectedPanels);
    if (next.has(k)) next.delete(k); else next.add(k);
    dispatch({ type: 'SET_SELECTED_PANELS', payload: next });
  }

  function selAll(on) {
    const next = new Set(selectedPanels);
    filteredRows.forEach(r => { const k = pKey(r); if (on) next.add(k); else next.delete(k); });
    dispatch({ type: 'SET_SELECTED_PANELS', payload: next });
  }

  function toggleColFilter(col, val, on) {
    const next = { ...p1ColFilters };
    if (!next[col]) next[col] = new Set();
    else next[col] = new Set(next[col]);
    if (on) next[col].add(val); else next[col].delete(val);
    if (!next[col].size) delete next[col];
    dispatch({ type: 'SET_P1_COL_FILTERS', payload: next });
  }

  function clearFilters() {
    dispatch({ type: 'SET_P1_COL_FILTERS', payload: {} });
    setPanelSearch('');
  }

  const canProceed = files.d && files.p && selectedPanels.size > 0;
  const hasFilters = Object.values(p1ColFilters).some(v => v && v.size > 0) || !!panelSearch;
  const headers = panelCSV?.headers || [];
  const allVisChecked = filteredRows.length > 0 && filteredRows.every(r => selectedPanels.has(pKey(r)));
  const anyVisChecked = filteredRows.some(r => selectedPanels.has(pKey(r)));

  return (
    <div className="page" id="p1">
      {loading && <Loader message={loadMsg} progress={loadPct} />}
      {ddCol && (
        <ColFilterDropdown
          col={ddCol}
          values={cascadeVals(ddCol)}
          selected={p1ColFilters[ddCol] || new Set()}
          onToggle={(v, on) => toggleColFilter(ddCol, v, on)}
          onClose={() => { setDdCol(null); setDdAnchor(null); }}
          anchorRect={ddAnchor}
        />
      )}

      <Header step={1} onNav={onNav} sub="Model Config" />

      <div className="body">
        {/* LEFT: uploads */}
        <div className="upload-panel">
          <FileCard id="d" label="Dictionary" color="var(--g)" hint="Variable definitions" accept=".csv"
            file={files.d} onFile={handleDictFile} onRemove={() => removeFile('d')} />
          <FileCard id="p" label="Panel List" color="var(--blue)" hint="Panel dataset rows" accept=".csv"
            file={files.p} onFile={handlePanelFile} onRemove={() => removeFile('p')} />
          <FileCard id="r" label="Past Result" color="var(--or)" hint="Restore previous run" accept=".xlsx"
            file={files.r} onFile={handleResultFile} onRemove={() => removeFile('r')}>
            {restoreMsg && (
              <div className="restore-ok" style={{ color: restoreMsg.ok ? 'var(--or)' : 'var(--red)' }}>
                {restoreMsg.text}
              </div>
            )}
          </FileCard>
        </div>

        {/* RIGHT: panel selection */}
        <div className="panel-panel card">
          <div className="card-hd">
            <div className="card-hd-t"><span className="dot" style={{ background: 'var(--teal)' }} />Panel Selection</div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
              <div style={{ position: 'relative', display: 'flex', alignItems: 'center' }}>
                <svg style={{ position: 'absolute', left: 7, width: 10, height: 10, color: 'var(--mt)', pointerEvents: 'none' }} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="6.5" cy="6.5" r="4.5"/><path d="M10.5 10.5l3 3"/></svg>
                <input className="inp" placeholder="Search panels…" value={panelSearch}
                  onChange={e => setPanelSearch(e.target.value)}
                  style={{ paddingLeft: 22, width: 160, fontSize: 11 }} />
              </div>
              <button className="btn btn-sm btn-sec" onClick={() => selAll(true)}>All</button>
              <button className="btn btn-sm btn-sec" onClick={() => selAll(false)}>None</button>
              {hasFilters && (
                <button className="btn btn-sm btn-sec" onClick={clearFilters}>Clear filters</button>
              )}
            </div>
          </div>
          <div className="panel-sel-bar">
            <span style={{ fontSize: 10.5, color: 'var(--mt)' }}>
              <strong style={{ color: 'var(--g)' }}>{selectedPanels.size}</strong> selected &nbsp;·&nbsp;
              <span>{panelCSV ? panelCSV.rows.length : 0} total</span>
            </span>
            <span className={`tag ${selectedPanels.size > 0 ? 'tag-g' : 'tag-gy'}`}>
              {selectedPanels.size > 0 ? `${selectedPanels.size} ready` : 'Select ≥ 1'}
            </span>
          </div>
          <div className="panel-tbl-wrap">
            {!panelCSV ? (
              <div className="empty-tbl">Upload a Panel List CSV to see rows here</div>
            ) : (
              <table className="ptbl">
                <thead>
                  <tr>
                    <th style={{ width: 32, minWidth: 32 }}>
                      <div className="pth-inner" style={{ cursor: 'default', justifyContent: 'center' }}>
                        <input type="checkbox" className="pcb"
                          checked={allVisChecked}
                          ref={el => { if (el) el.indeterminate = !allVisChecked && anyVisChecked; }}
                          onChange={e => selAll(e.target.checked)} />
                      </div>
                    </th>
                    {headers.map(h => (
                      <th key={h}>
                        <div
                          className={`pth-inner${p1ColFilters[h] && p1ColFilters[h].size ? ' filtered' : ''}`}
                          onClick={e => { setDdCol(h); setDdAnchor(e.currentTarget.getBoundingClientRect()); }}
                        >
                          <span className="pth-lbl" title={h}>{h}</span>
                          <svg style={{ width: 8, height: 8, flexShrink: 0, opacity: p1ColFilters[h]?.size ? 1 : 0.45 }} viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.8"><path d="M1 3h10M3 6h6M5 9h2"/></svg>
                          {p1ColFilters[h]?.size > 0 && (
                            <span style={{ background: 'var(--g)', color: '#fff', borderRadius: 10, fontSize: 8, fontWeight: 700, padding: '0 4px' }}>
                              {p1ColFilters[h].size}
                            </span>
                          )}
                        </div>
                      </th>
                    ))}
                  </tr>
                </thead>
                <tbody>
                  {filteredRows.map(row => {
                    const k = pKey(row);
                    return (
                      <tr key={k} className={selectedPanels.has(k) ? 'sel' : ''} onClick={() => togglePanel(k)}>
                        <td><input type="checkbox" className="pcb" checked={selectedPanels.has(k)} onChange={() => togglePanel(k)} onClick={e => e.stopPropagation()} /></td>
                        {headers.map(h => <td key={h} title={row[h]}>{row[h]}</td>)}
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            )}
          </div>
        </div>
      </div>

      <div className="nav-foot">
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <span className="nav-foot-info">
            {!files.d || !files.p ? 'Upload required files to continue'
              : !selectedPanels.size ? 'Select at least 1 panel'
              : <><strong>{selectedPanels.size} panel(s)</strong> selected — ready</>}
          </span>
          <button className="btn btn-sm btn-sec btn-danger" onClick={() => dispatch({ type: 'RESET_ALL' })}>
            Reset All
          </button>
        </div>
        <button className="btn btn-pri" disabled={!canProceed} onClick={onNext}>
          Proceed to Variables
          <svg width="10" height="10" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="2.3" strokeLinecap="round"><path d="M2 7h10M8 3l4 4-4 4"/></svg>
        </button>
      </div>
    </div>
  );
}
