/**
 * Registry for the Robot configurator. Each template maps to Blockly block
 * types/fields in esp32_blocks.js (keep keys in sync with block definitions).
 */

/** @typedef {{ key: string, label: string, type: 'number', min?: number, max?: number, step?: number, default: number } | { key: string, label: string, type: 'dropdown', options: [string, string][], default: string }} RobotField */

/**
 * @typedef {{
 *   id: string,
 *   label: string,
 *   blockType: string,
 *   applyAs: 'statement' | 'sonar_reporter',
 *   fields: RobotField[],
 * }} RobotTemplate
 */

/** @type {RobotTemplate[]} */
export const ROBOT_TEMPLATE_LIST = [
  {
    id: 'hbridge_dual',
    label: 'Dual H-bridge (init)',
    blockType: 'hbridge_dual_init',
    applyAs: 'statement',
    fields: [
      { key: 'L_IN1', label: 'Left IN1', type: 'number', min: 0, max: 48, default: 2 },
      { key: 'L_IN2', label: 'Left IN2', type: 'number', min: 0, max: 48, default: 3 },
      { key: 'R_IN1', label: 'Right IN1', type: 'number', min: 0, max: 48, default: 4 },
      { key: 'R_IN2', label: 'Right IN2', type: 'number', min: 0, max: 48, default: 5 },
    ],
  },
  {
    id: 'hbridge_motor',
    label: 'Single H-bridge motor (init)',
    blockType: 'hbridge_motor_init',
    applyAs: 'statement',
    fields: [
      {
        key: 'MOTOR',
        label: 'Motor',
        type: 'dropdown',
        options: [
          ['A', 'A'],
          ['B', 'B'],
        ],
        default: 'A',
      },
      { key: 'IN1', label: 'IN1 pin', type: 'number', min: 0, max: 48, default: 2 },
      { key: 'IN2', label: 'IN2 pin', type: 'number', min: 0, max: 48, default: 3 },
    ],
  },
  {
    id: 'neopixel',
    label: 'NeoPixel strip (init)',
    blockType: 'neopixel_init',
    applyAs: 'statement',
    fields: [
      { key: 'PIN', label: 'Data pin', type: 'number', min: 0, max: 48, default: 48 },
      { key: 'NUM', label: 'LED count', type: 'number', min: 1, max: 1024, default: 8 },
    ],
  },
  {
    id: 'i2c',
    label: 'I2C bus (init)',
    blockType: 'i2c_init',
    applyAs: 'statement',
    fields: [
      {
        key: 'CH',
        label: 'Channel',
        type: 'dropdown',
        options: [
          ['0', '0'],
          ['1', '1'],
        ],
        default: '0',
      },
      { key: 'SDA', label: 'SDA pin', type: 'number', min: 0, max: 48, default: 8 },
      { key: 'SCL', label: 'SCL pin', type: 'number', min: 0, max: 48, default: 9 },
      { key: 'FREQ', label: 'Frequency (Hz)', type: 'number', min: 1, max: 1_000_000, default: 400_000 },
    ],
  },
  {
    id: 'pwm',
    label: 'PWM (init)',
    blockType: 'pwm_init',
    applyAs: 'statement',
    fields: [
      { key: 'PIN', label: 'Pin', type: 'number', min: 0, max: 48, default: 2 },
      { key: 'FREQ', label: 'Frequency (Hz)', type: 'number', min: 1, max: 40_000_000, default: 1000 },
    ],
  },
  {
    id: 'buzzer',
    label: 'Buzzer (init)',
    blockType: 'sound_buzzer_init',
    applyAs: 'statement',
    fields: [
      { key: 'PIN', label: 'Pin', type: 'number', min: 0, max: 48, default: 0 },
    ],
  },
  {
    id: 'sonar',
    label: 'HC-SR04 sonar (distance block)',
    blockType: 'sonar_distance',
    applyAs: 'sonar_reporter',
    fields: [
      { key: 'TRIG', label: 'Trigger pin', type: 'number', min: 0, max: 48, default: 5 },
      { key: 'ECHO', label: 'Echo pin', type: 'number', min: 0, max: 48, default: 18 },
    ],
  },
];

const byId = new Map(ROBOT_TEMPLATE_LIST.map((t) => [t.id, t]));

/**
 * @param {string} id
 * @returns {RobotTemplate | undefined}
 */
export function getTemplate(id) {
  return byId.get(id);
}

/**
 * @param {RobotTemplate} template
 * @returns {Record<string, string | number>}
 */
export function defaultFieldsFor(template) {
  /** @type {Record<string, string | number>} */
  const out = {};
  for (const f of template.fields) {
    out[f.key] = f.default;
  }
  return out;
}

/**
 * @param {string} kind
 * @param {Record<string, unknown>} fields
 * @returns {Record<string, string>}
 */
export function normalizeFieldValues(kind, fields) {
  const template = getTemplate(kind);
  if (!template) return {};
  /** @type {Record<string, string>} */
  const out = {};
  const raw = fields && typeof fields === 'object' ? fields : {};
  for (const f of template.fields) {
    let v = raw[f.key];
    if (f.type === 'number') {
      const n = Math.round(Number(v));
      const clamped = Math.min(f.max ?? 48, Math.max(f.min ?? 0, Number.isFinite(n) ? n : f.default));
      out[f.key] = String(clamped);
    } else {
      const allowed = new Set(f.options.map((o) => o[1]));
      const s = String(v ?? f.default);
      out[f.key] = allowed.has(s) ? s : String(f.default);
    }
  }
  return out;
}
