const { useEffect, useMemo, useReducer, useState } = React;

const roles = {
  admin: {
    label: 'Admin',
    permissions: ['bulkEdit', 'export', 'saveReports', 'viewDashboard']
  },
  editor: {
    label: 'Editor',
    permissions: ['bulkEdit', 'saveReports', 'viewDashboard']
  },
  viewer: {
    label: 'Viewer',
    permissions: ['viewDashboard']
  }
};

const initialState = {
  rows: [],
  filters: { scenario: 'base', source: '', year: '' },
  selectedIds: [],
  savedReports: [],
  csvPreview: [],
  errors: {},
  viewToken: ''
};

function reducer(state, action) {
  switch (action.type) {
    case 'setRows':
      return { ...state, rows: action.rows };
    case 'updateCell': {
      const { id, field, value, error } = action;
      const rows = state.rows.map((row) =>
        row.id === id ? { ...row, [field]: value } : row
      );
      const errors = { ...state.errors };
      if (error) {
        errors[id] = { ...(errors[id] || {}), [field]: error };
      } else if (errors[id]) {
        const { [field]: _removed, ...rest } = errors[id];
        errors[id] = rest;
        if (!Object.keys(errors[id]).length) delete errors[id];
      }
      return { ...state, rows, errors };
    }
    case 'toggleSelect': {
      const { id } = action;
      const selectedIds = state.selectedIds.includes(id)
        ? state.selectedIds.filter((r) => r !== id)
        : [...state.selectedIds, id];
      return { ...state, selectedIds };
    }
    case 'setFilter': {
      const filters = { ...state.filters, ...action.filters };
      return { ...state, filters };
    }
    case 'bulkApply': {
      const { patch } = action;
      const rows = state.rows.map((row) =>
        state.selectedIds.includes(row.id) ? { ...row, ...patch } : row
      );
      return { ...state, rows };
    }
    case 'setCsvPreview':
      return { ...state, csvPreview: action.rows };
    case 'commitCsvPreview': {
      const nextId =
        state.rows.reduce((max, r) => Math.max(max, Number(r.id) || 0), 0) + 1;
      const mapped = state.csvPreview.map((row, idx) => ({
        ...row,
        id: nextId + idx
      }));
      return { ...state, rows: [...mapped, ...state.rows], csvPreview: [] };
    }
    case 'loadReports':
      return { ...state, savedReports: action.reports };
    case 'saveReport':
      return { ...state, savedReports: action.reports };
    case 'applyReport':
      return { ...state, filters: action.filters };
    case 'setViewToken':
      return { ...state, viewToken: action.token };
    default:
      return state;
  }
}

function useData() {
  const [state, dispatch] = useReducer(reducer, initialState);
  const [role, setRole] = useState('admin');

  useEffect(() => {
    const saved = localStorage.getItem('fpa_reports');
    if (saved) {
      dispatch({ type: 'loadReports', reports: JSON.parse(saved) });
    }
  }, []);

  useEffect(() => {
    const params = new URLSearchParams(window.location.search);
    const scenario = params.get('scenario');
    const source = params.get('source');
    const year = params.get('year');
    if (scenario || source || year) {
      dispatch({ type: 'setFilter', filters: { scenario: scenario || 'base', source: source || '', year: year || '' } });
    }
  }, []);

  useEffect(() => {
    fetch('/api/revenue')
      .then((res) => res.json())
      .then((data) => {
        const rows = (data.entries || []).map((row, idx) => ({
          ...row,
          id: row.id || idx + 1,
          displayMonth: row.month,
          amount: Number(row.amount || 0)
        }));
        if (!rows.length) {
          rows.push(
            { id: 1, source: 'Enterprise', scenario: 'base', type: 'forecast', year: 2025, month: 1, amount: 120000 },
            { id: 2, source: 'SMB', scenario: 'upside', type: 'forecast', year: 2025, month: 2, amount: 98000 },
            { id: 3, source: 'Channel', scenario: 'downside', type: 'actual', year: 2024, month: 12, amount: 64000 }
          );
        }
        dispatch({ type: 'setRows', rows });
      })
      .catch(() => {
        dispatch({
          type: 'setRows',
          rows: [
            { id: 1, source: 'Enterprise', scenario: 'base', type: 'forecast', year: 2025, month: 1, amount: 120000 },
            { id: 2, source: 'SMB', scenario: 'upside', type: 'forecast', year: 2025, month: 2, amount: 98000 },
            { id: 3, source: 'Channel', scenario: 'downside', type: 'actual', year: 2024, month: 12, amount: 64000 }
          ]
        });
      });
  }, []);

  const filteredRows = useMemo(() => {
    return state.rows.filter((row) => {
      if (state.filters.scenario && row.scenario !== state.filters.scenario) return false;
      if (state.filters.source && !row.source.toLowerCase().includes(state.filters.source.toLowerCase())) return false;
      if (state.filters.year && String(row.year) !== String(state.filters.year)) return false;
      return true;
    });
  }, [state.rows, state.filters]);

  return { state, dispatch, filteredRows, role, setRole };
}

const Pill = ({ children }) => <span className="pill">{children}</span>;

function FilterPanel({ filters, onChange, onShare }) {
  return (
    <div className="panel">
      <div className="panel-header">
        <div>
          <p className="eyebrow">Shared views</p>
          <h2 className="panel-title">Filters</h2>
        </div>
        <div className="chip-row">
          <Pill>{filters.scenario || 'all'} scenario</Pill>
          {filters.year && <Pill>{filters.year}</Pill>}
        </div>
      </div>
      <div className="grid three">
        <div className="field">
          <label>Scenario</label>
          <select
            value={filters.scenario}
            className="input-field"
            onChange={(e) => onChange({ scenario: e.target.value })}
          >
            <option value="">All</option>
            <option value="base">Base</option>
            <option value="upside">Upside</option>
            <option value="downside">Downside</option>
          </select>
        </div>
        <div className="field">
          <label>Source</label>
          <input
            className="input-field"
            value={filters.source}
            placeholder="Enterprise, SMB..."
            onChange={(e) => onChange({ source: e.target.value })}
          />
        </div>
        <div className="field">
          <label>Year</label>
          <input
            className="input-field"
            value={filters.year}
            type="number"
            placeholder="2025"
            onChange={(e) => onChange({ year: e.target.value })}
          />
        </div>
      </div>
      <div className="button-row single">
        <button className="btn primary" type="button" onClick={onShare}>
          Share current view
        </button>
      </div>
    </div>
  );
}

function CsvPreview({ preview, onUpload }) {
  const handleFile = (evt) => {
    const file = evt.target.files[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = (e) => {
      const text = e.target.result;
      const [header, ...lines] = text.split(/\r?\n/).filter(Boolean);
      const cols = header.split(',');
      const rows = lines.map((line, idx) => {
        const values = line.split(',');
        return {
          id: `csv-${idx + 1}`,
          source: values[0] || 'Unknown',
          scenario: (values[1] || 'base').toLowerCase(),
          type: (values[2] || 'forecast').toLowerCase(),
          year: Number(values[3]) || new Date().getFullYear(),
          month: Number(values[4]) || 1,
          amount: Number(values[5]) || 0
        };
      });
      onUpload(rows);
    };
    reader.readAsText(file);
  };

  return (
    <div className="panel">
      <div className="panel-header">
        <div>
          <p className="eyebrow">CSV intake</p>
          <h2 className="panel-title">Preview before import</h2>
        </div>
        <input type="file" accept=".csv" onChange={handleFile} />
      </div>
      {preview.length ? (
        <>
          <p className="subhead">{preview.length} rows staged. Validate inline before committing.</p>
          <div className="table-wrapper mini">
            <table>
              <thead>
                <tr>
                  <th>Source</th>
                  <th>Scenario</th>
                  <th>Type</th>
                  <th>Year</th>
                  <th>Month</th>
                  <th>Amount</th>
                </tr>
              </thead>
              <tbody>
                {preview.map((row) => (
                  <tr key={row.id}>
                    <td>{row.source}</td>
                    <td>{row.scenario}</td>
                    <td>{row.type}</td>
                    <td>{row.year}</td>
                    <td>{row.month}</td>
                    <td>{row.amount.toLocaleString()}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
          <button className="btn primary" type="button" onClick={onUpload.bind(null, preview, true)}>
            Commit to grid
          </button>
        </>
      ) : (
        <p className="subhead">Drop a CSV with columns: source, scenario, type, year, month, amount.</p>
      )}
    </div>
  );
}

function BulkEditor({ onApply, disabled }) {
  const [scenario, setScenario] = useState('');
  const [type, setType] = useState('');
  const [amountDelta, setAmountDelta] = useState('');

  const handleApply = () => {
    const patch = {};
    if (scenario) patch.scenario = scenario;
    if (type) patch.type = type;
    if (amountDelta) patch.amountDelta = Number(amountDelta);
    onApply(patch);
    setScenario('');
    setType('');
    setAmountDelta('');
  };

  return (
    <div className="panel">
      <div className="panel-header">
        <div>
          <p className="eyebrow">Bulk edit</p>
          <h2 className="panel-title">Mass-update selection</h2>
        </div>
      </div>
      <div className="grid three">
        <div className="field">
          <label>Scenario override</label>
          <select className="input-field" value={scenario} onChange={(e) => setScenario(e.target.value)}>
            <option value="">Keep existing</option>
            <option value="base">Base</option>
            <option value="upside">Upside</option>
            <option value="downside">Downside</option>
          </select>
        </div>
        <div className="field">
          <label>Type override</label>
          <select className="input-field" value={type} onChange={(e) => setType(e.target.value)}>
            <option value="">Keep existing</option>
            <option value="forecast">Forecast</option>
            <option value="actual">Actual</option>
          </select>
        </div>
        <div className="field">
          <label>Add/subtract</label>
          <input
            className="input-field"
            type="number"
            value={amountDelta}
            placeholder="5000"
            onChange={(e) => setAmountDelta(e.target.value)}
          />
        </div>
      </div>
      <button className="btn primary" type="button" onClick={handleApply} disabled={disabled}>
        Apply to selected rows
      </button>
    </div>
  );
}

function Grid({ rows, selected, errors, onSelect, onChange, disabled }) {
  const handleChange = (row, field, value) => {
    let error = '';
    if (field === 'amount') {
      if (Number.isNaN(Number(value))) {
        error = 'Amount must be numeric';
      } else if (Number(value) < 0) {
        error = 'Amount cannot be negative';
      }
    }
    onChange(row.id, field, field === 'amount' ? Number(value) : value, error);
  };

  return (
    <div className="panel">
      <div className="panel-header">
        <div>
          <p className="eyebrow">Live grid</p>
          <h2 className="panel-title">Inline edits with validation</h2>
        </div>
        <span className="pill">{rows.length} rows</span>
      </div>
      <div className="table-wrapper">
        <table>
          <thead>
            <tr>
              <th></th>
              <th>Source</th>
              <th>Scenario</th>
              <th>Type</th>
              <th>Year</th>
              <th>Month</th>
              <th>Amount</th>
            </tr>
          </thead>
          <tbody>
            {rows.map((row) => {
              const rowErrors = errors[row.id] || {};
              return (
                <tr key={row.id} className={rowErrors.amount ? 'row-error' : ''}>
                  <td>
                    <input
                      type="checkbox"
                      disabled={disabled}
                      checked={selected.includes(row.id)}
                      onChange={() => onSelect(row.id)}
                    />
                  </td>
                  <td>{row.source}</td>
                  <td>
                    <select
                      className="input-field"
                      value={row.scenario}
                      disabled={disabled}
                      onChange={(e) => handleChange(row, 'scenario', e.target.value)}
                    >
                      <option value="base">Base</option>
                      <option value="upside">Upside</option>
                      <option value="downside">Downside</option>
                    </select>
                  </td>
                  <td>
                    <select
                      className="input-field"
                      value={row.type}
                      disabled={disabled}
                      onChange={(e) => handleChange(row, 'type', e.target.value)}
                    >
                      <option value="forecast">Forecast</option>
                      <option value="actual">Actual</option>
                    </select>
                  </td>
                  <td>
                    <input
                      className="input-field"
                      type="number"
                      value={row.year}
                      disabled={disabled}
                      onChange={(e) => handleChange(row, 'year', Number(e.target.value))}
                    />
                  </td>
                  <td>
                    <input
                      className="input-field"
                      type="number"
                      value={row.month}
                      disabled={disabled}
                      onChange={(e) => handleChange(row, 'month', Number(e.target.value))}
                    />
                  </td>
                  <td>
                    <input
                      className="input-field"
                      type="number"
                      value={row.amount}
                      disabled={disabled}
                      onChange={(e) => handleChange(row, 'amount', e.target.value)}
                    />
                    {rowErrors.amount && <div className="inline-error">{rowErrors.amount}</div>}
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    </div>
  );
}

function ExportPanel({ data, disabled }) {
  const exportToExcel = () => {
    const header = ['Source', 'Scenario', 'Type', 'Year', 'Month', 'Amount'];
    const csv = [header.join(',')]
      .concat(
        data.map((row) =>
          [row.source, row.scenario, row.type, row.year, row.month, row.amount].join(',')
        )
      )
      .join('\n');
    const blob = new Blob([csv], { type: 'application/vnd.ms-excel' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'fp&a-export.xlsx';
    a.click();
    URL.revokeObjectURL(url);
  };

  const exportToPdf = () => {
    const popup = window.open('', 'print', 'height=600,width=800');
    popup.document.write('<html><head><title>FP&A Export</title>');
    popup.document.write('<style>table{width:100%;border-collapse:collapse;font-family:sans-serif;}th,td{border:1px solid #ddd;padding:8px;text-align:left;}th{background:#0f172a;color:#fff;}</style>');
    popup.document.write('</head><body>');
    popup.document.write('<h2>Revenue grid</h2>');
    popup.document.write('<table><thead><tr><th>Source</th><th>Scenario</th><th>Type</th><th>Year</th><th>Month</th><th>Amount</th></tr></thead><tbody>');
    data.forEach((row) => {
      popup.document.write(
        `<tr><td>${row.source}</td><td>${row.scenario}</td><td>${row.type}</td><td>${row.year}</td><td>${row.month}</td><td>${row.amount}</td></tr>`
      );
    });
    popup.document.write('</tbody></table>');
    popup.document.write('</body></html>');
    popup.document.close();
    popup.print();
  };

  return (
    <div className="panel">
      <div className="panel-header">
        <div>
          <p className="eyebrow">Exports</p>
          <h2 className="panel-title">Excel / PDF</h2>
        </div>
        <div className="button-row">
          <button className="btn secondary" type="button" onClick={exportToPdf} disabled={disabled}>
            Export PDF
          </button>
          <button className="btn primary" type="button" onClick={exportToExcel} disabled={disabled}>
            Export Excel
          </button>
        </div>
      </div>
      <p className="subhead">Exports honor filters and selections, enabling shareable offline packets.</p>
    </div>
  );
}

function SavedReports({ reports, filters, onSave, onApply, disabled }) {
  const [name, setName] = useState('');

  const handleSave = () => {
    if (!name) return;
    const next = [...reports, { name, filters }];
    localStorage.setItem('fpa_reports', JSON.stringify(next));
    onSave(next);
    setName('');
  };

  return (
    <div className="panel">
      <div className="panel-header">
        <div>
          <p className="eyebrow">Saved reports</p>
          <h2 className="panel-title">Reusable dashboards</h2>
        </div>
      </div>
      <div className="grid two">
        <div className="field">
          <label>Name</label>
          <input
            className="input-field"
            value={name}
            placeholder="Board - Base case"
            onChange={(e) => setName(e.target.value)}
            disabled={disabled}
          />
        </div>
        <div className="field">
          <label>Actions</label>
          <button className="btn primary" type="button" onClick={handleSave} disabled={disabled}>
            Save current filters
          </button>
        </div>
      </div>
      <div className="chip-row">
        {reports.map((report, idx) => (
          <button
            key={idx}
            className="pill clickable"
            type="button"
            onClick={() => onApply(report.filters)}
          >
            {report.name}
          </button>
        ))}
      </div>
    </div>
  );
}

function Dashboard({ data }) {
  const monthly = useMemo(() => {
    const buckets = {};
    data.forEach((row) => {
      const key = `${row.year}-${row.month}`;
      buckets[key] = (buckets[key] || 0) + Number(row.amount || 0);
    });
    return Object.entries(buckets)
      .map(([key, value]) => ({ key, value }))
      .sort((a, b) => (a.key > b.key ? 1 : -1));
  }, [data]);

  const max = Math.max(...monthly.map((m) => m.value), 1);

  return (
    <div className="panel">
      <div className="panel-header">
        <div>
          <p className="eyebrow">Performance</p>
          <h2 className="panel-title">Dashboard</h2>
        </div>
      </div>
      <div className="bar-chart">
        {monthly.map((m) => (
          <div key={m.key} className="bar" title={`${m.key}: ${m.value.toLocaleString()}`}>
            <div className="bar-fill" style={{ height: `${(m.value / max) * 100}%` }} />
            <span className="bar-label">{m.key}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

function PermissionBanner({ role, onChange }) {
  return (
    <div className="panel">
      <div className="panel-header">
        <div>
          <p className="eyebrow">Permissions</p>
          <h2 className="panel-title">Role aware dashboards</h2>
        </div>
        <select className="input-field" value={role} onChange={(e) => onChange(e.target.value)}>
          {Object.entries(roles).map(([key, value]) => (
            <option key={key} value={key}>
              {value.label}
            </option>
          ))}
        </select>
      </div>
      <p className="subhead">
        Feature availability, bulk editing, exports, and saving reports respect the selected role. Use this to
        validate guardrails before sharing links.
      </p>
    </div>
  );
}

function App() {
  const { state, dispatch, filteredRows, role, setRole } = useData();
  const permissions = roles[role].permissions;

  const applyFilter = (filters) => dispatch({ type: 'setFilter', filters });

  const handleShare = () => {
    const params = new URLSearchParams(state.filters).toString();
    const url = `${window.location.origin}${window.location.pathname}?${params}`;
    navigator.clipboard.writeText(url);
    dispatch({ type: 'setViewToken', token: 'View link copied to clipboard' });
    setTimeout(() => dispatch({ type: 'setViewToken', token: '' }), 2400);
  };

  const handleChange = (id, field, value, error) => {
    dispatch({ type: 'updateCell', id, field, value, error });
  };

  const handleBulkApply = (patch) => {
    if (!state.selectedIds.length) return;
    const updates = {};
    if (patch.scenario) updates.scenario = patch.scenario;
    if (patch.type) updates.type = patch.type;
    const rows = filteredRows
      .filter((r) => state.selectedIds.includes(r.id))
      .map((row) => ({ ...row, ...updates, amount: patch.amountDelta ? row.amount + patch.amountDelta : row.amount }));
    const merged = state.rows.map((row) => rows.find((r) => r.id === row.id) || row);
    dispatch({ type: 'setRows', rows: merged });
  };

  const handleCsvUpload = (rows, commit = false) => {
    if (commit) {
      dispatch({ type: 'commitCsvPreview' });
    } else {
      dispatch({ type: 'setCsvPreview', rows });
    }
  };

  const canUse = (feature) => permissions.includes(feature);

  return (
    <div className="shell wide stack">
      <div className="top-row">
        <div>
          <span className="badge">Aurelius FP&A · Componentized SPA</span>
          <h1 className="title">Revenue & Planning Workspace</h1>
          <p className="subhead">
            React-powered single page experience with inline validation, CSV preview, bulk editing, and export-ready
            reporting.
          </p>
        </div>
        <div className="stack" style={{ alignItems: 'flex-end', gap: '8px' }}>
          <Pill>Stateful grids & charts</Pill>
          {state.viewToken && <span className="pill success">{state.viewToken}</span>}
        </div>
      </div>

      <PermissionBanner role={role} onChange={setRole} />

      <div className="layout workspace-layout">
        <div className="layout-main">
          <FilterPanel filters={state.filters} onChange={applyFilter} onShare={handleShare} />
          <Grid
            rows={filteredRows}
            selected={state.selectedIds}
            errors={state.errors}
            onSelect={(id) => dispatch({ type: 'toggleSelect', id })}
            onChange={handleChange}
            disabled={!canUse('bulkEdit')}
          />
          {canUse('bulkEdit') && (
            <BulkEditor onApply={handleBulkApply} disabled={!state.selectedIds.length} />
          )}
          {canUse('export') && <ExportPanel data={filteredRows} disabled={!filteredRows.length} />}
        </div>
        <div className="layout-side">
          <Dashboard data={filteredRows} />
          <CsvPreview preview={state.csvPreview} onUpload={handleCsvUpload} />
          {canUse('saveReports') && (
            <SavedReports
              reports={state.savedReports}
              filters={state.filters}
              onSave={(reports) => dispatch({ type: 'saveReport', reports })}
              onApply={(filters) => dispatch({ type: 'applyReport', filters })}
            />
          )}
        </div>
      </div>
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
