/* eslint-disable @typescript-eslint/ban-ts-comment */
// @ts-nocheck
import { mergeImagesPayloadWithSpritesPayload, mergeImagesPayloadWithTilesPayload } from "../contentTransforms/graphicsPayload";

const TILE_SYMBOL_POOL = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!$%&()*+,-/:;<=>?@[]^_{|}~=";

function cloneValue(value) {
  if (typeof structuredClone === "function") {
    return structuredClone(value);
  }
  return value == null ? value : JSON.parse(JSON.stringify(value));
}

function getRootKey(type) {
  return type === "sprites" ? "sprites" : "tiles";
}

function getTypeLabel(type) {
  return type === "sprites" ? "Sprites" : "Tiles";
}

function extractImportRecords(payload, rootKey) {
  if (Array.isArray(payload)) {
    return payload;
  }
  if (!payload || typeof payload !== "object") {
    return [];
  }
  if (Array.isArray(payload[rootKey])) {
    return payload[rootKey];
  }
  return [payload];
}

function normalizeOptionalImportText(value) {
  const normalized = String(value || "").trim();
  return normalized || "";
}

function normalizeImportRecord(record) {
  if (!record || typeof record !== "object" || Array.isArray(record)) {
    return null;
  }
  const rawRows = Array.isArray(record.rows) ? record.rows.map((row) => String(row || "")) : [];
  const parsedWidth = Number(record.width);
  const parsedHeight = Number(record.height);
  const parsedPixelScale = Number(record.pixelScale);
  const widthFromRows = rawRows.reduce((max, row) => Math.max(max, row.length), 0);
  const width = Number.isFinite(parsedWidth) && parsedWidth > 0 ? Math.floor(parsedWidth) : widthFromRows;
  const height = Number.isFinite(parsedHeight) && parsedHeight > 0 ? Math.floor(parsedHeight) : rawRows.length;
  const pixelScale = Number.isFinite(parsedPixelScale) && parsedPixelScale > 0 ? Math.floor(parsedPixelScale) : 1;
  if (width <= 0 || height <= 0 || rawRows.length === 0) {
    return null;
  }
  return {
    name: normalizeOptionalImportText(record.name) || normalizeOptionalImportText(record.id),
    description: normalizeOptionalImportText(record.description),
    width,
    height,
    pixelScale,
    rows: Array.from({ length: height }, (_value, rowIndex) => {
      const source = String(rawRows[rowIndex] || "");
      return source.padEnd(width, ".").slice(0, width);
    }),
  };
}

function getImportSignature(record) {
  return [
    String(record.width || 0),
    String(record.height || 0),
    String(record.pixelScale || 1),
    Array.isArray(record.rows) ? record.rows.join("\n") : "",
  ].join("|");
}

function createGeneratedId(scope, prefix) {
  return prefix + "_" + String(scope.runtimeUniqueId() || "").replace(/^inst_/, "");
}

function createImportedSpriteRecord(scope, normalizedRecord) {
  const id = createGeneratedId(scope, "sprite");
  return {
    id,
    name: normalizedRecord.name || id,
    width: normalizedRecord.width,
    height: normalizedRecord.height,
    pixelScale: normalizedRecord.pixelScale,
    rows: normalizedRecord.rows.slice(),
  };
}

function takeNextTileSymbol(usedSymbols) {
  for (const symbol of TILE_SYMBOL_POOL) {
    if (!usedSymbols.has(symbol)) {
      usedSymbols.add(symbol);
      return symbol;
    }
  }
  throw new Error("No free tile symbols remain for imported tiles.");
}

function createImportedTileRecord(scope, normalizedRecord, usedSymbols) {
  const id = createGeneratedId(scope, "tile");
  return {
    id,
    symbol: takeNextTileSymbol(usedSymbols),
    name: normalizedRecord.name || id,
    description: normalizedRecord.description || "",
    width: normalizedRecord.width,
    height: normalizedRecord.height,
    pixelScale: normalizedRecord.pixelScale,
    rows: normalizedRecord.rows.slice(),
  };
}

function normalizeExistingPayload(scope, type) {
  const rootKey = getRootKey(type);
  const source = scope.contentBundle[type] && typeof scope.contentBundle[type] === "object" && !Array.isArray(scope.contentBundle[type])
    ? scope.contentBundle[type]
    : { schemaVersion: 1, [rootKey]: [] };
  const records = Array.isArray(source[rootKey]) ? source[rootKey] : [];
  return {
    schemaVersion: typeof source.schemaVersion === "number" ? source.schemaVersion : 1,
    [rootKey]: records.slice(),
  };
}

export function createImportController(scope) {
  const documentScope = scope.documentScope || scope;
  const renderScope = scope.renderScope || scope;
  const uiScope = scope.uiScope || scope;
  const sessionScope = scope.sessionScope || scope;
  let experimentalImportExpanded = false;
  let isImporting = false;
  let jsonImportModalOpen = false;

  function refreshImportControls() {
    if (scope.experimentalImportToggleBtn) {
      scope.experimentalImportToggleBtn.classList.toggle("expanded", experimentalImportExpanded);
      scope.experimentalImportToggleBtn.setAttribute("aria-expanded", experimentalImportExpanded ? "true" : "false");
    }
    if (scope.experimentalImportCheckEl) {
      scope.experimentalImportCheckEl.textContent = experimentalImportExpanded ? "[x]" : "[ ]";
    }
    if (scope.experimentalImportBodyEl) {
      scope.experimentalImportBodyEl.classList.toggle("hidden", !experimentalImportExpanded);
    }
    if (scope.importSpritesBtn) {
      scope.importSpritesBtn.disabled = isImporting;
    }
    if (scope.importTilesBtn) {
      scope.importTilesBtn.disabled = isImporting;
    }
    if (scope.importJsonBtn) {
      scope.importJsonBtn.disabled = isImporting;
    }
    if (scope.importJsonConfirmBtn) {
      scope.importJsonConfirmBtn.disabled = isImporting;
    }
    if (scope.importJsonCancelBtn) {
      scope.importJsonCancelBtn.disabled = isImporting;
    }
    if (scope.importJsonModal) {
      scope.importJsonModal.classList.toggle("hidden", !jsonImportModalOpen);
    }
  }

  function toggleExperimentalImportPanel() {
    experimentalImportExpanded = !experimentalImportExpanded;
    refreshImportControls();
  }

  function openImportDialog(type) {
    const inputEl = type === "sprites" ? scope.importSpritesInputEl : scope.importTilesInputEl;
    if (!inputEl) {
      return;
    }
    inputEl.value = "";
    inputEl.click();
  }

  function openJsonImportModal() {
    jsonImportModalOpen = true;
    if (scope.importJsonTypeSelect) {
      scope.importJsonTypeSelect.value = "sprites";
    }
    if (scope.importJsonTextarea) {
      scope.importJsonTextarea.value = "";
    }
    refreshImportControls();
    if (scope.importJsonTextarea && typeof scope.importJsonTextarea.focus === "function") {
      window.setTimeout(() => {
        try {
          scope.importJsonTextarea.focus();
        } catch {
          // Ignore focus issues if the modal closes before the deferred focus runs.
        }
      }, 0);
    }
  }

  function closeJsonImportModal() {
    jsonImportModalOpen = false;
    refreshImportControls();
  }

  async function importRecordsFromPayload(type, payload) {
    const label = getTypeLabel(type);

    const rootKey = getRootKey(type);
    const importedEntries = extractImportRecords(payload, rootKey);
    if (importedEntries.length === 0) {
      scope.setStatus(label + " import failed: no compatible records found.", true);
      return;
    }

    const existingPayload = normalizeExistingPayload(scope, type);
    const existingRecords = existingPayload[rootKey];
    const knownSignatures = new Set();
    existingRecords.forEach((entry) => {
      const normalized = normalizeImportRecord(entry);
      if (!normalized) {
        return;
      }
      knownSignatures.add(getImportSignature(normalized));
    });

    const pendingSignatures = new Set();
    const nextRecords = [];
    const usedTileSymbols = new Set(
      type === "tiles"
        ? existingRecords.map((entry) => String(entry?.symbol || "").charAt(0)).filter(Boolean)
        : [],
    );
    let duplicateCount = 0;
    let invalidCount = 0;

    importedEntries.forEach((entry) => {
      const normalized = normalizeImportRecord(entry);
      if (!normalized) {
        invalidCount += 1;
        return;
      }
      const signature = getImportSignature(normalized);
      if (knownSignatures.has(signature) || pendingSignatures.has(signature)) {
        duplicateCount += 1;
        return;
      }
      pendingSignatures.add(signature);
      nextRecords.push(
        type === "sprites"
          ? createImportedSpriteRecord(scope, normalized)
          : createImportedTileRecord(scope, normalized, usedTileSymbols),
      );
    });

    if (nextRecords.length === 0) {
      const summary = [
        "No new " + label.toLowerCase() + " imported.",
        duplicateCount > 0 ? (String(duplicateCount) + " duplicate" + (duplicateCount === 1 ? "" : "s") + " skipped.") : "",
        invalidCount > 0 ? (String(invalidCount) + " invalid entr" + (invalidCount === 1 ? "y" : "ies") + " ignored.") : "",
      ].filter(Boolean).join(" ");
      uiScope.setStatus(summary, false);
      return;
    }

    const nextPayload = {
      schemaVersion: existingPayload.schemaVersion,
      [rootKey]: existingRecords.concat(nextRecords),
    };
    const existingImagesPayload = cloneValue(
      documentScope.ensureDocumentContentPayload?.("images", { schemaVersion: 1, images: [] })
        || scope.contentBundle?.images
        || { schemaVersion: 1, images: [] },
    ) || { schemaVersion: 1, images: [] };
    const nextImagesPayload = type === "sprites"
      ? mergeImagesPayloadWithSpritesPayload(existingImagesPayload, nextPayload)
      : mergeImagesPayloadWithTilesPayload(existingImagesPayload, nextPayload);

    isImporting = true;
    refreshImportControls();
    uiScope.setStatus("Importing " + label.toLowerCase() + "...", false);
    try {
      await documentScope.persistContentPayload("images", nextImagesPayload);
      if (typeof documentScope.applyContentPayloadToRuntime === "function") {
        documentScope.applyContentPayloadToRuntime("images", nextImagesPayload);
      }
      if (type === "tiles" && !sessionScope.activeBrushTileId && nextRecords[0]?.id) {
        sessionScope.activeBrushTileId = nextRecords[0].id;
      }
      renderScope.draw();
      const summary = [
        "Imported " + nextRecords.length + " new " + label.toLowerCase() + ".",
        duplicateCount > 0 ? (String(duplicateCount) + " duplicate" + (duplicateCount === 1 ? "" : "s") + " skipped.") : "",
        invalidCount > 0 ? (String(invalidCount) + " invalid entr" + (invalidCount === 1 ? "y" : "ies") + " ignored.") : "",
      ].filter(Boolean).join(" ");
      uiScope.setStatus(summary, false);
    } catch (error) {
      uiScope.setStatus(String(error), true);
    } finally {
      isImporting = false;
      refreshImportControls();
    }
  }

  async function importRecordsFromFile(type, file) {
    const label = getTypeLabel(type);
    if (!file) {
      return;
    }

    let payload;
    try {
      payload = JSON.parse(await file.text());
    } catch {
      uiScope.setStatus(label + " import failed: invalid JSON.", true);
      return;
    }

    await importRecordsFromPayload(type, payload);
  }

  async function handleImportSelection(type) {
    const inputEl = type === "sprites" ? scope.importSpritesInputEl : scope.importTilesInputEl;
    const file = inputEl?.files && inputEl.files[0] ? inputEl.files[0] : null;
    if (!file) {
      return;
    }
    try {
      await importRecordsFromFile(type, file);
    } catch (error) {
      uiScope.setStatus(String(error), true);
    } finally {
      inputEl.value = "";
    }
  }

  async function submitJsonImport() {
    const type = String(scope.importJsonTypeSelect?.value || "sprites").trim() === "tiles" ? "tiles" : "sprites";
    const rawText = String(scope.importJsonTextarea?.value || "").trim();
    const label = getTypeLabel(type);
    if (!rawText) {
      uiScope.setStatus(label + " import failed: no JSON provided.", true);
      return;
    }
    let payload;
    try {
      payload = JSON.parse(rawText);
    } catch {
      uiScope.setStatus(label + " import failed: invalid JSON.", true);
      return;
    }
    await importRecordsFromPayload(type, payload);
    if (!isImporting) {
      closeJsonImportModal();
    }
  }

  return {
    refreshImportControls,
    toggleExperimentalImportPanel,
    openImportDialog,
    handleImportSelection,
    openJsonImportModal,
    closeJsonImportModal,
    submitJsonImport,
  };
}
