/**
 * SensorControlPanel — wokwi-style interactive sensor controls.
 *
 * Appears at the top-left of the simulation canvas when a sensor component is
 * clicked during simulation.  Provides sliders and buttons that feed values
 * directly into the running simulation via SensorUpdateRegistry.
 */

import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
  SENSOR_CONTROLS,
  type SensorControl,
  type SliderControl,
} from '../../simulation/sensorControlConfig';
import { dispatchSensorUpdate, getLastSensorValues } from '../../simulation/SensorUpdateRegistry';
import './SensorControlPanel.css';

interface SensorControlPanelProps {
  componentId: string;
  metadataId: string;
  sensorName: string;
  onClose: () => void;
}

// ── Section grouping for MPU6050 ────────────────────────────────────────────

interface SensorSection {
  label: string;
  icon: string;
  keys: string[];
}

const MPU6050_SECTIONS: SensorSection[] = [
  { label: 'Acceleration', icon: '↗', keys: ['accelX', 'accelY', 'accelZ'] },
  { label: 'Rotation', icon: '↻', keys: ['gyroX', 'gyroY', 'gyroZ'] },
  { label: 'Temperature', icon: '🌡', keys: ['temp'] },
];

// Keys that use single-char axis labels (X / Y / Z) rather than the full key name
const AXIS_KEYS = new Set(['accelX', 'accelY', 'accelZ', 'gyroX', 'gyroY', 'gyroZ']);

// ── Component ───────────────────────────────────────────────────────────────

export const SensorControlPanel: React.FC<SensorControlPanelProps> = ({
  componentId,
  metadataId,
  sensorName,
  onClose,
}) => {
  const { t } = useTranslation();
  const def = SENSOR_CONTROLS[metadataId];

  // Local slider/button state — hydrated from the registry's last-known
  // values for this componentId (so reopening a sensor or switching between
  // two sensors of the same type shows each one's current state, not the
  // previous panel's). Falls back to config defaults the first time a
  // sensor is opened.
  const [values, setValues] = useState<Record<string, number | boolean>>(() => {
    const cached = getLastSensorValues(componentId);
    if (cached) return { ...(def?.defaultValues ?? {}), ...cached };
    return def ? { ...def.defaultValues } : {};
  });

  // Push defaults into simulation on first open for this sensor. Skipped
  // when the sensor already has cached values — the simulation still holds
  // them, no need to clobber.
  useEffect(() => {
    if (def && Object.keys(def.defaultValues).length > 0 && !getLastSensorValues(componentId)) {
      dispatchSensorUpdate(componentId, def.defaultValues);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [componentId]);

  // Close on Escape
  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      if (e.key === 'Escape') onClose();
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [onClose]);

  if (!def) return null;

  // ── Handlers ──────────────────────────────────────────────────────────────

  const handleSlider = (key: string, raw: string) => {
    const v = parseFloat(raw);
    setValues((prev) => ({ ...prev, [key]: v }));
    dispatchSensorUpdate(componentId, { [key]: v });
  };

  const handleButton = (key: string) => {
    dispatchSensorUpdate(componentId, { [key]: true });
  };

  // ── Render helpers ────────────────────────────────────────────────────────

  const renderControl = (ctrl: SensorControl) => {
    if (ctrl.type === 'button') {
      return (
        <button
          key={ctrl.key}
          className="sensor-trigger-button"
          onClick={() => handleButton(ctrl.key)}
        >
          {ctrl.label}
        </button>
      );
    }

    // Slider
    const sc = ctrl as SliderControl;
    const val = (values[sc.key] as number) ?? sc.defaultValue;
    const displayVal = sc.formatValue ? sc.formatValue(val) : String(val);
    const isAxisKey = AXIS_KEYS.has(sc.key);

    return (
      <div key={sc.key} className="sensor-control-row">
        <span className={isAxisKey ? 'sensor-control-label' : 'sensor-control-label-wide'}>
          {isAxisKey ? sc.label : sc.label}
        </span>
        <input
          type="range"
          className="sensor-slider"
          min={sc.min}
          max={sc.max}
          step={sc.step}
          value={val}
          onChange={(e) => handleSlider(sc.key, e.target.value)}
        />
        <span className="sensor-value-display">
          {displayVal}
          {sc.unit ? ` ${sc.unit}` : ''}
        </span>
      </div>
    );
  };

  // For MPU6050 render sections; for everything else render controls flat
  const renderControls = () => {
    if (metadataId === 'mpu6050') {
      return MPU6050_SECTIONS.map((section) => {
        const sectionControls = def.controls.filter((c) => section.keys.includes(c.key));
        return (
          <React.Fragment key={section.label}>
            <div className="sensor-section-label">
              <span className="sensor-section-icon">{section.icon}</span>
              {section.label}
            </div>
            {sectionControls.map(renderControl)}
          </React.Fragment>
        );
      });
    }
    return def.controls.map(renderControl);
  };

  return (
    <div
      className="sensor-control-panel"
      onClick={(e) => e.stopPropagation()}
      // The canvas treats left mousedown on empty space as a pan gesture.
      // Without stopping mousedown here, dragging the lux/temp/etc. slider
      // thumb pans the canvas instead of moving the slider.
      onMouseDown={(e) => e.stopPropagation()}
      onPointerDown={(e) => e.stopPropagation()}
    >
      <div className="sensor-panel-header">
        <span className="sensor-panel-title">{sensorName || def.title}</span>
        <button className="sensor-panel-close" onClick={onClose} title={t('editor.sensorPanel.close')}>
          ×
        </button>
      </div>
      {renderControls()}
    </div>
  );
};
