import { pythonGenerator, Order } from 'blockly/python';
import { getDeviceId } from '../devices/registry.js';

const DEVICE = () => getDeviceId();

// micro:bit pin name: pin0..pin20 (clamp to valid range)
function mbPin(pin) {
  const n = Math.max(0, Math.min(20, parseInt(pin, 10) || 0));
  return `pin${n}`;
}

// ─── Pin I/O ──────────────────────────────────────────────

function ensurePinVar(pin, mode) {
  pythonGenerator.definitions_['import_machine'] = 'from machine import Pin, PWM, ADC, I2C';
  const varName = `pin_${pin}`;
  const modeStr = mode === 'IN' ? 'Pin.IN' : 'Pin.OUT';
  pythonGenerator.definitions_[`pin_init_${pin}`] = `${varName} = Pin(${pin}, ${modeStr})`;
  return varName;
}

pythonGenerator.forBlock['pin_set_mode'] = function (block) {
  const pin = block.getFieldValue('PIN');
  if (DEVICE() === 'microbit') {
    return ''; // micro:bit has no explicit set_mode
  }
  const mode = block.getFieldValue('MODE');
  pythonGenerator.definitions_['import_machine'] = 'from machine import Pin, PWM, ADC, I2C';
  const varName = `pin_${pin}`;
  const pyMode = `Pin.${mode}`;
  pythonGenerator.definitions_[`pin_init_${pin}`] = `${varName} = Pin(${pin}, ${pyMode})`;
  return '';
};

pythonGenerator.forBlock['pin_digital_write'] = function (block) {
  const pin = block.getFieldValue('PIN');
  const value = block.getFieldValue('VALUE');
  if (DEVICE() === 'microbit') {
    return `${mbPin(pin)}.write_digital(${value})\n`;
  }
  const varName = ensurePinVar(pin, 'OUT');
  return `${varName}.value(${value})\n`;
};

pythonGenerator.forBlock['pin_digital_read'] = function (block) {
  const pin = block.getFieldValue('PIN');
  if (DEVICE() === 'microbit') {
    return [`${mbPin(pin)}.read_digital()`, Order.FUNCTION_CALL];
  }
  const varName = ensurePinVar(pin, 'IN');
  return [`${varName}.value()`, Order.FUNCTION_CALL];
};

// ─── PWM ──────────────────────────────────────────────────

pythonGenerator.forBlock['pwm_init'] = function (block) {
  if (DEVICE() === 'microbit') return ''; // no separate init on micro:bit
  const pin = block.getFieldValue('PIN');
  const freq = block.getFieldValue('FREQ');
  pythonGenerator.definitions_['import_machine'] = 'from machine import Pin, PWM, ADC, I2C';
  return `pwm_${pin} = PWM(Pin(${pin}), freq=${freq})\n`;
};

pythonGenerator.forBlock['pwm_set_duty'] = function (block) {
  const pin = block.getFieldValue('PIN');
  const duty = pythonGenerator.valueToCode(block, 'DUTY', Order.NONE) || '0';
  if (DEVICE() === 'microbit') {
    return `${mbPin(pin)}.write_analog(${duty})\n`;
  }
  pythonGenerator.definitions_['import_machine'] = 'from machine import Pin, PWM, ADC, I2C';
  return `pwm_${pin}.duty(${duty})\n`;
};

pythonGenerator.forBlock['pwm_set_freq'] = function (block) {
  if (DEVICE() === 'microbit') return ''; // micro:bit doesn't support changing PWM freq per-pin
  const pin = block.getFieldValue('PIN');
  const freq = pythonGenerator.valueToCode(block, 'FREQ', Order.NONE) || '1000';
  pythonGenerator.definitions_['import_machine'] = 'from machine import Pin, PWM, ADC, I2C';
  return `pwm_${pin}.freq(${freq})\n`;
};

// ─── ADC ──────────────────────────────────────────────────

pythonGenerator.forBlock['adc_read'] = function (block) {
  const pin = block.getFieldValue('PIN');
  if (DEVICE() === 'microbit') {
    return [`${mbPin(pin)}.read_analog()`, Order.FUNCTION_CALL];
  }
  pythonGenerator.definitions_['import_machine'] = 'from machine import Pin, PWM, ADC, I2C';
  pythonGenerator.definitions_[`adc_init_${pin}`] = `adc_${pin} = ADC(Pin(${pin}))`;
  return [`adc_${pin}.read()`, Order.FUNCTION_CALL];
};

// ─── Time ─────────────────────────────────────────────────

pythonGenerator.forBlock['sleep_seconds'] = function (block) {
  const seconds = pythonGenerator.valueToCode(block, 'SECONDS', Order.NONE) || '1';
  if (DEVICE() === 'microbit') {
    pythonGenerator.definitions_['import_microbit_sleep'] = 'from microbit import sleep';
    return `sleep(${seconds} * 1000)\n`;
  }
  pythonGenerator.definitions_['import_time'] = 'import time';
  return `time.sleep(${seconds})\n`;
};

pythonGenerator.forBlock['sleep_ms'] = function (block) {
  const ms = pythonGenerator.valueToCode(block, 'MS', Order.NONE) || '100';
  if (DEVICE() === 'microbit') {
    pythonGenerator.definitions_['import_microbit_sleep'] = 'from microbit import sleep';
    return `sleep(${ms})\n`;
  }
  pythonGenerator.definitions_['import_time'] = 'import time';
  return `time.sleep_ms(${ms})\n`;
};

pythonGenerator.forBlock['ticks_ms'] = function () {
  if (DEVICE() === 'microbit') {
    pythonGenerator.definitions_['import_microbit_running_time'] = 'from microbit import running_time';
    return ['running_time()', Order.FUNCTION_CALL];
  }
  pythonGenerator.definitions_['import_time'] = 'import time';
  return ['time.ticks_ms()', Order.FUNCTION_CALL];
};

// ─── WiFi ─────────────────────────────────────────────────

pythonGenerator.forBlock['wifi_connect'] = function (block) {
  if (DEVICE() === 'microbit' || DEVICE() === 'rp2040') {
    return '# WiFi not available on this device\n';
  }
  const ssid = block.getFieldValue('SSID');
  const password = block.getFieldValue('PASSWORD');
  pythonGenerator.definitions_['import_network'] = 'import network';
  return [
    `wlan = network.WLAN(network.STA_IF)\n`,
    `wlan.active(True)\n`,
    `wlan.connect('${ssid}', '${password}')\n`,
    `while not wlan.isconnected():\n`,
    `    time.sleep(0.1)\n`,
    `print('Connected:', wlan.ifconfig()[0])\n`,
  ].join('');
};

pythonGenerator.forBlock['wifi_get_ip'] = function (block) {
  if (DEVICE() === 'microbit' || DEVICE() === 'rp2040') {
    return ["''", Order.ATOMIC];
  }
  pythonGenerator.definitions_['import_network'] = 'import network';
  return ['network.WLAN(network.STA_IF).ifconfig()[0]', Order.FUNCTION_CALL];
};

// ─── NeoPixel ─────────────────────────────────────────────

pythonGenerator.forBlock['neopixel_init'] = function (block) {
  if (DEVICE() === 'microbit') {
    return '# NeoPixel on micro:bit: use neopixel library with pin\n';
  }
  const pin = block.getFieldValue('PIN');
  const num = block.getFieldValue('NUM');
  pythonGenerator.definitions_['import_machine'] = 'from machine import Pin, PWM, ADC, I2C';
  pythonGenerator.definitions_['import_neopixel'] = 'import neopixel';
  return `np = neopixel.NeoPixel(Pin(${pin}), ${num})\n`;
};

pythonGenerator.forBlock['neopixel_set_color'] = function (block) {
  if (DEVICE() === 'microbit') return '';
  const index = block.getFieldValue('INDEX');
  const r = pythonGenerator.valueToCode(block, 'R', Order.NONE) || '0';
  const g = pythonGenerator.valueToCode(block, 'G', Order.NONE) || '0';
  const b = pythonGenerator.valueToCode(block, 'B', Order.NONE) || '0';
  return `np[${index}] = (${r}, ${g}, ${b})\n`;
};

pythonGenerator.forBlock['neopixel_show'] = function () {
  if (DEVICE() === 'microbit') return '';
  return 'np.write()\n';
};

// ─── I2C ──────────────────────────────────────────────────

pythonGenerator.forBlock['i2c_init'] = function (block) {
  if (DEVICE() === 'microbit') return ''; // micro:bit has built-in i2c
  const sda = block.getFieldValue('SDA');
  const scl = block.getFieldValue('SCL');
  const freq = block.getFieldValue('FREQ');
  pythonGenerator.definitions_['import_machine'] = 'from machine import Pin, PWM, ADC, I2C';
  return `i2c = I2C(0, sda=Pin(${sda}), scl=Pin(${scl}), freq=${freq})\n`;
};

pythonGenerator.forBlock['i2c_scan'] = function (block) {
  if (DEVICE() === 'microbit') {
    return ['[]', Order.ATOMIC]; // stub
  }
  pythonGenerator.definitions_['import_machine'] = 'from machine import Pin, PWM, ADC, I2C';
  return ['i2c.scan()', Order.FUNCTION_CALL];
};

pythonGenerator.forBlock['i2c_writeto'] = function (block) {
  if (DEVICE() === 'microbit') return '';
  const addr = block.getFieldValue('ADDR');
  const data = pythonGenerator.valueToCode(block, 'DATA', Order.NONE) || 'b""';
  return `i2c.writeto(${addr}, ${data})\n`;
};

pythonGenerator.forBlock['i2c_readfrom'] = function (block) {
  if (DEVICE() === 'microbit') return ['b""', Order.ATOMIC];
  const addr = block.getFieldValue('ADDR');
  const nbytes = block.getFieldValue('NBYTES');
  return [`i2c.readfrom(${addr}, ${nbytes})`, Order.FUNCTION_CALL];
};

// ─── Print ────────────────────────────────────────────────

pythonGenerator.forBlock['print_text'] = function (block) {
  const text = pythonGenerator.valueToCode(block, 'TEXT', Order.NONE) || "''";
  return `print(${text})\n`;
};

// ─── Sound ───────────────────────────────────────────────

pythonGenerator.forBlock['sound_play_tone_speaker'] = function (block) {
  const freq = pythonGenerator.valueToCode(block, 'FREQ', Order.NONE) || '440';
  const duration = pythonGenerator.valueToCode(block, 'DURATION', Order.NONE) || '500';
  const wait = block.getFieldValue('WAIT') === 'TRUE';

  if (DEVICE() === 'microbit') {
    pythonGenerator.definitions_['import_music'] = 'import music';
    return `music.pitch(${freq}, ${duration}, wait=${wait ? 'True' : 'False'})\n`;
  }
  return '# Built-in speaker only available on micro:bit\n';
};

pythonGenerator.forBlock['sound_stop_speaker'] = function () {
  if (DEVICE() === 'microbit') {
    pythonGenerator.definitions_['import_music'] = 'import music';
    return 'music.stop()\n';
  }
  return '# Built-in speaker only available on micro:bit\n';
};

pythonGenerator.forBlock['sound_play_tone'] = function (block) {
  const pin = block.getFieldValue('PIN');
  const freq = pythonGenerator.valueToCode(block, 'FREQ', Order.NONE) || '440';
  const duration = pythonGenerator.valueToCode(block, 'DURATION', Order.NONE) || '500';
  const wait = block.getFieldValue('WAIT') === 'TRUE';

  if (DEVICE() === 'microbit') {
    pythonGenerator.definitions_['import_music'] = 'import music';
    pythonGenerator.definitions_['import_microbit_pins'] = 'from microbit import *';
    return `music.pitch(${freq}, ${duration}, pin=${mbPin(pin)}, wait=${wait ? 'True' : 'False'})\n`;
  }

  pythonGenerator.definitions_['import_machine'] = 'from machine import Pin, PWM, ADC, I2C';
  const varName = `buzzer_${pin}`;
  pythonGenerator.definitions_[`buzzer_init_${pin}`] = `${varName} = PWM(Pin(${pin}))`;

  if (wait) {
    pythonGenerator.definitions_['import_time'] = 'import time';
    return `${varName}.freq(${freq})\n${varName}.duty(512)\ntime.sleep_ms(${duration})\n${varName}.duty(0)\n`;
  }

  pythonGenerator.definitions_['import_timer'] = 'from machine import Timer';
  return `${varName}.freq(${freq})\n${varName}.duty(512)\nTimer(0).init(period=${duration}, mode=Timer.ONE_SHOT, callback=lambda t: ${varName}.duty(0))\n`;
};

pythonGenerator.forBlock['sound_stop'] = function (block) {
  const pin = block.getFieldValue('PIN');

  if (DEVICE() === 'microbit') {
    pythonGenerator.definitions_['import_music'] = 'import music';
    return 'music.stop()\n';
  }

  pythonGenerator.definitions_['import_machine'] = 'from machine import Pin, PWM, ADC, I2C';
  const varName = `buzzer_${pin}`;
  pythonGenerator.definitions_[`buzzer_init_${pin}`] = `${varName} = PWM(Pin(${pin}))`;
  return `${varName}.duty(0)\n`;
};

// ─── micro:bit Display ────────────────────────────────────

pythonGenerator.forBlock['microbit_display_all'] = function (block) {
  if (DEVICE() !== 'microbit') {
    return '# micro:bit display block (select micro:bit device)\n';
  }
  // Build 5x5 string from checkboxes P00..P44 (each row = 5 chars, rows joined by :)
  const rows = [];
  for (let r = 0; r < 5; r++) {
    let row = '';
    for (let c = 0; c < 5; c++) {
      const val = block.getFieldValue(`P${r}${c}`);
      row += val === 'TRUE' ? '9' : '0';
    }
    rows.push(row);
  }
  const imageStr = rows.join(':');
  pythonGenerator.definitions_['import_microbit_display'] = 'from microbit import display, Image';
  return `display.show(Image("${imageStr}"))\n`;
};
