// ============================================================
// Data model + default simulation
// ============================================================
const GRID_W = 16;
const GRID_H = 10;

const rid = (p = 'id') => `${p}_${Math.random().toString(36).slice(2, 8)}`;

function makeRack(rackId, K, M) {
  const kindPool = ['server', 'server', 'switch', 'storage', 'server'];
  const slots = [];
  for (let u = 0; u < K; u++) {
    const r = Math.random();
    let kind = 'empty';
    if (r < 0.78) kind = kindPool[Math.floor(Math.random() * kindPool.length)];
    const ports = (kind === 'server' || kind === 'switch')
      ? Array.from({ length: 2 * M }, () => {
          const rr = Math.random();
          if (rr < 0.55) return 'connected';
          if (rr < 0.72) return 'suspicious';
          return 'unplugged';
        })
      : [];
    slots.push({
      id: rid('slot'),
      kind,
      name: kind === 'empty' ? '' : {
        server:  `app-${Math.floor(Math.random() * 900 + 100)}`,
        switch:  `sw-${Math.floor(Math.random() * 90 + 10)}`,
        storage: `vol-${Math.floor(Math.random() * 900 + 100)}`,
      }[kind],
      power: kind !== 'empty' ? (Math.random() < 0.82 ? 'on' : 'off') : 'off',
      ports,
    });
  }
  return { id: rackId, label: rackId.toUpperCase(), absent: false, slots };
}

function makeAbsentRack(rackId) {
  return { id: rackId, label: rackId.toUpperCase(), absent: true, slots: [] };
}

// health for a single rack (skips absent)
function rackHealth(rack) {
  if (!rack || rack.absent) return { level: 'absent', on: 0, total: 0, ports: 0, susp: 0 };
  let on = 0, total = 0, ports = 0, susp = 0;
  for (const s of rack.slots) {
    if (s.kind === 'empty') continue;
    total++;
    if (s.power === 'on') on++;
    for (const p of s.ports) {
      ports++;
      if (p === 'suspicious') susp++;
    }
  }
  let level = 'off';
  if (total === 0) level = 'empty';
  else if (on === 0) level = 'off';
  else if (susp / Math.max(1, ports) > 0.25) level = 'bad';
  else if (susp > 0) level = 'warn';
  else level = 'ok';
  return { level, on, total, ports, susp };
}

// derive group width (in grid cells) from rack count — each rack = 1 cell wide
// never smaller than 1, never bigger than grid
function groupWidth(g) {
  return Math.max(1, g.racks.length);
}

function makeGroup(gridX, gridY, w, h, N, K, M, tag, absentIdxs = []) {
  const racks = Array.from({ length: N }, (_, i) => {
    const label = `${tag}-${String.fromCharCode(65 + i)}`;
    if (absentIdxs.includes(i)) return makeAbsentRack(label.toLowerCase());
    return makeRack(label.toLowerCase(), K, M);
  });
  return { id: rid('rg'), x: gridX, y: gridY, w, h, tag, racks };
}

function defaultConfig() {
  const N = 4, M = 4, K = 6;
  return {
    params: { N, M, K, gridW: GRID_W, gridH: GRID_H, floorDetail: 'status' },
    entrances: [{ id: rid('ent'), x: 0, y: 4, w: 1, h: 2 }],
    groups: [
      makeGroup(3, 2, 4, 2, N, K, M, 'RG-01'),
      makeGroup(9, 5, 4, 2, N, K, M, 'RG-02', [2]),
    ],
    robots: [
      {
        id: rid('rbt'),
        name: 'R-01',
        speed: 0.8,
        // each waypoint: {x, y, inspect?: {facing: radians, duration: seconds}}
        path: [
          { x: 1, y: 5 },
          { x: 5, y: 5, inspect: { facing: -Math.PI/2, duration: 3 } },
          { x: 5, y: 2 },
          { x: 10, y: 2, inspect: { facing: Math.PI/2, duration: 3 } },
          { x: 10, y: 6 },
          { x: 14, y: 6 },
          { x: 14, y: 1 },
          { x: 1, y: 1 },
        ],
      },
    ],
  };
}

function groupHealth(group) {
  let ports = 0, bad = 0, susp = 0, total = 0, on = 0;
  for (const r of group.racks) {
    if (r.absent) continue;
    for (const s of r.slots) {
      if (s.kind === 'empty') continue;
      total++;
      if (s.power === 'on') on++;
      for (const p of s.ports) {
        ports++;
        if (p === 'suspicious') susp++;
      }
    }
  }
  let level = 'ok';
  if (susp / Math.max(1, ports) > 0.25) level = 'bad';
  else if (susp > 0) level = 'warn';
  return { level, ports, susp, total, on };
}

const STORAGE_KEY = 'sr_console_config_v1';

function loadLocal() {
  try {
    const s = localStorage.getItem(STORAGE_KEY);
    if (!s) return null;
    return JSON.parse(s);
  } catch { return null; }
}
function saveLocal(cfg) {
  try { localStorage.setItem(STORAGE_KEY, JSON.stringify(cfg)); } catch {}
}

async function fetchServerState() {
  try {
    const r = await fetch('/api/state', { cache: 'no-store' });
    if (!r.ok) return null;
    const j = await r.json();
    return j && j.updated_at ? j : null;
  } catch {
    return null;
  }
}

function mergeServerStatus(cfg, server) {
  if (!server || !server.racks) return cfg;
  const groups = cfg.groups.map(g => ({
    ...g,
    racks: g.racks.map(rack => {
      const patch = server.racks[rack.label];
      if (!patch || rack.absent) return rack;
      return {
        ...rack,
        slots: rack.slots.map((slot, i) => {
          const sp = patch.slots && patch.slots[i];
          if (!sp) return slot;
          return {
            ...slot,
            power: sp.power !== undefined ? sp.power : slot.power,
            ports: sp.ports !== undefined ? sp.ports : slot.ports,
          };
        }),
      };
    }),
  }));
  return { ...cfg, groups };
}

Object.assign(window, { defaultConfig, groupHealth, rackHealth, makeRack, makeAbsentRack, loadLocal, saveLocal, STORAGE_KEY, rid, groupWidth, fetchServerState, mergeServerStatus });
