import { h } 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 };

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

// Compare a live signal value against a configured on/off target. Numeric when
// both parse as finite numbers (tolerant compare), otherwise string equality.
function matches(value: unknown, target: string): boolean {
  if (value === null || value === undefined) return false;
  const vn = typeof value === 'number' ? value : parseFloat(String(value));
  const tn = parseFloat(target);
  if (Number.isFinite(vn) && Number.isFinite(tn)) return Math.abs(vn - tn) < 1e-9;
  return String(value) === target;
}

export default function Toggle({ 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);

  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 onValue = widget.options['onValue'] ?? '1';
  const offValue = widget.options['offValue'] ?? '0';
  const onLabel = widget.options['onLabel'] || 'ON';
  const offLabel = widget.options['offLabel'] || 'OFF';
  const confirm = widget.options['confirm'] === 'true';

  const isLocal = sigRef?.ds === 'local';
  const writeAllowed = isLocal || (canWrite(me.level) && meta?.writable !== false);

  // "On" when the value matches the configured on-target; unknown until a value
  // arrives. A value matching neither target is treated as off.
  const known = sv.value !== null && sv.value !== undefined;
  const isOn = known && matches(sv.value, onValue);

  function toggle() {
    if (!sigRef || !writeAllowed) return;
    const target = isOn ? offValue : onValue;
    const numeric = Number.isFinite(parseFloat(target)) && meta?.type !== 'string';
    const write = () => wsClient.write(sigRef, numeric ? parseFloat(target) : target);
    if (confirm) {
      if (window.confirm(`Set ${label} to ${isOn ? offLabel : onLabel}?`)) write();
    } else {
      write();
    }
  }

  return (
    <div
      class="toggle-widget"
      style={`left:${widget.x}px;top:${widget.y}px;width:${widget.w}px;height:${widget.h}px;`}
      onContextMenu={onContextMenu}
    >
      {label && <span class="toggle-label">{label}</span>}
      <button
        type="button"
        class={`toggle-switch${isOn ? ' toggle-on' : ''}${!known ? ' toggle-unknown' : ''}`}
        disabled={!writeAllowed}
        onClick={toggle}
        title={writeAllowed ? '' : 'You do not have permission to write this signal'}
      >
        <span class="toggle-track"><span class="toggle-knob" /></span>
        <span class="toggle-state">{!known ? '—' : isOn ? onLabel : offLabel}</span>
      </button>
    </div>
  );
}
