import { h } from 'preact';
import { useState, useEffect } from 'preact/hooks';
import LuaEditor from './LuaEditor';
import SearchableSelect from './SearchableSelect';
import type { InputRef, SignalDef } from './lib/types';

interface Props {
  onClose: () => void;
  onCreated: () => void;
  // Interface id the wizard was opened from — used to bind panel-scoped signals.
  currentIfaceId?: string;
}

interface NodeParam {
  label: string;
  key: string;
  type: 'number' | 'text' | 'lua';
  default: string;
}

const NODE_TYPES: Array<{ type: string; label: string; params: NodeParam[] }> = [
  { type: 'gain',           label: 'Gain',            params: [{ label: 'Factor',           key: 'gain',   type: 'number', default: '1' }] },
  { type: 'offset',         label: 'Offset',          params: [{ label: 'Offset',           key: 'offset', type: 'number', default: '0' }] },
  { type: 'moving_average', label: 'Moving Average',  params: [{ label: 'Window (n)',       key: 'window', type: 'number', default: '10' }] },
  { type: 'rms',            label: 'RMS',             params: [{ label: 'Window (n)',       key: 'window', type: 'number', default: '10' }] },
  { type: 'lowpass',        label: 'Low-pass Filter', params: [
    { label: 'Cutoff freq (Hz)', key: 'freq',  type: 'number', default: '1' },
    { label: 'Order (1–8)',      key: 'order', type: 'number', default: '1' },
  ]},
  { type: 'derivative',     label: 'Derivative',      params: [] },
  { type: 'integrate',      label: 'Integrate',       params: [] },
  { type: 'clamp',          label: 'Clamp',           params: [{ label: 'Min', key: 'min', type: 'number', default: '0' }, { label: 'Max', key: 'max', type: 'number', default: '100' }] },
  { type: 'expr',           label: 'Formula',         params: [{ label: 'Expression (a,b,c,d = inputs)', key: 'expr',   type: 'text', default: 'a * 1.0' }] },
  { type: 'lua',            label: 'Lua Script',      params: [{ label: 'Script (a,b,c,d = inputs)',      key: 'script', type: 'lua',  default: 'return a' }] },
];

interface DataSource {
  name: string;
}

interface SignalInfo {
  name: string;
  description?: string;
}

// ── Main Wizard ──────────────────────────────────────────────────────────────

export default function SyntheticWizard({ onClose, onCreated, currentIfaceId }: Props) {
  const [name, setName] = useState('');
  const [inputs, setInputs] = useState<InputRef[]>([{ ds: 'stub', signal: 'sine_1hz' }]);
  const [nodeType, setNodeType] = useState('gain');
  const [params, setParams] = useState<Record<string, string>>({});
  // Visibility scope. Panel scope requires an interface to bind to, so when the
  // wizard is opened outside a saved panel it falls back to 'user'.
  const [visibility, setVisibility] = useState<'panel' | 'user' | 'global'>(currentIfaceId ? 'panel' : 'user');
  const [unit, setUnit] = useState('');
  const [desc, setDesc] = useState('');
  const [dispLow, setDispLow] = useState('0');
  const [dispHigh, setDispHigh] = useState('100');
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState('');

  // Loaded data
  const [dataSources, setDataSources] = useState<string[]>([]);
  const [dsSignals, setDsSignals] = useState<Record<string, string[]>>({});

  useEffect(() => {
    fetch('/api/v1/datasources')
      .then(r => r.ok ? r.json() : [])
      .then((ds: DataSource[]) => setDataSources(ds.map(d => d.name)))
      .catch(() => {});
  }, []);

  async function loadSignals(ds: string) {
    if (dsSignals[ds]) return;
    try {
      const res = await fetch(`/api/v1/signals?ds=${encodeURIComponent(ds)}`);
      if (!res.ok) return;
      const sigs: SignalInfo[] = await res.json();
      setDsSignals(prev => ({ ...prev, [ds]: sigs.map(s => s.name) }));
    } catch {}
  }

  const nodeDef = NODE_TYPES.find(n => n.type === nodeType)!;

  function getParam(key: string, def: string): string {
    return params[key] ?? def;
  }

  function setParam(key: string, val: string) {
    setParams(p => ({ ...p, [key]: val }));
  }

  function updateInput(idx: number, patch: Partial<InputRef>) {
    setInputs(prev => prev.map((inp, i) => {
      if (i !== idx) return inp;
      const next = { ...inp, ...patch };
      if (patch.ds) {
        next.signal = '';
        loadSignals(patch.ds);
      }
      return next;
    }));
  }

  function addInput() {
    setInputs(prev => [...prev, { ds: dataSources[0] || '', signal: '' }]);
  }

  function removeInput(idx: number) {
    setInputs(prev => prev.filter((_, i) => i !== idx));
  }

  async function handleCreate() {
    if (!name.trim()) { setError('Name is required'); return; }
    if (inputs.some(i => !i.ds || !i.signal)) { setError('All inputs must have a data source and signal'); return; }

    const nodeParams: Record<string, any> = {};
    for (const p of nodeDef.params) {
      const raw = getParam(p.key, p.default);
      nodeParams[p.key] = p.type === 'number' ? parseFloat(raw) : raw;
    }

    const def: SignalDef = {
      name: name.trim(),
      inputs,
      pipeline: [{ type: nodeType, params: nodeParams }],
      meta: {
        unit: unit || undefined,
        description: desc || undefined,
        displayLow: parseFloat(dispLow) || 0,
        displayHigh: parseFloat(dispHigh) || 100,
      },
      visibility,
      ...(visibility === 'panel' && currentIfaceId ? { panel: currentIfaceId } : {}),
    };

    setSaving(true);
    setError('');
    try {
      const res = await fetch('/api/v1/synthetic', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(def),
      });
      if (!res.ok) {
        const j = await res.json().catch(() => ({ error: res.statusText }));
        throw new Error(j.error ?? res.statusText);
      }
      onCreated();
      onClose();
    } catch (err) {
      setError(err instanceof Error ? err.message : String(err));
    } finally {
      setSaving(false);
    }
  }

  return (
    <div class="wizard-backdrop" onClick={onClose}>
      <div class="wizard" onClick={(e) => e.stopPropagation()}>
        <div class="wizard-header">
          <span>New Synthetic Signal</span>
          <button class="icon-btn" onClick={onClose}>✕</button>
        </div>

        <div class="wizard-body">
          {error && <p class="wizard-error">{error}</p>}

          <div class="wizard-section-title">Signal identity</div>
          <div class="wizard-field">
            <label>Name</label>
            <input class="prop-input" value={name}
              placeholder="e.g. smoothed_pressure"
              onInput={(e) => setName((e.target as HTMLInputElement).value)} />
          </div>

          <div class="wizard-field">
            <label>Visibility</label>
            <select class="prop-select" value={visibility}
              onChange={(e) => setVisibility((e.target as HTMLSelectElement).value as any)}>
              <option value="panel" disabled={!currentIfaceId}>
                This panel only{currentIfaceId ? '' : ' (save panel first)'}
              </option>
              <option value="user">All my panels</option>
              <option value="global">All panels (global)</option>
            </select>
          </div>

          <div class="wizard-section-title">Input signals</div>
          {inputs.map((inp, idx) => (
            <div key={idx} class="wizard-field wizard-field-row" style="align-items: flex-end; gap: 0.5rem; margin-bottom: 0.5rem;">
              <div class="wizard-field" style="flex:1;">
                <label>Data source</label>
                <SearchableSelect
                  value={inp.ds}
                  options={dataSources}
                  onSelect={(ds) => updateInput(idx, { ds })}
                />
              </div>
              <div class="wizard-field" style="flex:2;">
                <label>Signal</label>
                <SearchableSelect
                  value={inp.signal}
                  options={dsSignals[inp.ds] || []}
                  onSelect={(signal) => updateInput(idx, { signal })}
                  placeholder={inp.ds ? 'Search signals…' : 'Select data source first'}
                />
              </div>
              <button
                class="icon-btn"
                style="margin-bottom: 0.25rem;"
                title="Remove input"
                onClick={() => removeInput(idx)}
                disabled={inputs.length === 1}
              >✕</button>
            </div>
          ))}
          <button class="panel-btn" style="margin-top: 0.5rem;" onClick={addInput}>+ Add input</button>

          <div class="wizard-section-title" style="margin-top: 1.5rem;">Processing</div>
          <div class="wizard-field">
            <label>Node type</label>
            <select class="prop-select" value={nodeType}
              onChange={(e) => { setNodeType((e.target as HTMLSelectElement).value); setParams({}); }}>
              {NODE_TYPES.map(n => <option key={n.type} value={n.type}>{n.label}</option>)}
            </select>
          </div>
          {nodeDef.params.map(p => (
            <div key={p.key} class="wizard-field">
              <label>{p.label}</label>
              {p.type === 'lua' ? (
                <LuaEditor
                  value={getParam(p.key, p.default)}
                  onChange={(v) => setParam(p.key, v)}
                />
              ) : (
                <input class="prop-input" type={p.type === 'number' ? 'number' : 'text'}
                  value={getParam(p.key, p.default)}
                  onInput={(e) => setParam(p.key, (e.target as HTMLInputElement).value)} />
              )}
            </div>
          ))}

          <div class="wizard-section-title">Metadata (optional)</div>
          <div class="wizard-field wizard-field-row">
            <div class="wizard-field">
              <label>Unit</label>
              <input class="prop-input" value={unit} placeholder="m/s"
                onInput={(e) => setUnit((e.target as HTMLInputElement).value)} />
            </div>
            <div class="wizard-field">
              <label>Display low</label>
              <input class="prop-input" type="number" value={dispLow}
                onInput={(e) => setDispLow((e.target as HTMLInputElement).value)} />
            </div>
            <div class="wizard-field">
              <label>Display high</label>
              <input class="prop-input" type="number" value={dispHigh}
                onInput={(e) => setDispHigh((e.target as HTMLInputElement).value)} />
            </div>
          </div>
          <div class="wizard-field">
            <label>Description</label>
            <input class="prop-input" value={desc} placeholder="Optional description"
              onInput={(e) => setDesc((e.target as HTMLInputElement).value)} />
          </div>
        </div>

        <div class="wizard-footer">
          <button class="toolbar-btn" onClick={onClose}>Cancel</button>
          <button class="toolbar-btn toolbar-btn-primary" onClick={handleCreate} disabled={saving}>
            {saving ? 'Creating…' : 'Create Signal'}
          </button>
        </div>
      </div>
    </div>
  );
}
