import { h, Fragment } from 'preact';
import { useState, useEffect } from 'preact/hooks';
import { getSignalStore, getMetaStore } from '../lib/stores';
import { wsClient } from '../lib/ws';
import { useAuth, canWrite } from '../lib/auth';
import type { Widget, SignalValue, SignalMeta } from '../lib/types';

const DEFAULT_VALUE: SignalValue = { value: null, quality: 'unknown', ts: null };

function qualityColor(q: string): string {
  switch (q) {
    case 'good': return '#4ade80';
    case 'uncertain': return '#fbbf24';
    case 'bad': return '#f87171';
    default: return '#6b7280';
  }
}

interface Props { widget: Widget; onContextMenu?: (e: MouseEvent) => void; }

export default function SetValue({ widget, onContextMenu }: Props) {
  const sigRef = widget.signals[0];
  const me = useAuth();
  const [sv, setSv] = useState<SignalValue>(DEFAULT_VALUE);
  const [meta, setMeta] = useState<SignalMeta | null>(null);
  const [inputValue, setInputValue] = useState('');

  useEffect(() => {
    if (!sigRef) return;
    const unsubV = getSignalStore(sigRef).subscribe(setSv);
    const unsubM = getMetaStore(sigRef).subscribe(setMeta);
    return () => { unsubV(); unsubM(); };
  }, [sigRef?.ds, sigRef?.name]);

  const label = widget.options['label'] || sigRef?.name || '';
  const rawUnit = widget.options['unit'];
  const unit = rawUnit === 'none' ? '' : (rawUnit || meta?.unit || '');
  const confirm = widget.options['confirm'] === 'true';
  const isNumeric = meta ? meta.type !== 'string' : true;
  const isEnum = !!(meta?.enumStrings && meta.enumStrings.length > 0);
  const quality = sv.quality;

  // The control is read-only when the current user cannot write this signal:
  // either their global access is read-only, or the signal's metadata reports
  // it is not writable. Panel-local vars are written client-side (no backend
  // meta) so they stay editable. meta.writable === false only once known, so
  // the control isn't prematurely disabled while metadata is still loading.
  const isLocal = sigRef?.ds === 'local';
  const writeAllowed = isLocal || (canWrite(me.level) && meta?.writable !== false);

  function displayValue(): string {
    const v = sv.value;
    if (v === null || v === undefined) return '---';
    if (isEnum && meta?.enumStrings) {
      const idx = typeof v === 'number' ? Math.round(v) : parseInt(String(v), 10);
      return meta.enumStrings[idx] ?? String(v);
    }
    if (typeof v === 'number') {
      return Number.isFinite(v) ? v.toPrecision(4).replace(/\.?0+$/, '') : String(v);
    }
    return String(v);
  }

  function doWrite(raw: string) {
    if (!sigRef || !writeAllowed || !raw.trim()) return;
    const val = isNumeric ? parseFloat(raw) : raw;
    wsClient.write(sigRef, val);
    setInputValue('');
  }

  function handleSet() {
    if (!sigRef) return;
    const raw = inputValue.trim();
    if (!raw) return;
    if (confirm) {
      const display = isEnum && meta?.enumStrings
        ? meta.enumStrings[parseInt(raw, 10)] ?? raw
        : raw;
      if (window.confirm(`Set ${label} to ${display}?`)) doWrite(raw);
    } else {
      doWrite(raw);
    }
  }

  function handleKeydown(e: KeyboardEvent) {
    if (e.key === 'Enter') handleSet();
  }

  const currentEnumIdx = isEnum && sv.value !== null
    ? Math.round(typeof sv.value === 'number' ? sv.value : parseFloat(String(sv.value)))
    : -1;

  // Tracks the user's selection in the dropdown before they click Set
  const [enumSelected, setEnumSelected] = useState(currentEnumIdx >= 0 ? String(currentEnumIdx) : '0');

  function handleEnumSet() {
    if (!sigRef || !writeAllowed) return;
    const doEnumWrite = () => { wsClient.write(sigRef, parseFloat(enumSelected)); };
    if (confirm) {
      const display = meta?.enumStrings?.[parseInt(enumSelected, 10)] ?? enumSelected;
      if (window.confirm(`Set ${label} to ${display}?`)) doEnumWrite();
    } else {
      doEnumWrite();
    }
  }

  return (
    <div
      class="setvalue"
      style={`left:${widget.x}px;top:${widget.y}px;width:${widget.w}px;height:${widget.h}px;`}
      onContextMenu={onContextMenu}
    >
      <span class="quality-dot" style={`background:${qualityColor(quality)};`} title={`Quality: ${quality}`} />
      <span class="sv-label">{label}:</span>
      {isEnum ? (
        <>
          <select
            class="sv-input sv-select"
            value={enumSelected}
            disabled={!writeAllowed}
            onChange={(e: Event) => setEnumSelected((e.target as HTMLSelectElement).value)}
          >
            {meta!.enumStrings!.map((s, i) => (
              <option key={i} value={String(i)}>{s}</option>
            ))}
          </select>
          <button class="sv-btn" onClick={handleEnumSet} disabled={!writeAllowed}
            title={writeAllowed ? '' : 'You do not have permission to write this signal'}>Set</button>
        </>
      ) : (
        <>
          <input
            class="sv-input"
            type={isNumeric ? 'number' : 'text'}
            value={inputValue}
            disabled={!writeAllowed}
            onInput={(e: Event) => setInputValue((e.target as HTMLInputElement).value)}
            onKeyDown={handleKeydown}
            placeholder="value"
          />
          <span class="sv-current">{displayValue()}</span>
          {unit && <span class="sv-unit">{unit}</span>}
          <button class="sv-btn" onClick={handleSet} disabled={!writeAllowed}
            title={writeAllowed ? '' : 'You do not have permission to write this signal'}>Set</button>
        </>
      )}
    </div>
  );
}
