/* eslint-disable @typescript-eslint/ban-ts-comment */
// @ts-nocheck

import {
  normalizeEditorTagValue,
  normalizeEditorTags,
  parseImportedEditorTags,
  serializeEditorTags,
} from "./tagUtils";
import {
  confirmDiscardChanges,
  copyTextWithClipboardFallback,
  promptForImportText,
} from "./textTransferUtils";
import { clampFloatingWindowRect } from "./floatingWindowUtils";

const ENTITY_EDITOR_WINDOW_KEY = "entityEditor";
const DEFAULT_WIDTH = 468;
const DEFAULT_HEIGHT = 648;
const MIN_WIDTH = 404;
const MIN_HEIGHT = 468;

function clampWindowRect(layerRect, left, top, width, height) {
  return clampFloatingWindowRect(layerRect, left, top, width, height, MIN_WIDTH, MIN_HEIGHT, DEFAULT_WIDTH, DEFAULT_HEIGHT);
}

export function createEntityEditorWindowController(scope) {
  let initialized = false;
  const uiScope = scope.uiScope || scope;
  const sessionScope = scope.sessionScope || scope;
  const persistedState = typeof sessionScope.getPersistedToolWindowState === "function"
    ? sessionScope.getPersistedToolWindowState(ENTITY_EDITOR_WINDOW_KEY)
    : null;
  const state = {
    visible: persistedState?.visible === true,
    x: Number(persistedState?.x) || 84,
    y: Number(persistedState?.y) || 54,
    width: Number(persistedState?.width) || DEFAULT_WIDTH,
    height: Number(persistedState?.height) || DEFAULT_HEIGHT,
    entityId: "",
    working: null,
    activeTab: "information",
    dirty: false,
    saving: false,
    shellEl: null,
    bodyEl: null,
    titleEl: null,
    subtitleEl: null,
    statusEl: null,
    saveBtnEl: null,
    resizeEl: null,
    nameInputEl: null,
    tabButtonsEl: null,
    informationTabBtnEl: null,
    tagsTabBtnEl: null,
    informationPaneEl: null,
    tagsPaneEl: null,
    typeSelectEl: null,
    layerSelectEl: null,
    factionSelectEl: null,
    spriteSelectEl: null,
    dialogueSelectEl: null,
    descriptionInputEl: null,
    positionValueEl: null,
    tagInputEl: null,
    tagListEl: null,
    nextZIndex: 118,
  };

  function getLayerRect() {
    return uiScope.toolWindowLayerEl?.getBoundingClientRect() || uiScope.editorBodyEl?.getBoundingClientRect() || {
      left: 0,
      top: 0,
      width: DEFAULT_WIDTH,
      height: DEFAULT_HEIGHT,
    };
  }

  function getNpcById(entityId) {
    const normalizedId = String(entityId || "").trim();
    if (!normalizedId) {
      return null;
    }
    return scope.npcOverlays.find((entry) => String(entry?.id || "").trim() === normalizedId) || null;
  }

  function buildWorkingFromNpc(npc) {
    return {
      id: String(npc?.id || ""),
      name: String(npc?.record?.name || ""),
      entityType: scope.normalizeEntityType?.(npc?.record?.entityType || npc?.entityType, "friendly") || "friendly",
      layer: String(Number(npc?.layer) || 0),
      faction: String(npc?.record?.faction || ""),
      spriteId: String(npc?.record?.spriteId || ""),
      dialogueId: String(npc?.record?.dialogueId || ""),
      description: String(npc?.record?.description || ""),
      tags: normalizeEditorTags(npc?.record?.tags),
      position: (Number.isFinite(Number(npc?.x)) && Number.isFinite(Number(npc?.y)) && Number(npc?.x) >= 0 && Number(npc?.y) >= 0)
        ? "(" + Math.floor(Number(npc.x) || 0) + "," + Math.floor(Number(npc.y) || 0) + ")"
        : "unplaced",
    };
  }

  function persistState() {
    if (typeof sessionScope.setPersistedToolWindowState === "function") {
      sessionScope.setPersistedToolWindowState(ENTITY_EDITOR_WINDOW_KEY, {
        visible: state.visible === true,
        mode: "floating",
        x: state.x,
        y: state.y,
        width: state.width,
        height: state.height,
        order: 997,
      });
    }
    scope.persistPopupSessionLayout?.();
    sessionScope.persistPopupSessionLayout?.();
  }

  function focusWindow() {
    if (!state.shellEl || state.visible !== true) {
      return;
    }
    state.nextZIndex += 1;
    state.shellEl.style.zIndex = String(state.nextZIndex);
    state.shellEl.classList.add("is-focused");
  }

  function clearFocus() {
    state.shellEl?.classList.remove("is-focused");
  }

  function applyWindowRect() {
    if (!state.shellEl) {
      return;
    }
    state.shellEl.style.left = Math.round(state.x) + "px";
    state.shellEl.style.top = Math.round(state.y) + "px";
    state.shellEl.style.width = Math.round(state.width) + "px";
    state.shellEl.style.height = Math.round(state.height) + "px";
  }

  function updateStatus(message, isError) {
    if (!state.statusEl) {
      return;
    }
    state.statusEl.textContent = String(message || "");
    state.statusEl.style.color = isError ? "var(--editor-status-error)" : "var(--editor-status-ok)";
  }

  function refreshHeader() {
    if (state.titleEl) {
      state.titleEl.textContent = String(state.working?.name || state.entityId || "Entity").trim() || "Entity";
    }
    if (state.subtitleEl) {
      const position = String(state.working?.position || "unplaced");
      const typeLabel = scope.getEntityTypeLabel?.(state.working?.entityType || "friendly") || "Entity";
      state.subtitleEl.textContent = `${state.entityId || "entity"} | ${typeLabel} | ${position}`;
    }
    if (state.nameInputEl && state.nameInputEl !== document.activeElement) {
      const nextValue = String(state.working?.name || "");
      if (state.nameInputEl.value !== nextValue) {
        state.nameInputEl.value = nextValue;
      }
    }
    if (state.informationTabBtnEl) {
      state.informationTabBtnEl.classList.toggle("is-active", state.activeTab === "information");
      state.informationTabBtnEl.setAttribute("aria-pressed", state.activeTab === "information" ? "true" : "false");
    }
    if (state.tagsTabBtnEl) {
      state.tagsTabBtnEl.classList.toggle("is-active", state.activeTab === "tags");
      state.tagsTabBtnEl.setAttribute("aria-pressed", state.activeTab === "tags" ? "true" : "false");
    }
    state.informationPaneEl?.classList.toggle("hidden", state.activeTab !== "information");
    state.tagsPaneEl?.classList.toggle("hidden", state.activeTab !== "tags");
    if (state.saveBtnEl) {
      state.saveBtnEl.disabled = state.saving || !state.dirty || !state.working;
      state.saveBtnEl.textContent = state.saving ? "Saving..." : "Save";
    }
  }

  function setActiveTab(nextTab) {
    state.activeTab = nextTab === "tags" ? "tags" : "information";
    refreshHeader();
  }

  function markDirty(message = "Unsaved entity changes.") {
    state.dirty = true;
    refreshHeader();
    updateStatus(message, false);
  }

  function populateSelect(selectEl, items, currentValue, placeholderLabel) {
    if (!selectEl) {
      return;
    }
    selectEl.innerHTML = "";
    const placeholderEl = document.createElement("option");
    placeholderEl.value = "";
    placeholderEl.textContent = placeholderLabel;
    selectEl.appendChild(placeholderEl);
    (Array.isArray(items) ? items : []).forEach((item) => {
      const optionEl = document.createElement("option");
      optionEl.value = String(item?.id || "");
      optionEl.textContent = String(item?.name || item?.id || item?.label || item?.value || "");
      selectEl.appendChild(optionEl);
    });
    selectEl.value = String(currentValue || "");
  }

  function refreshFormValues() {
    if (!state.working) {
      return;
    }
    if (state.nameInputEl) {
      state.nameInputEl.value = String(state.working.name || "");
    }
    if (state.typeSelectEl) {
      state.typeSelectEl.value = String(state.working.entityType || "friendly");
    }
    if (state.layerSelectEl) {
      state.layerSelectEl.innerHTML = "";
      scope.roomLayers
        .slice()
        .sort((left, right) => (Number(left?.layer) || 0) - (Number(right?.layer) || 0))
        .forEach((layerEntry) => {
          const optionEl = document.createElement("option");
          optionEl.value = String(Number(layerEntry?.layer) || 0);
          optionEl.textContent = scope.getLayerDisplayName(layerEntry);
          state.layerSelectEl.appendChild(optionEl);
        });
      state.layerSelectEl.value = String(state.working.layer || "0");
    }
    populateSelect(state.factionSelectEl, scope.getFactionRecords?.() || [], state.working.faction, "(None)");
    populateSelect(state.spriteSelectEl, scope.getSpriteCatalogRecords?.() || [], state.working.spriteId, "(Select sprite)");
    populateSelect(state.dialogueSelectEl, scope.getDialogueCatalogRecords?.() || [], state.working.dialogueId, "(None)");
    if (state.descriptionInputEl) {
      state.descriptionInputEl.value = String(state.working.description || "");
    }
    if (state.positionValueEl) {
      state.positionValueEl.textContent = String(state.working.position || "unplaced");
    }
    renderTags();
    refreshHeader();
  }

  function renderTags() {
    if (!state.tagListEl) {
      return;
    }
    const tags = normalizeEditorTags(state.working?.tags);
    state.tagListEl.innerHTML = "";
    if (tags.length <= 0) {
      const emptyEl = document.createElement("div");
      emptyEl.className = "tile-art-tags-empty";
      emptyEl.textContent = "No tags yet. Type a tag and press Enter.";
      state.tagListEl.appendChild(emptyEl);
      return;
    }
    tags.forEach((tag) => {
      const chipEl = document.createElement("button");
      chipEl.type = "button";
      chipEl.className = "tile-art-tag-chip";
      chipEl.title = `Remove tag "${tag}"`;
      chipEl.setAttribute("aria-label", `Remove tag ${tag}`);
      chipEl.innerHTML = `<span class="tile-art-tag-chip-label">${tag}</span><span class="tile-art-tag-chip-remove" aria-hidden="true">x</span>`;
      chipEl.addEventListener("click", () => {
        if (!state.working) {
          return;
        }
        state.working = {
          ...state.working,
          tags: normalizeEditorTags((state.working.tags || []).filter((entry) => String(entry || "").toLocaleLowerCase() !== tag.toLocaleLowerCase())),
        };
        renderTags();
        markDirty();
      });
      state.tagListEl.appendChild(chipEl);
    });
  }

  function addTag(rawValue) {
    if (!state.working) {
      return false;
    }
    const normalizedTag = normalizeEditorTagValue(rawValue);
    if (!normalizedTag) {
      return false;
    }
    const nextTags = normalizeEditorTags([...(state.working.tags || []), normalizedTag]);
    if (nextTags.length === normalizeEditorTags(state.working.tags).length) {
      if (state.tagInputEl) {
        state.tagInputEl.value = "";
      }
      updateStatus("That tag already exists.", false);
      return false;
    }
    state.working = {
      ...state.working,
      tags: nextTags,
    };
    if (state.tagInputEl) {
      state.tagInputEl.value = "";
    }
    renderTags();
    markDirty();
    return true;
  }

  async function exportTags() {
    if (!state.working) {
      return false;
    }
    const serialized = serializeEditorTags(state.working.tags);
    return copyTextWithClipboardFallback(
      serialized,
      "Copy tag export string",
      () => updateStatus("Copied entity tags to clipboard.", false),
      (clipboardAvailable) => updateStatus(
        clipboardAvailable
          ? "Clipboard unavailable. Tag export string opened for manual copy."
          : "Tag export string ready to copy.",
        false,
      ),
    );
  }

  function importTags() {
    if (!state.working) {
      return false;
    }
    const pasted = promptForImportText("Paste tag export string", "");
    if (pasted === null) {
      return false;
    }
    const importedTags = parseImportedEditorTags(pasted);
    if (importedTags.length <= 0) {
      updateStatus("No valid tags were found in that import string.", true);
      return false;
    }
    const previousTags = normalizeEditorTags(state.working.tags);
    const nextTags = normalizeEditorTags([...(state.working.tags || []), ...importedTags]);
    if (nextTags.length === previousTags.length) {
      updateStatus("All imported tags already exist on this entity.", false);
      return false;
    }
    state.working = {
      ...state.working,
      tags: nextTags,
    };
    renderTags();
    markDirty(`Imported ${nextTags.length - previousTags.length} tag${nextTags.length - previousTags.length === 1 ? "" : "s"}.`);
    return true;
  }

  function confirmDiscardIfDirty() {
    return confirmDiscardChanges("Discard unsaved entity changes?", state.dirty);
  }

  function loadEntity(entityId) {
    const npc = getNpcById(entityId);
    if (!npc) {
      return false;
    }
    state.entityId = String(entityId || "").trim();
    state.working = buildWorkingFromNpc(npc);
    state.activeTab = "information";
    state.dirty = false;
    refreshFormValues();
    updateStatus("Edit the entity, then save your changes.", false);
    return true;
  }

  async function save() {
    if (!state.working || !state.entityId || state.saving) {
      return false;
    }
    const npc = getNpcById(state.entityId);
    if (!npc) {
      updateStatus("Entity no longer exists.", true);
      return false;
    }
    state.saving = true;
    refreshHeader();
    try {
      const nextType = scope.normalizeEntityType?.(state.working.entityType || "friendly", "friendly") || "friendly";
      const nextLayer = Number(state.working.layer || 0);
      const nextTags = normalizeEditorTags(state.working.tags);
      scope.applyNpcEditorChange?.(npc, (target) => {
        target.record.name = String(state.working.name || "");
        target.record.entityType = nextType;
        target.layer = Number.isFinite(nextLayer) ? nextLayer : 0;
        target.record.layer = target.layer;
        target.record.faction = String(state.working.faction || "");
        target.record.spriteId = String(state.working.spriteId || "");
        target.record.dialogueId = String(state.working.dialogueId || "");
        target.record.description = String(state.working.description || "");
        target.record.tags = nextTags;
      }, "Entity");
      if (scope.activeEntityCategory !== nextType) {
        scope.activeEntityCategory = nextType;
        uiScope.refreshEntityTypeTabs?.();
      }
      uiScope.renderInstancePalette?.();
      uiScope.renderNpcList?.();
      loadEntity(state.entityId);
      updateStatus("Entity saved.", false);
      return true;
    } finally {
      state.saving = false;
      refreshHeader();
    }
  }

  function buildField(labelText, controlEl) {
    const fieldEl = document.createElement("label");
    fieldEl.className = "entity-editor-field";
    const labelEl = document.createElement("span");
    labelEl.className = "entity-editor-label";
    labelEl.textContent = labelText;
    fieldEl.appendChild(labelEl);
    fieldEl.appendChild(controlEl);
    return fieldEl;
  }

  function ensureShell() {
    if (state.shellEl && state.shellEl.isConnected) {
      return state.shellEl;
    }
    const shellEl = document.createElement("div");
    shellEl.className = "tool-popout-window entity-editor-window hidden";
    const titlebarEl = document.createElement("div");
    titlebarEl.className = "tool-popout-titlebar";
    titlebarEl.innerHTML = `
      <div class="tool-popout-title">Entity Editor</div>
      <div class="tool-popout-hint">Placed entity details</div>
      <button class="tool-popout-close-btn" type="button" aria-label="Close entity editor">X</button>
    `;
    const bodyEl = document.createElement("div");
    bodyEl.className = "tool-popout-body";

    const cardEl = document.createElement("div");
    cardEl.className = "entity-editor-card";
    const headEl = document.createElement("div");
    headEl.className = "entity-editor-head";
    const titleEl = document.createElement("div");
    titleEl.className = "entity-editor-title";
    const subtitleEl = document.createElement("div");
    subtitleEl.className = "entity-editor-subtitle";
    headEl.appendChild(titleEl);
    headEl.appendChild(subtitleEl);

    const nameRowEl = document.createElement("div");
    nameRowEl.className = "tile-art-name-row";
    const nameFieldEl = document.createElement("label");
    nameFieldEl.className = "tile-art-name-field";
    const nameLabelEl = document.createElement("span");
    nameLabelEl.className = "tile-art-name-label";
    nameLabelEl.textContent = "Entity Name";
    const nameInputEl = document.createElement("input");
    nameInputEl.type = "text";
    nameInputEl.className = "tile-art-name-input";
    nameInputEl.maxLength = 80;
    nameInputEl.spellcheck = false;
    nameInputEl.placeholder = "Entity name";
    nameInputEl.addEventListener("input", () => {
      if (!state.working) {
        return;
      }
      state.working = {
        ...state.working,
        name: String(nameInputEl.value || ""),
      };
      refreshHeader();
      markDirty();
    });
    nameFieldEl.appendChild(nameLabelEl);
    nameFieldEl.appendChild(nameInputEl);

    const tabButtonsEl = document.createElement("div");
    tabButtonsEl.className = "tile-art-tabs";
    const informationTabBtnEl = document.createElement("button");
    informationTabBtnEl.type = "button";
    informationTabBtnEl.className = "tile-art-tab-btn";
    informationTabBtnEl.textContent = "Information";
    informationTabBtnEl.addEventListener("click", () => {
      setActiveTab("information");
    });
    const tagsTabBtnEl = document.createElement("button");
    tagsTabBtnEl.type = "button";
    tagsTabBtnEl.className = "tile-art-tab-btn";
    tagsTabBtnEl.textContent = "Tags";
    tagsTabBtnEl.addEventListener("click", () => {
      setActiveTab("tags");
    });
    tabButtonsEl.appendChild(informationTabBtnEl);
    tabButtonsEl.appendChild(tagsTabBtnEl);
    nameRowEl.appendChild(nameFieldEl);
    nameRowEl.appendChild(tabButtonsEl);

    const informationPaneEl = document.createElement("div");
    informationPaneEl.className = "entity-editor-pane";
    const gridEl = document.createElement("div");
    gridEl.className = "entity-editor-grid";

    const typeSelectEl = document.createElement("select");
    ["friendly", "hostile", "prop"].forEach((type) => {
      const optionEl = document.createElement("option");
      optionEl.value = type;
      optionEl.textContent = scope.getEntityTypeLabel?.(type) || type;
      typeSelectEl.appendChild(optionEl);
    });
    typeSelectEl.addEventListener("change", () => {
      if (!state.working) {
        return;
      }
      state.working = {
        ...state.working,
        entityType: scope.normalizeEntityType?.(typeSelectEl.value, "friendly") || "friendly",
      };
      refreshHeader();
      markDirty();
    });

    const layerSelectEl = document.createElement("select");
    layerSelectEl.addEventListener("change", () => {
      if (!state.working) {
        return;
      }
      state.working = {
        ...state.working,
        layer: String(layerSelectEl.value || "0"),
      };
      markDirty();
    });

    const factionSelectEl = document.createElement("select");
    factionSelectEl.addEventListener("change", () => {
      if (!state.working) {
        return;
      }
      state.working = {
        ...state.working,
        faction: String(factionSelectEl.value || ""),
      };
      markDirty();
    });

    const spriteSelectEl = document.createElement("select");
    spriteSelectEl.addEventListener("change", () => {
      if (!state.working) {
        return;
      }
      state.working = {
        ...state.working,
        spriteId: String(spriteSelectEl.value || ""),
      };
      markDirty();
    });

    const dialogueSelectEl = document.createElement("select");
    dialogueSelectEl.addEventListener("change", () => {
      if (!state.working) {
        return;
      }
      state.working = {
        ...state.working,
        dialogueId: String(dialogueSelectEl.value || ""),
      };
      markDirty();
    });

    const positionValueEl = document.createElement("div");
    positionValueEl.className = "entity-editor-static";

    const descriptionInputEl = document.createElement("textarea");
    descriptionInputEl.className = "entity-editor-textarea";
    descriptionInputEl.rows = 6;
    descriptionInputEl.addEventListener("input", () => {
      if (!state.working) {
        return;
      }
      state.working = {
        ...state.working,
        description: String(descriptionInputEl.value || ""),
      };
      markDirty();
    });

    gridEl.appendChild(buildField("Type", typeSelectEl));
    gridEl.appendChild(buildField("Layer", layerSelectEl));
    gridEl.appendChild(buildField("Faction", factionSelectEl));
    gridEl.appendChild(buildField("Sprite", spriteSelectEl));
    gridEl.appendChild(buildField("Dialogue", dialogueSelectEl));
    gridEl.appendChild(buildField("Position", positionValueEl));
    gridEl.appendChild(buildField("Description", descriptionInputEl));
    informationPaneEl.appendChild(gridEl);

    const tagsPaneEl = document.createElement("div");
    tagsPaneEl.className = "tile-art-pane tile-art-tags-pane hidden";
    const tagFieldEl = document.createElement("label");
    tagFieldEl.className = "tile-art-tag-field";
    const tagHeadEl = document.createElement("div");
    tagHeadEl.className = "tile-art-tag-head";
    const tagLabelEl = document.createElement("span");
    tagLabelEl.className = "tile-art-tag-label";
    tagLabelEl.textContent = "Add Tag";
    const tagActionsEl = document.createElement("div");
    tagActionsEl.className = "tile-art-tag-actions";
    const exportTagsBtnEl = document.createElement("button");
    exportTagsBtnEl.type = "button";
    exportTagsBtnEl.className = "mini-btn";
    exportTagsBtnEl.textContent = "Export";
    exportTagsBtnEl.addEventListener("click", () => {
      void exportTags();
    });
    const importTagsBtnEl = document.createElement("button");
    importTagsBtnEl.type = "button";
    importTagsBtnEl.className = "mini-btn";
    importTagsBtnEl.textContent = "Import";
    importTagsBtnEl.addEventListener("click", () => {
      importTags();
    });
    tagActionsEl.appendChild(exportTagsBtnEl);
    tagActionsEl.appendChild(importTagsBtnEl);
    tagHeadEl.appendChild(tagLabelEl);
    tagHeadEl.appendChild(tagActionsEl);
    const tagInputEl = document.createElement("input");
    tagInputEl.type = "text";
    tagInputEl.className = "tile-art-tag-input";
    tagInputEl.maxLength = 48;
    tagInputEl.spellcheck = false;
    tagInputEl.placeholder = "Type a tag and press Enter";
    tagInputEl.addEventListener("keydown", (event) => {
      if (event.key !== "Enter") {
        return;
      }
      event.preventDefault();
      addTag(tagInputEl.value);
    });
    const tagListEl = document.createElement("div");
    tagListEl.className = "tile-art-tag-list";
    tagFieldEl.appendChild(tagHeadEl);
    tagFieldEl.appendChild(tagInputEl);
    tagsPaneEl.appendChild(tagFieldEl);
    tagsPaneEl.appendChild(tagListEl);

    const footerEl = document.createElement("div");
    footerEl.className = "entity-editor-footer";
    const statusEl = document.createElement("div");
    statusEl.className = "entity-editor-status";
    const actionsEl = document.createElement("div");
    actionsEl.className = "entity-editor-actions";
    const saveBtnEl = document.createElement("button");
    saveBtnEl.type = "button";
    saveBtnEl.className = "mini-btn";
    saveBtnEl.textContent = "Save";
    saveBtnEl.addEventListener("click", () => {
      void save();
    });
    actionsEl.appendChild(saveBtnEl);
    footerEl.appendChild(statusEl);
    footerEl.appendChild(actionsEl);

    const resizeEl = document.createElement("div");
    resizeEl.className = "tool-popout-resize";

    cardEl.appendChild(headEl);
    cardEl.appendChild(nameRowEl);
    cardEl.appendChild(informationPaneEl);
    cardEl.appendChild(tagsPaneEl);
    cardEl.appendChild(footerEl);
    bodyEl.appendChild(cardEl);
    const closeBtnEl = titlebarEl.querySelector(".tool-popout-close-btn");

    shellEl.appendChild(titlebarEl);
    shellEl.appendChild(bodyEl);
    shellEl.appendChild(resizeEl);

    shellEl.addEventListener("pointerdown", () => {
      focusWindow();
    });

    titlebarEl.addEventListener("pointerdown", (event) => {
      if (closeBtnEl && closeBtnEl.contains(event.target)) {
        return;
      }
      if (event.button !== 0) {
        return;
      }
      event.preventDefault();
      focusWindow();
      const layerRect = getLayerRect();
      const originLeft = Number(state.x) || 0;
      const originTop = Number(state.y) || 0;
      const startX = event.clientX;
      const startY = event.clientY;
      const move = (moveEvent) => {
        const nextRect = clampWindowRect(
          layerRect,
          originLeft + (moveEvent.clientX - startX),
          originTop + (moveEvent.clientY - startY),
          state.width,
          state.height,
        );
        state.x = nextRect.left;
        state.y = nextRect.top;
        applyWindowRect();
      };
      const up = () => {
        window.removeEventListener("pointermove", move);
        window.removeEventListener("pointerup", up);
        persistState();
      };
      window.addEventListener("pointermove", move);
      window.addEventListener("pointerup", up);
    });

    closeBtnEl?.addEventListener("click", (event) => {
      event.preventDefault();
      event.stopPropagation();
      close();
    });

    resizeEl.addEventListener("pointerdown", (event) => {
      if (event.button !== 0) {
        return;
      }
      event.preventDefault();
      focusWindow();
      const layerRect = getLayerRect();
      const startX = event.clientX;
      const startY = event.clientY;
      const originWidth = Number(state.width) || DEFAULT_WIDTH;
      const originHeight = Number(state.height) || DEFAULT_HEIGHT;
      const move = (moveEvent) => {
        const nextRect = clampWindowRect(
          layerRect,
          state.x,
          state.y,
          Math.max(MIN_WIDTH, originWidth + (moveEvent.clientX - startX)),
          Math.max(MIN_HEIGHT, originHeight + (moveEvent.clientY - startY)),
        );
        state.width = nextRect.width;
        state.height = nextRect.height;
        applyWindowRect();
      };
      const up = () => {
        window.removeEventListener("pointermove", move);
        window.removeEventListener("pointerup", up);
        persistState();
      };
      window.addEventListener("pointermove", move);
      window.addEventListener("pointerup", up);
    });

    state.shellEl = shellEl;
    state.bodyEl = bodyEl;
    state.titleEl = titleEl;
    state.subtitleEl = subtitleEl;
    state.statusEl = statusEl;
    state.saveBtnEl = saveBtnEl;
    state.resizeEl = resizeEl;
    state.nameInputEl = nameInputEl;
    state.tabButtonsEl = tabButtonsEl;
    state.informationTabBtnEl = informationTabBtnEl;
    state.tagsTabBtnEl = tagsTabBtnEl;
    state.informationPaneEl = informationPaneEl;
    state.tagsPaneEl = tagsPaneEl;
    state.typeSelectEl = typeSelectEl;
    state.layerSelectEl = layerSelectEl;
    state.factionSelectEl = factionSelectEl;
    state.spriteSelectEl = spriteSelectEl;
    state.dialogueSelectEl = dialogueSelectEl;
    state.descriptionInputEl = descriptionInputEl;
    state.positionValueEl = positionValueEl;
    state.tagInputEl = tagInputEl;
    state.tagListEl = tagListEl;
    uiScope.toolWindowLayerEl?.appendChild(shellEl);
    applyWindowRect();
    shellEl.classList.toggle("hidden", state.visible !== true);
    refreshHeader();
    renderTags();
    return shellEl;
  }

  function open(entityId) {
    const normalizedId = String(entityId || "").trim();
    if (!normalizedId) {
      return false;
    }
    if (state.entityId && state.entityId !== normalizedId && !confirmDiscardIfDirty()) {
      return false;
    }
    ensureShell();
    if (!loadEntity(normalizedId)) {
      scope.setStatus?.("Entity not found: " + normalizedId, true);
      return false;
    }
    const nextRect = clampWindowRect(getLayerRect(), state.x, state.y, state.width, state.height);
    state.x = nextRect.left;
    state.y = nextRect.top;
    state.width = nextRect.width;
    state.height = nextRect.height;
    state.visible = true;
    state.shellEl?.classList.remove("hidden");
    applyWindowRect();
    focusWindow();
    persistState();
    return true;
  }

  function close() {
    if (!confirmDiscardIfDirty()) {
      return false;
    }
    state.visible = false;
    state.dirty = false;
    clearFocus();
    state.shellEl?.classList.add("hidden");
    persistState();
    return true;
  }

  function initialize() {
    if (initialized) {
      return;
    }
    initialized = true;
    ensureShell();
    window.addEventListener("resize", () => {
      const nextRect = clampWindowRect(getLayerRect(), state.x, state.y, state.width, state.height);
      state.x = nextRect.left;
      state.y = nextRect.top;
      state.width = nextRect.width;
      state.height = nextRect.height;
      applyWindowRect();
      persistState();
    });
    if (state.visible && state.entityId) {
      open(state.entityId);
    } else {
      state.visible = false;
      state.shellEl?.classList.add("hidden");
    }
  }

  return {
    initialize,
    open,
    close,
    isOpen: () => state.visible === true,
  };
}
