const themeStorageKey = "windows-os-website-desktop-style-v1";

const windows = new Map(
  Array.from(document.querySelectorAll("[data-window]")).map((windowEl) => [
    windowEl.dataset.window,
    windowEl,
  ]),
);

const desktopStage = document.querySelector(".desktop-stage");
const taskbarApps = document.querySelector(".taskbar-apps");
const startButton = document.querySelector("[data-start-button]");
const startMenu = document.querySelector("[data-start-menu]");
const clock = document.querySelector("[data-clock]");
const styleChooser = document.querySelector("[data-style-chooser]");
const settingsTitle = document.querySelector("#settings-title");
const settingsTabButtons = document.querySelectorAll("[data-settings-tab]");
const settingsPanels = document.querySelectorAll("[data-settings-panel]");

let zIndex = 10;
let activeDrag = null;

function clamp(value, min, max) {
  return Math.min(Math.max(value, min), max);
}

function hideStyleChooser() {
  if (!styleChooser) return;
  styleChooser.classList.add("is-hidden");
  styleChooser.setAttribute("hidden", "");
  styleChooser.setAttribute("aria-hidden", "true");
}

function showStyleChooser() {
  if (!styleChooser) return;
  styleChooser.removeAttribute("hidden");
  styleChooser.removeAttribute("aria-hidden");
  styleChooser.classList.remove("is-hidden");
}

function getWindowTitle(windowId) {
  const windowEl = windows.get(windowId);
  return (
    windowEl?.querySelector(".window-titlebar span")?.textContent || windowId
  );
}

function makeTaskButton(windowId) {
  const button = document.createElement("button");
  button.type = "button";
  button.dataset.taskWindow = windowId;
  button.textContent = getWindowTitle(windowId);
  button.addEventListener("click", () => openWindow(windowId));
  return button;
}

function getTaskButton(windowId) {
  let button = taskbarApps?.querySelector(`[data-task-window="${windowId}"]`);

  if (!button && taskbarApps) {
    button = makeTaskButton(windowId);
    taskbarApps.appendChild(button);
  }

  return button;
}

function setActiveTask(windowId) {
  document.querySelectorAll("[data-task-window]").forEach((button) => {
    button.classList.toggle(
      "is-active",
      button.dataset.taskWindow === windowId,
    );
  });
}

function openWindow(windowId) {
  const windowEl = windows.get(windowId);
  if (!windowEl) return;

  windowEl.classList.remove("is-hidden");
  windowEl.style.zIndex = String(++zIndex);

  const taskButton = getTaskButton(windowId);
  taskButton?.classList.remove("is-hidden");
  setActiveTask(windowId);
  startMenu?.classList.add("is-hidden");

  window.setTimeout(() => constrainWindow(windowEl), 0);
}

function closeWindow(windowId) {
  const windowEl = windows.get(windowId);
  if (!windowEl) return;

  restoreWindowSize(windowEl);
  windowEl.classList.add("is-hidden");

  const taskButton = getTaskButton(windowId);
  taskButton?.classList.add("is-hidden");
  taskButton?.classList.remove("is-active");
}

function minimizeWindow(windowId) {
  const windowEl = windows.get(windowId);
  if (!windowEl) return;

  windowEl.classList.add("is-hidden");
  getTaskButton(windowId)?.classList.remove("is-active");
}

function toggleWindowSize(windowId) {
  const windowEl = windows.get(windowId);
  if (!windowEl) return;

  if (windowEl.classList.contains("is-maximized")) {
    restoreWindowSize(windowEl);
    openWindow(windowId);
    return;
  }

  expandWindowSize(windowEl);
  openWindow(windowId);
}

function getWindowPosition(windowEl) {
  if (!desktopStage) return null;

  const stageRect = desktopStage.getBoundingClientRect();
  const windowRect = windowEl.getBoundingClientRect();

  return {
    left: windowRect.left - stageRect.left,
    top: windowRect.top - stageRect.top,
    width: windowRect.width,
    height: windowRect.height,
    stageWidth: stageRect.width,
    stageHeight: stageRect.height,
  };
}

function canPositionWindow(windowEl) {
  return (
    desktopStage &&
    windowEl &&
    !windowEl.classList.contains("is-hidden") &&
    !windowEl.classList.contains("is-maximized") &&
    getComputedStyle(windowEl).position === "absolute"
  );
}

function setWindowPosition(windowEl, left, top) {
  windowEl.style.right = "auto";
  windowEl.style.bottom = "auto";
  windowEl.style.left = `${Math.round(left)}px`;
  windowEl.style.top = `${Math.round(top)}px`;
}

function setWindowBounds(windowEl, left, top, width, height) {
  setWindowPosition(windowEl, left, top);
  windowEl.style.width = `${Math.round(width)}px`;
  windowEl.style.height = `${Math.round(height)}px`;
}

function rememberWindowSize(windowEl) {
  windowEl.dataset.restoreStyle = JSON.stringify({
    left: windowEl.style.left,
    top: windowEl.style.top,
    right: windowEl.style.right,
    bottom: windowEl.style.bottom,
    width: windowEl.style.width,
    height: windowEl.style.height,
  });
}

function restoreWindowSize(windowEl) {
  if (!windowEl.classList.contains("is-maximized")) return;

  let restoreStyle = null;

  try {
    restoreStyle = JSON.parse(windowEl.dataset.restoreStyle || "null");
  } catch {
    restoreStyle = null;
  }

  windowEl.classList.remove("is-maximized");

  if (restoreStyle) {
    windowEl.style.left = restoreStyle.left || "";
    windowEl.style.top = restoreStyle.top || "";
    windowEl.style.right = restoreStyle.right || "";
    windowEl.style.bottom = restoreStyle.bottom || "";
    windowEl.style.width = restoreStyle.width || "";
    windowEl.style.height = restoreStyle.height || "";
  } else {
    windowEl.style.width = "";
    windowEl.style.height = "";
  }

  delete windowEl.dataset.restoreStyle;
}

function expandWindowSize(windowEl) {
  if (!desktopStage || getComputedStyle(windowEl).position !== "absolute") {
    windowEl.classList.add("is-maximized");
    return;
  }

  const position = getWindowPosition(windowEl);
  if (!position) return;

  rememberWindowSize(windowEl);

  const stageWidthLimit = Math.max(position.width, position.stageWidth * 0.88);
  const stageHeightLimit = Math.max(
    position.height,
    position.stageHeight * 0.88,
  );
  const nextWidth = Math.min(position.width * 1.7, stageWidthLimit);
  const nextHeight = Math.min(position.height * 1.65, stageHeightLimit);
  const nextLeft = clamp(
    position.left - (nextWidth - position.width) / 2,
    0,
    Math.max(0, position.stageWidth - nextWidth),
  );
  const nextTop = clamp(
    position.top - (nextHeight - position.height) / 2,
    0,
    Math.max(0, position.stageHeight - nextHeight),
  );

  setWindowBounds(windowEl, nextLeft, nextTop, nextWidth, nextHeight);
  windowEl.classList.add("is-maximized");
}

function constrainWindow(windowEl) {
  if (!canPositionWindow(windowEl)) return;

  const position = getWindowPosition(windowEl);
  if (!position) return;

  const maxLeft = Math.max(0, position.stageWidth - position.width);
  const maxTop = Math.max(0, position.stageHeight - position.height);

  setWindowPosition(
    windowEl,
    clamp(position.left, 0, maxLeft),
    clamp(position.top, 0, maxTop),
  );
}

function startWindowDrag(event, windowEl) {
  if (event.button !== 0 || !canPositionWindow(windowEl)) return;
  if (event.target.closest(".window-controls")) return;

  openWindow(windowEl.dataset.window);

  const position = getWindowPosition(windowEl);
  if (!position) return;

  activeDrag = {
    windowEl,
    startX: event.clientX,
    startY: event.clientY,
    startLeft: position.left,
    startTop: position.top,
  };

  setWindowPosition(windowEl, position.left, position.top);
  windowEl.classList.add("is-dragging");
  document.body.classList.add("is-dragging-window");
  event.preventDefault();
}

function moveWindowDrag(event) {
  if (!activeDrag || !desktopStage) return;

  const { windowEl, startX, startY, startLeft, startTop } = activeDrag;
  const position = getWindowPosition(windowEl);
  if (!position) return;

  setWindowPosition(
    windowEl,
    clamp(
      startLeft + event.clientX - startX,
      0,
      Math.max(0, position.stageWidth - position.width),
    ),
    clamp(
      startTop + event.clientY - startY,
      0,
      Math.max(0, position.stageHeight - position.height),
    ),
  );
}

function stopWindowDrag() {
  if (!activeDrag) return;

  activeDrag.windowEl.classList.remove("is-dragging");
  document.body.classList.remove("is-dragging-window");
  constrainWindow(activeDrag.windowEl);
  activeDrag = null;
}

function setTheme(theme, shouldStore = true) {
  document.body.dataset.theme = theme;
  updateSettingsChrome(theme);

  if (shouldStore) {
    localStorage.setItem(themeStorageKey, theme);
  }

  document
    .querySelectorAll("[data-theme-choice], [data-style-pick]")
    .forEach((button) => {
      const choice = button.dataset.themeChoice || button.dataset.stylePick;
      const isSelected = choice === theme;
      button.setAttribute("aria-pressed", String(isSelected));
      button.classList.toggle("is-active", isSelected);
    });
}

function updateSettingsChrome(theme) {
  const title = theme === "win10" ? "Settings" : "Control Panel";

  if (settingsTitle) {
    settingsTitle.textContent = title;
  }

  taskbarApps
    ?.querySelector('[data-task-window="settings"]')
    ?.replaceChildren(title);
}

function setSettingsTab(tabName) {
  const allowedTabs = new Set(["system", "info"]);
  const nextTab = allowedTabs.has(tabName) ? tabName : "system";

  settingsTabButtons.forEach((button) => {
    const isSelected = button.dataset.settingsTab === nextTab;
    button.classList.toggle("is-active", isSelected);
    button.setAttribute("aria-selected", String(isSelected));
  });

  settingsPanels.forEach((panel) => {
    panel.classList.toggle(
      "is-hidden",
      panel.dataset.settingsPanel !== nextTab,
    );
  });
}

function chooseStyle(theme) {
  setTheme(theme, true);
  hideStyleChooser();
  window.history.replaceState({}, "", window.location.pathname);
}

function updateClock() {
  if (!clock) return;

  const now = new Date();
  clock.textContent = now.toLocaleTimeString([], {
    hour: "2-digit",
    minute: "2-digit",
  });
}

document.querySelectorAll("[data-open]").forEach((trigger) => {
  trigger.addEventListener("click", () => openWindow(trigger.dataset.open));
});

document.querySelectorAll("[data-close]").forEach((trigger) => {
  trigger.addEventListener("click", () => closeWindow(trigger.dataset.close));
});

document.querySelectorAll("[data-minimize]").forEach((trigger) => {
  trigger.addEventListener("click", () =>
    minimizeWindow(trigger.dataset.minimize),
  );
});

document.querySelectorAll("[data-toggle-size]").forEach((trigger) => {
  trigger.addEventListener("click", () =>
    toggleWindowSize(trigger.dataset.toggleSize),
  );
});

document.querySelectorAll(".desktop-window").forEach((windowEl) => {
  windowEl.addEventListener("mousedown", () =>
    openWindow(windowEl.dataset.window),
  );

  const titlebar = windowEl.querySelector(".window-titlebar");
  titlebar?.addEventListener("pointerdown", (event) =>
    startWindowDrag(event, windowEl),
  );
});

window.addEventListener("pointermove", moveWindowDrag);
window.addEventListener("pointerup", stopWindowDrag);
window.addEventListener("pointercancel", stopWindowDrag);
window.addEventListener("resize", () => {
  windows.forEach((windowEl) => constrainWindow(windowEl));
});

document.querySelectorAll("[data-theme-choice]").forEach((button) => {
  button.addEventListener("click", () => setTheme(button.dataset.themeChoice));
});

settingsTabButtons.forEach((button) => {
  button.addEventListener("click", () =>
    setSettingsTab(button.dataset.settingsTab),
  );
});

document.querySelectorAll("[data-style-pick]").forEach((button) => {
  button.addEventListener("click", () => chooseStyle(button.dataset.stylePick));
});

if (startButton && startMenu) {
  startButton.addEventListener("click", () => {
    startMenu.classList.toggle("is-hidden");
  });

  document.addEventListener("click", (event) => {
    const target = event.target;
    if (!startMenu.contains(target) && !startButton.contains(target)) {
      startMenu.classList.add("is-hidden");
    }
  });
}

document.addEventListener("keydown", (event) => {
  if (event.key === "Escape") {
    startMenu?.classList.add("is-hidden");
  }
});

document.querySelectorAll('a[href^="http"]').forEach((link) => {
  link.setAttribute("target", "_blank");
  link.setAttribute("rel", "noreferrer");
});

const savedTheme = localStorage.getItem(themeStorageKey);
const forceStyleChooser = /[?&]choose(?:[=&]|$)/.test(window.location.href);

if (savedTheme && !forceStyleChooser) {
  setTheme(savedTheme, false);
  hideStyleChooser();
} else {
  setTheme(savedTheme || "win95", false);
  showStyleChooser();
}

setSettingsTab("system");

updateClock();
setInterval(updateClock, 30000);
