const electron = require('electron');
const ipcRenderer = electron.ipcRenderer;
const {
  adjacentRect,
  boundaryMemberForEdge,
  edgeOpposite,
  findGroupSnapCandidate,
} = require('./display-layout');

let currentState = null;
let selectedTargetId = null;
let activeDisplayDrag = null;
const DEFAULT_RELEASE_SHORTCUT = 'Esc, Esc, Esc';
const DISPLAY_SNAP_DISTANCE = 48;

const peerList = document.getElementById('peerList');
const statusLine = document.getElementById('statusLine');
const networkLine = document.getElementById('networkLine');
const localName = document.getElementById('localName');
const localMeta = document.getElementById('localMeta');
const displayMap = document.getElementById('displayMap');
const setupTitle = document.getElementById('setupTitle');
const setupMeta = document.getElementById('setupMeta');
const nickname = document.getElementById('nickname');
const releaseShortcut = document.getElementById('releaseShortcut');
const releaseReset = document.getElementById('releaseReset');
const shortcutTest = document.getElementById('shortcutTest');
const permissionPanel = document.getElementById('permissionPanel');
const permissionButton = document.getElementById('permissionButton');
const connectionsEnabled = document.getElementById('connectionsEnabled');

function edgeLabel(edge) {
  return {
    top: 'Above',
    left: 'Left',
    right: 'Right',
    bottom: 'Below',
  }[edge] || edge;
}

function selectedTarget() {
  if (!currentState || !selectedTargetId) {
    return null;
  }
  return (currentState.targets || []).find(target => target.targetId === selectedTargetId) || null;
}

function networkLabel(path) {
  if (!path) {
    return 'Unknown path';
  }
  const iface = path.interfaceName ? ` ${path.interfaceName}` : '';
  const address = path.remoteAddress ? ` · ${path.remoteAddress}` : '';
  return `${path.label || 'Unknown'}${iface}${address}`;
}

function networkSummaryLabel(path) {
  if (!path) {
    return 'Unknown path';
  }
  const iface = path.interfaceName ? ` ${path.interfaceName}` : '';
  return `${path.label || 'Unknown'}${iface}`;
}

function roundMs(value) {
  return `${Math.round(Number(value || 0))} ms`;
}

function latencyLabel(metrics) {
  const latency = metrics && metrics.latency;
  if (!latency || !latency.sent) {
    return 'RTT measuring';
  }
  if (latency.stale && latency.rttMs === null) {
    return 'RTT no reply';
  }
  if (latency.rttMs !== null && latency.rttMs !== undefined) {
    return `${latency.stale ? 'RTT stale' : 'RTT'} ${roundMs(latency.rttMs)}`;
  }
  if (latency.pending) {
    return 'RTT pending';
  }
  return 'RTT measuring';
}

function metricsLabel(metrics) {
  const parts = [latencyLabel(metrics)];
  const latency = metrics && metrics.latency;
  const input = metrics && metrics.input;
  if (latency && latency.missed) {
    parts.push(`${latency.missed} missed`);
  }
  if (input && input.eventsPerSecond) {
    parts.push(`${input.eventsPerSecond}/s input`);
  }
  return parts.join(' · ');
}

function normalizeBounds(bounds, fallback) {
  const source = bounds || fallback || {};
  return {
    x: Number(source.x || 0),
    y: Number(source.y || 0),
    width: Math.max(1, Number(source.width || 0)),
    height: Math.max(1, Number(source.height || 0)),
  };
}

function boundsRight(bounds) {
  return bounds.x + bounds.width;
}

function boundsBottom(bounds) {
  return bounds.y + bounds.height;
}

function localDisplays() {
  if (currentState.local.displays && currentState.local.displays.length) {
    return currentState.local.displays;
  }
  return [{
    id: 'main',
    name: 'Display 1',
    bounds: {
      x: 0,
      y: 0,
      width: currentState.local.screen.width,
      height: currentState.local.screen.height,
    },
    primary: true,
  }];
}

function displayForId(displays, displayId) {
  const id = String(displayId || '');
  return (displays || []).find(display => String(display.id) === id) || null;
}

function defaultDisplayForEdge(edge, displays) {
  const candidates = (displays || []).slice();
  if (!candidates.length) {
    return null;
  }

  return candidates.sort((a, b) => {
    const aBounds = normalizeBounds(a.bounds);
    const bBounds = normalizeBounds(b.bounds);
    if (edge === 'left') {
      return aBounds.x - bBounds.x || aBounds.y - bBounds.y;
    }
    if (edge === 'right') {
      return boundsRight(bBounds) - boundsRight(aBounds) || aBounds.y - bBounds.y;
    }
    if (edge === 'top') {
      return aBounds.y - bBounds.y || aBounds.x - bBounds.x;
    }
    if (edge === 'bottom') {
      return boundsBottom(bBounds) - boundsBottom(aBounds) || aBounds.x - bBounds.x;
    }
    return 0;
  })[0];
}

function defaultSourceDisplayIdForEdge(edge) {
  const display = defaultDisplayForEdge(edge, localDisplays());
  return display ? String(display.id) : null;
}

function displaySize(bounds) {
  return `${Math.round(bounds.width)} x ${Math.round(bounds.height)}`;
}

function placementLabel(target) {
  if (!target || !target.edge) {
    return 'Not placed';
  }
  const source = displayForId(localDisplays(), target.sourceDisplayId);
  const sourceName = source && source.name ? source.name : 'local display';
  return `${edgeLabel(target.edge)} of ${sourceName}`;
}

function targetGroupsByHost(targets) {
  const groups = {};
  (targets || []).forEach(target => {
    if (!groups[target.hostId]) {
      groups[target.hostId] = [];
    }
    groups[target.hostId].push(target);
  });
  return groups;
}

function placementTargetForHost(hostId, edge) {
  const group = targetGroupsByHost(currentState.targets || [])[hostId] || [];
  const member = boundaryMemberForEdge(
    group.map(target => ({ id: target.targetId, rect: target.bounds })),
    edge
  );
  return member && group.find(target => target.targetId === member.id);
}

function placementSourceDisplayForEdge(edge) {
  const member = boundaryMemberForEdge(
    localDisplays().map(display => ({
      id: String(display.id),
      rect: display.bounds,
    })),
    edgeOpposite(edge)
  );
  return member && displayForId(localDisplays(), member.id);
}

function displayUnion(items) {
  if (!items.length) {
    return { x: 0, y: 0, width: 1, height: 1 };
  }
  const minX = Math.min.apply(null, items.map(item => item.drawBounds.x));
  const minY = Math.min.apply(null, items.map(item => item.drawBounds.y));
  const maxX = Math.max.apply(null, items.map(item => boundsRight(item.drawBounds)));
  const maxY = Math.max.apply(null, items.map(item => boundsBottom(item.drawBounds)));
  return {
    x: minX,
    y: minY,
    width: Math.max(1, maxX - minX),
    height: Math.max(1, maxY - minY),
  };
}

function appendRemoteGroup(items, group, offsetX, offsetY, placed) {
  group.forEach(target => {
    const targetBounds = normalizeBounds(target.bounds);
    items.push({
      key: target.targetId,
      kind: 'remote',
      hostId: target.hostId,
      machineKey: `remote:${target.hostId}`,
      machineLabel: target.peerNickname,
      memberId: target.targetId,
      targetId: target.targetId,
      label: target.displayName,
      meta: displaySize(targetBounds),
      selected: target.targetId === selectedTargetId,
      placed,
      drawBounds: {
        x: targetBounds.x + offsetX,
        y: targetBounds.y + offsetY,
        width: targetBounds.width,
        height: targetBounds.height,
      },
    });
  });
}

function elementRect(element) {
  const rect = element.getBoundingClientRect();
  return {
    x: rect.left,
    y: rect.top,
    width: rect.width,
    height: rect.height,
  };
}

function tilesByMachine() {
  const groups = {};
  Array.from(displayMap.querySelectorAll('.display-tile')).forEach(element => {
    const machineKey = element.dataset.machineKey;
    if (!groups[machineKey]) {
      groups[machineKey] = {
        groupId: machineKey,
        kind: element.dataset.kind,
        elements: [],
        members: [],
      };
    }
    groups[machineKey].elements.push(element);
    groups[machineKey].members.push({
      id: element.dataset.memberId,
      element,
      rect: elementRect(element),
    });
    groups[machineKey].machineLabel = element.dataset.machineLabel;
  });
  return groups;
}

function dropSourceGroups(kind, machineKey) {
  const groups = tilesByMachine();
  return Object.keys(groups)
    .filter(key => key !== machineKey)
    .map(key => groups[key])
    .filter(group => kind === 'remote' ? group.kind === 'local' : group.kind === 'remote');
}

function clearDropTarget() {
  if (!displayMap) {
    return;
  }
  displayMap.querySelectorAll('.drop-target').forEach(element => {
    element.classList.remove('drop-target');
    delete element.dataset.dropEdge;
  });
}

function showDropTarget(candidate, sourceGroups) {
  clearDropTarget();
  if (!candidate) {
    return;
  }
  const sourceGroup = sourceGroups.find(group =>
    String(group.groupId) === candidate.sourceGroupId);
  if (!sourceGroup) {
    return;
  }
  sourceGroup.elements.forEach(element => element.classList.add('drop-target'));
  const outline = Array.from(displayMap.querySelectorAll('.display-machine-group'))
    .find(element => element.dataset.machineKey === sourceGroup.groupId);
  if (outline) {
    outline.classList.add('drop-target');
  }
  const sourceMember = sourceGroup.members.find(member =>
    String(member.id) === candidate.sourceMemberId);
  if (sourceMember) {
    sourceMember.element.classList.add('drop-target');
    sourceMember.element.dataset.dropEdge = candidate.edge;
  }
  const handle = sourceMember && sourceMember.element
    .querySelector(`.edge-handle.${candidate.edge}`);
  if (handle) {
    handle.classList.add('drop-target');
  }
}

function updateSelectedTarget(targetId, tile) {
  selectedTargetId = targetId;
  displayMap.querySelectorAll('.display-tile.remote').forEach(remoteTile => {
    remoteTile.classList.toggle('selected', remoteTile === tile);
  });
  renderPeers();
}

function beginDisplayDrag(event, item, tile) {
  if (event.button !== 0 ||
      event.isPrimary === false ||
      activeDisplayDrag ||
      (event.target.closest && event.target.closest('.edge-handle'))) {
    return;
  }

  const groups = tilesByMachine();
  const movingGroup = groups[item.machineKey];
  const sources = dropSourceGroups(item.kind, item.machineKey);
  if (!movingGroup || !sources.length) {
    return;
  }

  event.preventDefault();
  if (item.kind === 'remote') {
    updateSelectedTarget(item.targetId, tile);
  }
  const outline = Array.from(displayMap.querySelectorAll('.display-machine-group'))
    .find(element => element.dataset.machineKey === item.machineKey);
  const visuals = movingGroup.elements.concat(outline ? [outline] : []);
  activeDisplayDrag = {
    pointerId: event.pointerId,
    item,
    tile,
    movingGroup,
    sources,
    visuals,
    startX: event.clientX,
    startY: event.clientY,
    candidate: null,
    moved: false,
  };

  visuals.forEach(element => element.classList.add('dragging'));
  displayMap.classList.add('dragging');
  tile.setPointerCapture(event.pointerId);
  if (setupMeta) {
    setupMeta.textContent = `Moving ${item.machineLabel} as one display group.`;
  }
}

function moveDisplayDrag(event) {
  const drag = activeDisplayDrag;
  if (!drag || drag.pointerId !== event.pointerId) {
    return;
  }

  const deltaX = event.clientX - drag.startX;
  const deltaY = event.clientY - drag.startY;
  drag.moved = drag.moved || Math.abs(deltaX) > 3 || Math.abs(deltaY) > 3;

  const movingMembers = drag.movingGroup.members.map(member => ({
    id: member.id,
    rect: {
      x: member.rect.x + deltaX,
      y: member.rect.y + deltaY,
      width: member.rect.width,
      height: member.rect.height,
    },
  }));
  drag.candidate = findGroupSnapCandidate(
    movingMembers,
    drag.sources,
    DISPLAY_SNAP_DISTANCE
  );
  const snapX = drag.candidate ? drag.candidate.deltaX : 0;
  const snapY = drag.candidate ? drag.candidate.deltaY : 0;
  const transform = `translate(${deltaX + snapX}px, ${deltaY + snapY}px)`;

  drag.visuals.forEach(element => {
    element.style.transform = transform;
  });
  showDropTarget(drag.candidate, drag.sources);
  if (setupMeta && drag.candidate) {
    const sourceGroup = drag.sources.find(group =>
      String(group.groupId) === drag.candidate.sourceGroupId);
    const destination = sourceGroup && sourceGroup.machineLabel
      ? sourceGroup.machineLabel
      : 'the other Mac';
    setupMeta.textContent =
      `Docking ${drag.item.machineLabel} ${edgeLabel(drag.candidate.edge).toLowerCase()} ${destination}.`;
  }
}

function endDisplayDrag(event, cancelled) {
  const drag = activeDisplayDrag;
  if (!drag || drag.pointerId !== event.pointerId) {
    return;
  }

  activeDisplayDrag = null;
  drag.visuals.forEach(element => element.classList.remove('dragging'));
  displayMap.classList.remove('dragging');
  clearDropTarget();

  if (!cancelled && drag.moved && drag.candidate) {
    const payload = drag.item.kind === 'remote'
      ? {
        targetId: drag.candidate.movingMemberId,
        edge: drag.candidate.edge,
        sourceDisplayId: drag.candidate.sourceMemberId,
        crossOffset: drag.candidate.crossOffset,
      }
      : {
        targetId: drag.candidate.sourceMemberId,
        edge: edgeOpposite(drag.candidate.edge),
        sourceDisplayId: drag.candidate.movingMemberId,
        crossOffset: -drag.candidate.crossOffset,
      };
    selectedTargetId = payload.targetId;
    ipcRenderer.send('peer:place', payload);
    return;
  }

  render();
}

function renderDisplayMap() {
  if (!displayMap || !currentState) {
    return;
  }

  const target = selectedTarget();
  const displays = localDisplays();
  const items = displays.map((display, index) => {
    const bounds = normalizeBounds(display.bounds);
    return {
      key: `local-${display.id || index}`,
      kind: 'local',
      machineKey: 'local',
      machineLabel: 'This Mac',
      memberId: String(display.id || 'main'),
      displayId: String(display.id || 'main'),
      label: display.name || `Display ${index + 1}`,
      meta: displaySize(bounds),
      placed: true,
      drawBounds: bounds,
    };
  });
  const localUnion = displayUnion(items);
  const parkingGap = Math.max(160, localUnion.width * 0.08);
  let parkingX = boundsRight(localUnion) + parkingGap;

  const groups = targetGroupsByHost(currentState.targets || []);
  Object.keys(groups).forEach(hostId => {
    const group = groups[hostId];
    const anchor = group
      .filter(target => target.edge)
      .sort((a, b) => Number(b.layoutUpdatedAt || 0) - Number(a.layoutUpdatedAt || 0))[0];
    if (!anchor) {
      const groupItems = group.map(target => ({
        drawBounds: normalizeBounds(target.bounds),
      }));
      const groupUnion = displayUnion(groupItems);
      const offsetX = parkingX - groupUnion.x;
      const offsetY = localUnion.y + ((localUnion.height - groupUnion.height) / 2) - groupUnion.y;
      appendRemoteGroup(items, group, offsetX, offsetY, false);
      parkingX += groupUnion.width + parkingGap;
      return;
    }

    const groupItems = group.map(target => ({
      drawBounds: normalizeBounds(target.bounds),
    }));
    const groupUnion = displayUnion(groupItems);
    const placedGroup = adjacentRect(
      localUnion,
      groupUnion,
      anchor.edge,
      anchor.crossOffset
    );
    const offsetX = placedGroup.x - groupUnion.x;
    const offsetY = placedGroup.y - groupUnion.y;
    appendRemoteGroup(items, group, offsetX, offsetY, true);
  });

  const union = displayUnion(items);
  const padX = Math.max(120, union.width * 0.08);
  const padY = Math.max(80, union.height * 0.12);
  const view = {
    x: union.x - padX,
    y: union.y - padY,
    width: union.width + (padX * 2),
    height: union.height + (padY * 2),
  };

  displayMap.innerHTML = '';
  displayMap.classList.toggle('ready', !!target);
  const machineGroups = {};
  items.forEach(item => {
    if (!machineGroups[item.machineKey]) {
      machineGroups[item.machineKey] = {
        kind: item.kind,
        label: item.machineLabel,
        items: [],
      };
    }
    machineGroups[item.machineKey].items.push(item);
  });
  Object.keys(machineGroups).forEach(machineKey => {
    const group = machineGroups[machineKey];
    const bounds = displayUnion(group.items);
    const outline = document.createElement('div');
    outline.className = `display-machine-group ${group.kind}`;
    outline.dataset.machineKey = machineKey;
    outline.style.left = `${((bounds.x - view.x) / view.width) * 100}%`;
    outline.style.top = `${((bounds.y - view.y) / view.height) * 100}%`;
    outline.style.width = `${(bounds.width / view.width) * 100}%`;
    outline.style.height = `${(bounds.height / view.height) * 100}%`;
    outline.innerHTML = '<span class="machine-label"></span>';
    outline.querySelector('.machine-label').textContent = group.label;
    displayMap.appendChild(outline);
  });
  items.forEach(item => {
    const tile = document.createElement('div');
    tile.className = `display-tile ${item.kind}${item.selected ? ' selected' : ''}${item.placed === false ? ' unplaced' : ''}`;
    tile.title = `${item.machineLabel} · ${item.label} · ${item.meta}`;
    tile.dataset.machineKey = item.machineKey;
    tile.dataset.memberId = item.memberId;
    tile.dataset.kind = item.kind;
    tile.dataset.machineLabel = item.machineLabel;
    tile.style.left = `${((item.drawBounds.x - view.x) / view.width) * 100}%`;
    tile.style.top = `${((item.drawBounds.y - view.y) / view.height) * 100}%`;
    tile.style.width = `${(item.drawBounds.width / view.width) * 100}%`;
    tile.style.height = `${(item.drawBounds.height / view.height) * 100}%`;
    tile.innerHTML = `
      <span class="display-name"></span>
      <span class="display-meta"></span>
    `;
    tile.querySelector('.display-name').textContent = item.label;
    tile.querySelector('.display-meta').textContent = item.meta;
    if (item.kind === 'remote') {
      tile.dataset.targetId = item.targetId;
      tile.addEventListener('click', () => {
        if (selectedTargetId === item.targetId) {
          return;
        }
        selectedTargetId = item.targetId;
        render();
      });
    }
    if (item.kind === 'local') {
      tile.dataset.displayId = item.displayId;
    }
    tile.addEventListener('pointerdown', event => beginDisplayDrag(event, item, tile));
    tile.addEventListener('pointermove', moveDisplayDrag);
    tile.addEventListener('pointerup', event => endDisplayDrag(event, false));
    tile.addEventListener('pointercancel', event => endDisplayDrag(event, true));
    if (item.kind === 'local' && target) {
      ['left', 'right', 'top', 'bottom'].forEach(edge => {
        const placementSource = placementSourceDisplayForEdge(edge);
        if (!placementSource || String(placementSource.id) !== item.displayId) {
          return;
        }
        const handle = document.createElement('button');
        handle.className = `edge-handle ${edge}`;
        handle.type = 'button';
        handle.title = `${edgeLabel(edge)} of ${item.label}`;
        handle.setAttribute('aria-label', `${edgeLabel(edge)} of ${item.label}`);
        handle.addEventListener('click', event => {
          event.stopPropagation();
          const placementTarget = placementTargetForHost(target.hostId, edge);
          if (!placementTarget) {
            return;
          }
          selectedTargetId = placementTarget.targetId;
          ipcRenderer.send('peer:place', {
            targetId: placementTarget.targetId,
            edge,
            sourceDisplayId: String(placementSource.id),
            crossOffset: 0,
          });
        });
        tile.appendChild(handle);
      });
    }
    displayMap.appendChild(tile);
  });
}

function renderPeers() {
  const peers = currentState && (currentState.peers || []);
  const targets = currentState && (currentState.targets || []);
  if (currentState && currentState.connectionsEnabled === false) {
    peerList.innerHTML = '<div class="empty paused">Nearby connections are paused.</div>';
    return;
  }
  if (!peers || peers.length === 0) {
    peerList.innerHTML = '<div class="empty">Launch OpenMouse on another Mac. When it appears, choose Pair.</div>';
    return;
  }

  peerList.innerHTML = '';
  const groups = targetGroupsByHost(targets);
  peers.forEach(peer => {
    const hostId = peer.hostId;
    const group = groups[hostId] || [];
    const placedTarget = group
      .filter(target => target.edge)
      .sort((a, b) => Number(b.layoutUpdatedAt || 0) - Number(a.layoutUpdatedAt || 0))[0];
    const target = placedTarget || group[0] || null;
    const selected = group.some(item => item.targetId === selectedTargetId);
    const card = document.createElement('div');
    card.className = `peer ${selected ? 'selected' : ''}`;
    card.innerHTML = `
      <div class="peer-head">
        <span class="peer-name"></span>
        <span class="badge"></span>
      </div>
      <div class="peer-meta"></div>
      <div class="peer-path"></div>
      <div class="pairing-code hidden" aria-live="polite"></div>
      <div class="peer-actions"></div>
    `;
    const badge = card.querySelector('.badge');
    const meta = card.querySelector('.peer-meta');
    const path = card.querySelector('.peer-path');
    const code = card.querySelector('.pairing-code');
    const actions = card.querySelector('.peer-actions');
    card.querySelector('.peer-name').textContent = peer.nickname;

    const addAction = (label, channel, className, disabled) => {
      const button = document.createElement('button');
      button.type = 'button';
      button.className = `small-button ${className || ''}`.trim();
      button.textContent = label;
      button.disabled = !!disabled;
      button.addEventListener('click', event => {
        event.stopPropagation();
        ipcRenderer.send(channel, hostId);
      });
      actions.appendChild(button);
    };

    if (peer.identityChanged) {
      badge.textContent = 'Blocked';
      badge.classList.add('warning');
      meta.textContent = 'This Mac’s secure identity changed.';
      path.textContent = 'Forget it, then pair again only if you recognize this Mac.';
      addAction('Forget', 'peer:forget', 'secondary');
    } else if (peer.pairing && peer.pairing.state === 'verify') {
      badge.textContent = 'Verify';
      badge.classList.add('warning');
      meta.textContent = peer.pairing.localConfirmed
        ? 'Confirmed here. Waiting for the other Mac.'
        : 'Confirm only if this code matches on both Macs.';
      path.textContent = `Safety ID · ${peer.shortFingerprint}`;
      code.textContent = String(peer.pairing.code || '').replace(/(\d{3})(\d{3})/, '$1 $2');
      code.classList.remove('hidden');
      addAction(
        peer.pairing.localConfirmed ? 'Confirmed' : 'Confirm',
        'peer:pair:confirm',
        'primary',
        peer.pairing.localConfirmed
      );
      addAction('Reject', 'peer:pair:reject', 'secondary');
    } else if (peer.pairing && peer.pairing.state === 'connecting') {
      badge.textContent = 'Pairing';
      meta.textContent = 'Starting a secure pairing request…';
      path.textContent = `Safety ID · ${peer.shortFingerprint}`;
      addAction('Cancel', 'peer:pair:reject', 'secondary');
    } else if (peer.paired) {
      card.classList.add('selectable');
      const displaySummary = group.length === 1
        ? `${target.displayName} · ${displaySize(target.screen)}`
        : `${group.length} displays · ${group.map(item => item.displayName).join(' + ')}`;
      badge.textContent = peer.connected ? 'Connected' : 'Paired';
      badge.classList.toggle('connected', peer.connected);
      meta.textContent = `${displaySummary} · ${placementLabel(placedTarget)}`;
      path.textContent = peer.connected
        ? `${networkSummaryLabel(peer.networkPath)} · ${metricsLabel(peer.metrics)}`
        : peer.connectionEnabled ? 'Secure reconnect is enabled.' : 'Disconnected on this Mac.';
      path.title = peer.connected
        ? `${networkLabel(peer.networkPath)} · ${metricsLabel(peer.metrics)}`
        : '';
      addAction(
        peer.connected ? 'Disconnect' : 'Connect',
        peer.connected ? 'peer:disconnect' : 'peer:connect',
        peer.connected ? 'secondary' : 'primary'
      );
      addAction('Forget', 'peer:forget', 'quiet');
      card.addEventListener('click', () => {
        if (!target) {
          return;
        }
        selectedTargetId = target.targetId;
        render();
      });
    } else {
      const updateRequired = !peer.fingerprint;
      badge.textContent = updateRequired
        ? 'Update needed'
        : peer.pairing && peer.pairing.state === 'failed'
          ? 'Not paired'
          : 'Available';
      if (updateRequired || (peer.pairing && peer.pairing.state === 'failed')) {
        badge.classList.add('warning');
      }
      if (updateRequired) {
        meta.textContent = 'Update OpenMouse on this Mac before pairing.';
      } else if (peer.pairing && peer.pairing.message) {
        meta.textContent = peer.pairing.message;
      } else {
        meta.textContent = 'Not paired. No input or clipboard is shared.';
      }
      path.textContent = updateRequired
        ? 'Secure pairing is unavailable on its current version.'
        : `Safety ID · ${peer.shortFingerprint}`;
      if (!updateRequired) {
        addAction('Pair', 'peer:pair', 'primary');
      }
    }
    peerList.appendChild(card);
  });
}

function render() {
  if (!currentState) {
    return;
  }

  const connected = currentState.peers.filter(peer => peer.connected).length;
  if (connectionsEnabled) {
    connectionsEnabled.checked = currentState.connectionsEnabled !== false;
  }
  if (localName) {
    localName.textContent = currentState.local.nickname;
  }
  if (localMeta) {
    localMeta.textContent = `${currentState.local.screen.width} x ${currentState.local.screen.height}`;
  }
  if (document.activeElement !== nickname) {
    nickname.value = currentState.local.nickname;
  }
  if (releaseShortcut && document.activeElement !== releaseShortcut) {
    releaseShortcut.value = currentState.releaseShortcut || DEFAULT_RELEASE_SHORTCUT;
  }
  const receivingPeer = currentState.peers.find(peer => peer.hostId === currentState.inboundControlPeerId);
  const pairingPeer = currentState.peers.find(peer =>
    peer.pairing && (peer.pairing.state === 'verify' || peer.pairing.state === 'connecting'));
  const availablePeers = currentState.peers.filter(peer => !peer.paired && !peer.identityChanged);
  if (currentState.connectionsEnabled === false) {
    statusLine.textContent = 'Paused. OpenMouse is not connecting to nearby Macs.';
  } else if (pairingPeer && pairingPeer.pairing.state === 'verify') {
    statusLine.textContent = `Compare the six-digit code with ${pairingPeer.nickname}.`;
  } else if (pairingPeer) {
    statusLine.textContent = `Starting secure pairing with ${pairingPeer.nickname}…`;
  } else if (currentState.permissions && currentState.permissions.accessibility === false) {
    statusLine.textContent = 'Accessibility permission is needed.';
  } else if (currentState.mode === 'receiving' && receivingPeer) {
    statusLine.textContent = `${receivingPeer.nickname} is controlling this Mac.`;
  } else if (currentState.mode === 'offscreen') {
    statusLine.textContent = 'Controlling another Mac.';
  } else if (selectedTarget() && selectedTarget().permissions && selectedTarget().permissions.accessibility === false) {
    statusLine.textContent = `${selectedTarget().peerNickname} needs Accessibility permission.`;
  } else if (selectedTarget()) {
    statusLine.textContent = `Place ${selectedTarget().peerNickname}.`;
  } else {
    statusLine.textContent = connected
      ? `${connected} Mac${connected === 1 ? '' : 's'} connected.`
      : availablePeers.length
        ? 'Choose a nearby Mac to pair.'
        : 'Looking for nearby Macs...';
  }
  const connectedPeers = currentState.peers.filter(peer => peer.connected);
  if (currentState.connectionsEnabled === false) {
    networkLine.textContent = 'Connections paused · No discovery or remote control traffic';
  } else if (connectedPeers.length) {
    networkLine.textContent = connectedPeers
      .map(peer => `${peer.nickname}: ${networkLabel(peer.networkPath)} · ${metricsLabel(peer.metrics)}`)
      .join(' · ');
  } else {
    networkLine.textContent = `Discovery UDP ${currentState.discoveryPort} · Control TCP ${currentState.tcpPort}`;
  }
  if (permissionPanel) {
    const needsAccessibility = currentState.permissions && currentState.permissions.accessibility === false;
    permissionPanel.classList.toggle('hidden', !needsAccessibility);
  }
  if (shortcutTest) {
    const target = selectedTarget();
    shortcutTest.disabled = currentState.connectionsEnabled === false ||
      !(target && target.connected);
  }
  if (setupTitle && setupMeta) {
    const target = selectedTarget();
    const group = target && targetGroupsByHost(currentState.targets || [])[target.hostId];
    const placedTarget = group && group
      .filter(item => item.edge)
      .sort((a, b) => Number(b.layoutUpdatedAt || 0) - Number(a.layoutUpdatedAt || 0))[0];
    setupTitle.textContent = 'Arrangement';
    setupMeta.textContent = target
      ? `${target.peerNickname} · ${group.length} display${group.length === 1 ? '' : 's'} · ${placementLabel(placedTarget)}`
      : availablePeers.length
        ? 'Pair a Mac before arranging its displays.'
        : 'Select a paired display.';
  }
  renderPeers();
  renderDisplayMap();
}

ipcRenderer.on('state', (event, state) => {
  currentState = state;
  const targets = state.targets || [];
  if (!targets.some(target => target.targetId === selectedTargetId)) {
    selectedTargetId = targets.length ? targets[0].targetId : null;
  }
  if (!activeDisplayDrag) {
    render();
  }
});

nickname.addEventListener('change', () => {
  ipcRenderer.send('nickname:set', nickname.value);
});

nickname.addEventListener('keydown', event => {
  if (event.key === 'Enter') {
    nickname.blur();
  }
});

releaseShortcut.addEventListener('change', () => {
  ipcRenderer.send('releaseShortcut:set', releaseShortcut.value);
});

releaseShortcut.addEventListener('keydown', event => {
  if (event.key === 'Enter') {
    releaseShortcut.blur();
  }
});

connectionsEnabled.addEventListener('change', () => {
  ipcRenderer.send('connections:set', connectionsEnabled.checked);
});

releaseReset.addEventListener('click', () => {
  releaseShortcut.value = DEFAULT_RELEASE_SHORTCUT;
  ipcRenderer.send('releaseShortcut:set', releaseShortcut.value);
});

shortcutTest.addEventListener('click', () => {
  const target = selectedTarget();
  if (!target) {
    return;
  }
  ipcRenderer.send('shortcut:test', { hostId: target.hostId });
});

permissionButton.addEventListener('click', () => {
  ipcRenderer.send('permission:accessibility:request');
});

ipcRenderer.send('state:get');
