import type { PlacedSymbol, ProjectInfo } from '../types.js';
import { serializeSymbol, deserializeSymbol } from '../serialization.js';

export const DEFAULT_CANVAS_W = 1920;
export const DEFAULT_CANVAS_H = 1080;

const MAX_UNDO = 50;

class LayoutStore {
  symbols = $state<PlacedSymbol[]>([]);
  nextId = $state(1);
  selectedIds = $state<Set<number>>(new Set());
  gridSize = $state(2);
  minSpacing = $state(2);
  snapEnabled = $state(true);
  showGrid = $state(false);
  zoomLevel = $state(1);
  panX = $state(0);
  panY = $state(0);
  canvasW = $state(DEFAULT_CANVAS_W);
  canvasH = $state(DEFAULT_CANVAS_H);

  // Project/MCM
  projects = $state<ProjectInfo[]>([]);
  currentProject = $state<string>('');
  currentMcm = $state<string>('');

  // PDF state
  pdfScale = $state(1.0);
  pdfOffsetX = $state(0);
  pdfOffsetY = $state(0);
  pdfLoaded = $state(false);
  editingBackground = $state(false);

  // Label-drag drop target highlight (symbol id or null)
  labelDropTarget = $state<number | null>(null);

  // Visibility: hidden device type groups and count of individually hidden symbols
  hiddenGroups = $state<Set<string>>(new Set());

  // Dirty flag for canvas re-render
  dirty = $state(0);

  // Undo stack (not reactive — internal only)
  private undoStack: { symbols: PlacedSymbol[]; nextId: number }[] = [];

  markDirty() {
    this.dirty++;
  }

  pushUndo() {
    this.undoStack.push({
      symbols: this.symbols.map(s => ({
        ...s,
        epcWaypoints: s.epcWaypoints?.map(wp => ({ ...wp })),
      })),
      nextId: this.nextId,
    });
    if (this.undoStack.length > MAX_UNDO) this.undoStack.shift();
  }

  undo() {
    const prev = this.undoStack.pop();
    if (!prev) return;
    this.symbols = prev.symbols;
    this.nextId = prev.nextId;
    this.selectedIds = new Set();
    this.markDirty();
    this.saveMcmState();
  }

  getMcmStorageKey(): string {
    return `scada_${this.currentProject}_${this.currentMcm}`;
  }

  saveMcmState() {
    if (!this.currentProject || !this.currentMcm) return;
    const state = {
      symbols: this.symbols.map(s => serializeSymbol(s)),
      nextId: this.nextId,
      gridSize: this.gridSize,
      minSpacing: this.minSpacing,
      pdfScale: this.pdfScale,
      pdfOffsetX: this.pdfOffsetX,
      pdfOffsetY: this.pdfOffsetY,
      canvasW: this.canvasW,
      canvasH: this.canvasH,
    };
    localStorage.setItem(this.getMcmStorageKey(), JSON.stringify(state));
  }

  loadMcmState() {
    this.undoStack = [];
    const raw = localStorage.getItem(this.getMcmStorageKey());
    if (!raw) {
      this.symbols = [];
      this.nextId = 1;
      this.selectedIds = new Set();
      this.markDirty();
      return;
    }
    try {
      const state = JSON.parse(raw);
      this.symbols = [];
      this.nextId = 1;
      this.selectedIds = new Set();
      if (state.gridSize) this.gridSize = state.gridSize;
      if (state.minSpacing) this.minSpacing = state.minSpacing;
      if (state.pdfScale) this.pdfScale = state.pdfScale;
      if (state.pdfOffsetX !== undefined) this.pdfOffsetX = state.pdfOffsetX;
      if (state.pdfOffsetY !== undefined) this.pdfOffsetY = state.pdfOffsetY;
      if (state.canvasW) this.canvasW = state.canvasW;
      if (state.canvasH) this.canvasH = state.canvasH;
      for (const s of state.symbols || []) {
        this.symbols.push(deserializeSymbol(s, this.nextId++));
      }
      if (state.nextId && state.nextId > this.nextId) this.nextId = state.nextId;
      this.markDirty();
    } catch (e) {
      console.error('Failed to load MCM state:', e);
    }
  }

  addSymbol(sym: Omit<PlacedSymbol, 'id'>): PlacedSymbol {
    this.pushUndo();
    const placed: PlacedSymbol = { ...sym, id: this.nextId++ };
    this.symbols.push(placed);
    this.selectedIds = new Set([placed.id]);
    this.markDirty();
    this.saveMcmState();
    return placed;
  }

  removeSymbol(id: number) {
    this.pushUndo();
    this.symbols = this.symbols.filter(s => s.id !== id);
    const newSet = new Set(this.selectedIds);
    newSet.delete(id);
    this.selectedIds = newSet;
    this.markDirty();
    this.saveMcmState();
  }

  removeSelected() {
    if (this.selectedIds.size === 0) return;
    this.pushUndo();
    this.symbols = this.symbols.filter(s => !this.selectedIds.has(s.id));
    this.selectedIds = new Set();
    this.markDirty();
    this.saveMcmState();
  }

  duplicateSymbol(id: number) {
    const orig = this.symbols.find(s => s.id === id);
    if (!orig) return;
    this.pushUndo();
    const newSym: PlacedSymbol = {
      ...orig,
      id: this.nextId++,
      x: orig.x + 20,
      y: orig.y + 20,
    };
    this.symbols.push(newSym);
    this.selectedIds = new Set([newSym.id]);
    this.markDirty();
    this.saveMcmState();
  }

  duplicateSelected() {
    if (this.selectedIds.size === 0) return;
    this.pushUndo();
    const newIds: number[] = [];
    for (const id of this.selectedIds) {
      const orig = this.symbols.find(s => s.id === id);
      if (!orig) continue;
      const newId = this.nextId++;
      this.symbols.push({
        ...orig,
        id: newId,
        x: orig.x + 20,
        y: orig.y + 20,
      });
      newIds.push(newId);
    }
    this.selectedIds = new Set(newIds);
    this.markDirty();
    this.saveMcmState();
  }

  mirrorSymbol(id: number) {
    const sym = this.symbols.find(s => s.id === id);
    if (!sym) return;
    this.pushUndo();
    sym.mirrored = !sym.mirrored;
    this.markDirty();
    this.saveMcmState();
  }

  mirrorSelected() {
    if (this.selectedIds.size === 0) return;
    this.pushUndo();
    for (const id of this.selectedIds) {
      const sym = this.symbols.find(s => s.id === id);
      if (sym) sym.mirrored = !sym.mirrored;
    }
    this.markDirty();
    this.saveMcmState();
  }

  toggleSymbolHidden(id: number) {
    const sym = this.symbols.find(s => s.id === id);
    if (!sym) return;
    sym.hidden = !sym.hidden;
    if (this.selectedIds.has(id)) {
      const newSet = new Set(this.selectedIds);
      newSet.delete(id);
      this.selectedIds = newSet;
    }
    this.markDirty();
    this.saveMcmState();
  }

  hideSelected() {
    if (this.selectedIds.size === 0) return;
    for (const id of this.selectedIds) {
      const sym = this.symbols.find(s => s.id === id);
      if (sym) sym.hidden = true;
    }
    this.selectedIds = new Set();
    this.markDirty();
    this.saveMcmState();
  }

  showAllHidden() {
    let changed = false;
    for (const sym of this.symbols) {
      if (sym.hidden) {
        sym.hidden = false;
        changed = true;
      }
    }
    if (this.hiddenGroups.size > 0) {
      this.hiddenGroups = new Set();
      changed = true;
    }
    if (changed) {
      this.markDirty();
    }
  }

  toggleGroupHidden(group: string) {
    const newSet = new Set(this.hiddenGroups);
    if (newSet.has(group)) {
      newSet.delete(group);
    } else {
      newSet.add(group);
    }
    this.hiddenGroups = newSet;
    this.markDirty();
  }

  clearAll() {
    this.pushUndo();
    this.symbols = [];
    this.selectedIds = new Set();
    this.nextId = 1;
    this.markDirty();
    this.saveMcmState();
  }
}

export const layout = new LayoutStore();
