import { h } from 'preact';
import { useState, useEffect } from 'preact/hooks';
import { getSignalStore, getMetaStore } from '../lib/stores';
import { formatValue } from '../lib/format';
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';
  }
}

// Arc: 135° (lower-left, 7 o'clock = min) → 45° (lower-right, 5 o'clock = max),
// sweeping clockwise through the top (12 o'clock). 270° total sweep.
const START_DEG = 135;
const END_DEG = 45;
const TOTAL_DEG = 270;
const cx = 50, cy = 50, r = 38;

function degToRad(deg: number) { return (deg * Math.PI) / 180; }
function polarToXY(deg: number, radius: number) {
  const rad = degToRad(deg);
  return { x: cx + radius * Math.cos(rad), y: cy + radius * Math.sin(rad) };
}
function arcPath(startDeg: number, endDeg: number, radius: number): string {
  const s = polarToXY(startDeg, radius);
  const e = polarToXY(endDeg, radius);
  // Compute the clockwise angular span so wrap-around works correctly.
  const span = ((endDeg - startDeg) % 360 + 360) % 360;
  const largeArc = span > 180 ? 1 : 0;
  return `M ${s.x} ${s.y} A ${radius} ${radius} 0 ${largeArc} 1 ${e.x} ${e.y}`;
}
function valueToDeg(v: number, minVal: number, maxVal: number): number {
  const clamped = Math.max(minVal, Math.min(maxVal, v));
  const frac = maxVal === minVal ? 0 : (clamped - minVal) / (maxVal - minVal);
  return START_DEG + frac * TOTAL_DEG;
}

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

export default function Gauge({ widget, onContextMenu }: Props) {
  const sigRef = widget.signals[0];
  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 rawUnit = widget.options['unit'];
  const unit = rawUnit === 'none' ? '' : (rawUnit || meta?.unit || '');
  const minVal = parseFloat(widget.options['min'] ?? String(meta?.displayLow ?? 0));
  const maxVal = parseFloat(widget.options['max'] ?? String(meta?.displayHigh ?? 100));
  const thresholdLow = widget.options['thresholdLow'] ? parseFloat(widget.options['thresholdLow']) : null;
  const thresholdHigh = widget.options['thresholdHigh'] ? parseFloat(widget.options['thresholdHigh']) : null;

  const quality = sv.quality;
  const fmt = widget.options['format'] ?? '';
  const rawV = sv.value;
  const rawValue: number | null = rawV === null || rawV === undefined ? null
    : typeof rawV === 'number' ? rawV : parseFloat(String(rawV));

  function displayValue(): string {
    if (rawValue === null || isNaN(rawValue)) return '---';
    return formatValue(rawValue, fmt);
  }

  function fillColor(): string {
    if (rawValue === null) return '#4a9eff';
    if (thresholdHigh !== null && rawValue >= thresholdHigh) return '#ef4444';
    if (thresholdLow !== null && rawValue <= thresholdLow) return '#f59e0b';
    return '#4a9eff';
  }

  const needleDeg = rawValue === null ? START_DEG : valueToDeg(rawValue, minVal, maxVal);
  const bgPath = arcPath(START_DEG, END_DEG, r);
  const fillPath = needleDeg > START_DEG ? arcPath(START_DEG, needleDeg, r) : '';
  const needlePos = polarToXY(needleDeg, r - 4);
  const fc = fillColor();

  return (
    <div
      class="gauge"
      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}`} />
      <svg viewBox="0 0 100 90" class="gauge-svg">
        <path d={bgPath} fill="none" stroke="#2d3748" stroke-width="6" stroke-linecap="round" />
        {fillPath && (
          <path d={fillPath} fill="none" stroke={fc} stroke-width="6" stroke-linecap="round" />
        )}
        <circle cx={needlePos.x} cy={needlePos.y} r="3" fill={fc} />
        <text x={cx} y={cy + 6} text-anchor="middle" class="gauge-value">{displayValue()}</text>
        {unit && <text x={cx} y={cy + 17} text-anchor="middle" class="gauge-unit">{unit}</text>}
        <text x="14" y="80" text-anchor="middle" class="gauge-range">{minVal}</text>
        <text x="86" y="80" text-anchor="middle" class="gauge-range">{maxVal}</text>
      </svg>
      {label && <div class="gauge-label">{label}</div>}
    </div>
  );
}
