import { LadderEngine } from '../engine/engine';
import type { Program, Rung, RungElement, Contact, Coil, Timer, Counter, LogicGate, ContactType, CoilType, TimerType, CounterType, GateType, Branch } from '../engine/types';

// ─── State ────────────────────────────────────────────────────────
let engine = new LadderEngine();
let currentProgram: Program = {
  name: 'Untitled',
  rungs: [],
  cycleTime: 100,
};

let selectedElement: { rungIndex: number; elementIndex: number } | null = null;
let stopEngine: (() => void) | null = null;

// Track structure to avoid redundant full renders
let lastStructureKey = '';

// ─── DOM ──────────────────────────────────────────────────────────
const canvas = document.getElementById('ladder-canvas')!;
const emptyState = document.getElementById('empty-state')!;
const btnAddRung = document.getElementById('btn-add-rung')!;
const btnPlay = document.getElementById('btn-play')!;
const btnPause = document.getElementById('btn-pause')!;
const btnStop = document.getElementById('btn-stop')!;
const configPanel = document.getElementById('config-panel')!;
const configBody = document.getElementById('config-body')!;
const statusEngineText = document.getElementById('status-engine-text')!;

const configClose = document.getElementById('config-close')!;
configClose.onclick = () => {
  configPanel.classList.remove('visible');
  selectedElement = null;
};

const btnDeleteElement = document.getElementById('btn-delete-element')!;
btnDeleteElement.onclick = () => {
  if (selectedElement) {
    currentProgram.rungs[selectedElement.rungIndex].series.splice(selectedElement.elementIndex, 1);
    engine.loadProgram(currentProgram);
    configPanel.classList.remove('visible');
    selectedElement = null;
  }
};

// ─── Initialization ───────────────────────────────────────────────
engine.subscribe(() => {
  syncUI();
});

function syncUI() {
  const currentStructureKey = currentProgram.rungs.map(r => `${r.id}-${r.series.length}`).join('|');
  
  if (currentStructureKey !== lastStructureKey) {
    renderLadder();
    lastStructureKey = currentStructureKey;
  }

  updateVisuals();
  updateStatus();
}

// ─── Rung Management ─────────────────────────────────────────────
btnAddRung.addEventListener('click', () => {
  const newRung: Rung = { id: `rung-${Date.now()}`, enabled: true, series: [] };
  currentProgram.rungs.push(newRung);
  engine.loadProgram(currentProgram);
});

btnPlay.addEventListener('click', () => {
  engine.loadProgram(currentProgram);
  stopEngine = engine.start();
  btnPlay.style.display = 'none';
  btnPause.style.display = '';
});

btnPause.addEventListener('click', () => {
  if (stopEngine) { stopEngine(); stopEngine = null; }
  btnPlay.style.display = '';
  btnPause.style.display = 'none';
});

btnStop.addEventListener('click', () => {
  if (stopEngine) { stopEngine(); stopEngine = null; }
  engine.loadProgram(currentProgram);
  btnPlay.style.display = '';
  btnPause.style.display = 'none';
});

// ─── Drag & Drop ────────────────────────────────────
document.querySelectorAll('.tool-item').forEach(item => {
  (item as HTMLElement).draggable = true;
  item.addEventListener('dragstart', (e) => {
    const tool = (e.target as HTMLElement).closest('.tool-item')?.getAttribute('data-tool');
    if (tool) {
      (e as DragEvent).dataTransfer?.setData('text/plain', tool);
    }
  });
});

// ─── Rendering (Structural) ───────────────────────────────────────

function renderLadder(): void {
  canvas.innerHTML = '';
  emptyState.style.display = currentProgram.rungs.length === 0 ? '' : 'none';

  currentProgram.rungs.forEach((rung, idx) => {
    const rungEl = document.createElement('div');
    rungEl.className = 'rung' + (rung.enabled ? '' : ' disabled');
    rungEl.dataset.rungId = rung.id;
    
    // Rung number and left line
    const num = document.createElement('div');
    num.className = 'rung-number';
    num.textContent = (idx + 1).toString();
    rungEl.appendChild(num);

    const lineLeft = document.createElement('div');
    lineLeft.className = 'rung-line-left';
    rungEl.appendChild(lineLeft);
    
    // Drag & Drop
    rungEl.addEventListener('dragover', (e) => {
      e.preventDefault();
      rungEl.classList.add('drag-over');
    });
    rungEl.addEventListener('dragleave', () => {
      rungEl.classList.remove('drag-over');
    });
    rungEl.addEventListener('drop', (e) => {
      e.preventDefault();
      rungEl.classList.remove('drag-over');
      const tool = (e as DragEvent).dataTransfer?.getData('text/plain');
      if (tool) {
        handleDrop(tool, idx, rungEl);
      }
    });

    // Elements in the rung
    rung.series.forEach((el, ei) => {
      const elEl = createElementDOM(el, idx, ei);
      rungEl.appendChild(elEl);
    });

    // Drop zone and right line
    const dropZone = document.createElement('div');
    dropZone.className = 'element-drop-zone';
    rungEl.appendChild(dropZone);

    const lineRight = document.createElement('div');
    lineRight.className = 'rung-line-right';
    rungEl.appendChild(lineRight);

    canvas.appendChild(rungEl);
  });
}

function createElementDOM(el: RungElement, rungIdx: number, elIdx: number): HTMLElement {
  const elEl = document.createElement('div');
  elEl.className = 'element';
  elEl.textContent = el.type;
  
  elEl.dataset.type = el.type;
  elEl.dataset.rungIdx = rungIdx.toString();
  elEl.dataset.elIdx = elIdx.toString();

  if (el.type === 'contact') {
    const contact = el as Contact;
    elEl.dataset.address = contact.address;
    elEl.dataset.contactType = contact.contactType;
  } else if (el.type === 'coil') {
    const coil = el as Coil;
    elEl.dataset.address = coil.address;
    elEl.dataset.coilType = coil.coilType;
  } else if (el.type === 'timer') {
    const timer = el as Timer;
    elEl.dataset.instanceId = timer.instanceId;
  } else if (el.type === 'counter') {
    const counter = el as Counter;
    elEl.dataset.instanceId = counter.instanceId;
  } else if (el.type === 'gate') {
    const gate = el as LogicGate;
    elEl.dataset.gateType = gate.gateType;
    gate.inputs.forEach((addr, i) => {
      elEl.setAttribute(`data-gate-input-${i}`, addr);
    });
  } else if (el.type === 'branch') {
    const branch = el as Branch;
    elEl.classList.add('branch-element');
    elEl.dataset.logic = branch.logic;
    
    if (branch.paths.length === 1) {
      elEl.classList.add('single-path');
    }

    branch.paths.forEach((path, pathIdx) => {
      const pathEl = document.createElement('div');
      pathEl.className = 'branch-path';
      path.forEach((pathElItem, pathElIdx) => {
        pathEl.appendChild(createElementDOM(pathElItem, rungIdx, pathElIdx));
      });
      elEl.appendChild(pathEl);
    });
  }

  if (el.type === 'coil') {
    elEl.style.marginLeft = 'auto';
  }

  elEl.onclick = (e) => {
    e.stopPropagation();
    selectedElement = { rungIndex: rungIdx, elementIndex: elIdx };
    showConfigPanel(rungIdx, elIdx);
  };

  return elEl;
}

function handleDrop(tool: string, rungIdx: number, rungEl: HTMLElement) {
  let newElement: any;
  if (tool.startsWith('contact-')) {
    newElement = { type: 'contact', contactType: tool.endsWith('no') ? 'NO' : 'NC', address: 'UNKNOWN' };
  } else if (tool.startsWith('coil-')) {
    newElement = { type: 'coil', coilType: tool.split('-')[1].toUpperCase() as any, address: 'UNKNOWN' };
  } else if (tool.startsWith('timer-')) {
    newElement = { type: 'timer', timerType: tool.split('-')[1].toUpperCase() as any, instanceId: `timer-${Date.now()}`, preset: 1000, accumulated: 0 };
  } else if (tool.startsWith('counter-')) {
    newElement = { type: 'counter', counterType: tool.split('-')[1].toUpperCase() as any, instanceId: `counter-${Date.now()}`, preset: 0, current: 0 };
  } else if (tool.startsWith('gate-')) {
    newElement = { type: 'gate', gateType: tool.split('-')[1].toUpperCase() as any, inputs: [], outputAddress: 'UNKNOWN' };
  } else if (tool === 'branch') {
    newElement = { type: 'branch', id: `branch-${Date.now()}`, logic: 'AND', paths: [[]] };
  }

  if (newElement) {
    const series = currentProgram.rungs[rungIdx].series;
    series.push(newElement);

    const coilIndex = series.findIndex(el => el.type === 'coil');
    if (coilIndex !== -1 && coilIndex !== series.length - 1) {
      const [coil] = series.splice(coilIndex, 1);
      series.push(coil);
    }

    engine.loadProgram(currentProgram);
  }
}

// ─── Visual Updates (State-driven) ─────────────────────────────────

function updateVisuals(): void {
  const state = engine.getState();

  document.querySelectorAll('.element[data-type="contact"]').forEach(el => {
    const address = el.dataset.address!;
    const contactType = el.dataset.contactType!;
    const input = state.inputs.get(address);
    const inputValue = input ? input.value : false;
    const isPassing = contactType === 'NO' ? !!inputValue : !inputValue;
    el.classList.toggle('active', isPassing);  });

  document.querySelectorAll('.element[data-type="coil"]').forEach(el => {
    const address = el.dataset.address!;
    const output = state.outputs.get(address);
    const isEnergized = !!output?.value;
    el.classList.toggle('energized', isEnergized);  });

  document.querySelectorAll('.element[data-instance-id]').forEach(el => {
    const instanceId = el.dataset.instanceId!;
    const type = el.dataset.type!;
    if (type === 'timer') {
      const timer = state.timers.get(instanceId);
      el.classList.toggle('running', !!timer && timer.accumulated > 0);
    } else if (type === 'counter') {
      const counter = state.counters.get(instanceId);
      el.classList.toggle('active', !!counter && counter.current > 0);
    }
  });
}

function showConfigPanel(rungIdx: number, elIdx: number): void {
  configPanel.classList.add('visible');
  const element = currentProgram.rungs[rungIdx].series[elIdx];
  const titleEl = document.getElementById('config-title')!;
  titleEl.textContent = `Edit ${element.type.toUpperCase()}`;
  
  configBody.innerHTML = '';
  const form = document.createElement('form');
  form.onsubmit = (e) => {
    e.preventDefault();
    const formData = new FormData(form);
    const updatedElement = { ...element } as any;

    if (element.type === 'contact') {
      updatedElement.contactType = formData.get('contactType') as ContactType;
      updatedElement.address = formData.get('address') as string;
    } else if (element.type === 'coil') {
      updatedElement.coilType = formData.get('coilType') as CoilType;
      updatedElement.address = formData.get('address') as string;
    } else if (element.type === 'timer') {
      updatedElement.timerType = formData.get('timerType') as TimerType;
      updatedElement.preset = Number(formData.get('preset'));
    } else if (element.type === 'counter') {
      updatedElement.counterType = formData.get('counterType') as CounterType;
      updatedElement.preset = Number(formData.get('preset'));
      updatedElement.resetAddress = formData.get('resetAddress') as string || undefined;
    } else if (element.type === 'gate') {
      updatedElement.gateType = formData.get('gateType') as GateType;
      updatedElement.outputAddress = formData.get('outputAddress') as string;
      const inputsInput = form.querySelector('input[name="inputs"]') as HTMLInputElement;
      updatedElement.inputs = inputsInput.value.split(',').map((s: string) => s.trim()).filter((s: string) => s !== '');
    } else if (element.type === 'branch') {
      updatedElement.logic = formData.get('logic') as 'AND' | 'OR';
    }

    currentProgram.rungs[rungIdx].series[elIdx] = updatedElement;
    engine.loadProgram(currentProgram);
    configPanel.classList.remove('visible');
  };

  const addField = (label: string, name: string, type: 'text' | 'number' | 'select', options?: string[]) => {
    const div = document.createElement('div');
    div.className = 'config-field';
    const lbl = document.createElement('label');
    lbl.textContent = label;
    div.appendChild(lbl);

    let input: HTMLInputElement | HTMLSelectElement;
    if (type === 'select' && options) {
      input = document.createElement('select');
      options.forEach(opt => {
        const o = document.createElement('option');
        o.value = opt;
        o.textContent = opt;
        if ((element as any)[name] === opt) o.selected = true;
        input.appendChild(o);
      });
      input.name = name;
    } else {
      input = document.createElement('input');
      input.type = type;
      input.name = name;
      (input as HTMLInputElement).value = (element as any)[name]?.toString() || '';
    }
    div.appendChild(input);
    form.appendChild(div);
  };

  if (element.type === 'contact') {
    addField('Contact Type', 'contactType', 'select', ['NO', 'NC']);
    addField('Address', 'address', 'text');
  } else if (element.type === 'coil') {
    addField('Coil Type', 'coilType', 'select', ['OUTPUT', 'SET', 'RESET', 'TOGGLE']);
    addField('Address', 'address', 'text');
  } else if (element.type === 'timer') {
    addField('Timer Type', 'timerType', 'select', ['TON', 'TOF', 'TP']);
    addField('Preset (ms)', 'preset', 'number');
  } else if (element.type === 'counter') {
    addField('Counter Type', 'counterType', 'select', ['CTU', 'CTD', 'CTUD']);
    addField('Preset', 'preset', 'number');
    addField('Reset Address', 'resetAddress', 'text');
  } else if (element.type === 'gate') {
    addField('Gate Type', 'gateType', 'select', ['AND', 'OR', 'XOR', 'NOT', 'NAND', 'NOR']);
    addField('Output Address', 'outputAddress', 'text');
    const inputsDiv = document.createElement('div');
    inputsDiv.className = 'config-field';
    const lbl = document.createElement('label');
    lbl.textContent = 'Inputs (comma separated)';
    inputsDiv.appendChild(lbl);
    const input = document.createElement('input');
    input.type = 'text';
    input.name = 'inputs';
    input.value = (element as LogicGate).inputs.join(', ');
    inputsDiv.appendChild(input);
    form.appendChild(inputsDiv);
  } else if (element.type === 'branch') {
    addField('Logic', 'logic', 'select', ['AND', 'OR']);
  }

  const actionsDiv = document.createElement('div');
  actionsDiv.className = 'config-actions';
  
  const submitBtn = document.createElement('button');
  submitBtn.type = 'submit';
  submitBtn.textContent = 'Save';
  submitBtn.className = 'btn';
  submitBtn.style.flex = '1';
  actionsDiv.appendChild(submitBtn);

  const cancelBtn = document.createElement('button');
  cancelBtn.type = 'button';
  cancelBtn.textContent = 'Cancel';
  cancelBtn.className = 'btn';
  cancelBtn.style.flex = '1';
  cancelBtn.onclick = () => configPanel.classList.remove('visible');
  actionsDiv.appendChild(cancelBtn);

  form.appendChild(actionsDiv);
  configBody.appendChild(form);
}

function updateStatus(): void {
  const state = engine.getState();
  statusEngineText.textContent = state.running ? 'Running' : 'Stopped';
  statusEngineText.className = state.running ? 'status-running' : 'status-stopped';
}