(function attachPromptFillerCore(root, factory) {
  const api = factory();
  if (typeof module === "object" && module.exports) module.exports = api;
  root.PromptFillerCore = api;
})(typeof globalThis !== "undefined" ? globalThis : window, function createPromptFillerCore() {
  "use strict";

  const DEFAULT_SPECIES = Object.freeze([
    { key: "cat", label: "猫" },
    { key: "dog", label: "狗" },
    { key: "rabbit", label: "兔子" },
    { key: "hamster", label: "仓鼠" },
    { key: "bird", label: "鸟" },
    { key: "fish", label: "鱼" },
    { key: "turtle", label: "乌龟" },
    { key: "other", label: "其他" },
  ]);

  const ACTION_DEFS = Object.freeze([
    {
      key: "idle",
      id: "action.idle",
      title: "待机",
      description: "这个物种在默认待机动画里的完整提示词。",
    },
    {
      key: "taskbar",
      id: "action.taskbar",
      title: "播放位置 1：任务栏",
      description: "这个物种在任务栏附近追加动作时的完整提示词。",
    },
    {
      key: "window",
      id: "action.window",
      title: "播放位置 2：窗口",
      description: "这个物种在窗口边缘追加动作时的完整提示词。",
    },
    {
      key: "desktop",
      id: "action.desktop",
      title: "播放位置 3：显示器",
      description: "这个物种在显示器桌面区域追加动作时的完整提示词。",
    },
  ]);

  function deepClone(value) {
    return JSON.parse(JSON.stringify(value));
  }

  function asObject(value) {
    return value && typeof value === "object" && !Array.isArray(value) ? value : {};
  }

  function asString(value) {
    return typeof value === "string" ? value : "";
  }

  function isoNow(now) {
    if (now instanceof Date) return now.toISOString();
    if (typeof now === "string" && now) return now;
    return new Date().toISOString();
  }

  function normalizeSpeciesKey(key) {
    return String(key || "")
      .trim()
      .toLowerCase()
      .replace(/[^a-z0-9_-]+/g, "_")
      .replace(/^_+|_+$/g, "");
  }

  function joinPromptParts(parts) {
    return parts.map((part) => asString(part).trim()).filter(Boolean).join("\n\n");
  }

  function emptyActionPrompts() {
    return Object.fromEntries(ACTION_DEFS.map((action) => [action.key, ""]));
  }

  function defaultActionPromptMap() {
    return Object.fromEntries(DEFAULT_SPECIES.map((species) => [species.key, emptyActionPrompts()]));
  }

  function defaultSpeciesLabels() {
    return Object.fromEntries(DEFAULT_SPECIES.map((species) => [species.key, species.label]));
  }

  function normalizeActionPrompts(sourceActionPrompts) {
    const actionPrompts = defaultActionPromptMap();
    for (const [rawSpeciesKey, rawActions] of Object.entries(asObject(sourceActionPrompts))) {
      const speciesKey = normalizeSpeciesKey(rawSpeciesKey);
      if (!speciesKey) continue;
      const sourceActions = asObject(rawActions);
      actionPrompts[speciesKey] = emptyActionPrompts();
      for (const action of ACTION_DEFS) {
        actionPrompts[speciesKey][action.key] = asString(sourceActions[action.key]);
      }
    }
    return actionPrompts;
  }

  function legacyActionPrompts(source) {
    const speciesSource = asObject(source.species);
    const speciesEntries = { ...Object.fromEntries(DEFAULT_SPECIES.map((species) => [species.key, ""])), ...speciesSource };
    const append = asObject(source.append_animation);
    const placements = asObject(append.placements);
    const actionPrompts = {};

    for (const [rawSpeciesKey, speciesText] of Object.entries(speciesEntries)) {
      const speciesKey = normalizeSpeciesKey(rawSpeciesKey);
      if (!speciesKey) continue;
      actionPrompts[speciesKey] = {
        idle: joinPromptParts([source.common, speciesText, source.idle]),
        taskbar: joinPromptParts([source.common, speciesText, append.common, placements.taskbar]),
        window: joinPromptParts([source.common, speciesText, append.common, placements.window]),
        desktop: joinPromptParts([source.common, speciesText, append.common, placements.desktop]),
      };
    }

    return normalizeActionPrompts(actionPrompts);
  }

  function normalizeServiceConfig(input) {
    const source = asObject(input);
    return {
      version: asString(source.version) || "action-prompts-v1",
      action_prompts: source.action_prompts
        ? normalizeActionPrompts(source.action_prompts)
        : legacyActionPrompts(source),
    };
  }

  function collectFieldKeys(serviceConfig) {
    const keys = [];
    for (const speciesKey of Object.keys(serviceConfig.action_prompts)) {
      for (const action of ACTION_DEFS) {
        keys.push(`action_prompts.${speciesKey}.${action.key}`);
      }
    }
    return keys;
  }

  function parseActionFieldKey(fieldKey) {
    const parts = String(fieldKey || "").split(".");
    if (parts.length !== 3 || parts[0] !== "action_prompts") return null;
    const speciesKey = normalizeSpeciesKey(parts[1]);
    const actionKey = parts[2];
    if (!speciesKey || !ACTION_DEFS.some((action) => action.key === actionKey)) return null;
    return { speciesKey, actionKey };
  }

  function getFromServiceConfig(serviceConfig, fieldKey) {
    const parsed = parseActionFieldKey(fieldKey);
    if (!parsed) return "";
    return asString(serviceConfig.action_prompts[parsed.speciesKey]?.[parsed.actionKey]);
  }

  function setInServiceConfig(serviceConfig, fieldKey, text) {
    const parsed = parseActionFieldKey(fieldKey);
    if (!parsed) return;
    if (!serviceConfig.action_prompts[parsed.speciesKey]) {
      serviceConfig.action_prompts[parsed.speciesKey] = emptyActionPrompts();
    }
    serviceConfig.action_prompts[parsed.speciesKey][parsed.actionKey] = asString(text);
  }

  function normalizeHistories(rawHistories, serviceConfig, now, options) {
    const histories = {};
    const source = asObject(rawHistories);
    const shouldSeedMissingHistories = asObject(options).seedMissingHistories !== false;

    for (const fieldKey of collectFieldKeys(serviceConfig)) {
      const sourceHistory = Array.isArray(source[fieldKey]) ? source[fieldKey] : [];
      const normalized = sourceHistory
        .filter((entry) => entry && typeof entry === "object")
        .map((entry, index) => ({
          id: asString(entry.id) || `v${index + 1}`,
          saved_at: isoNow(entry.saved_at || entry.created_at || now),
          text: asString(entry.text),
        }));
      if (normalized.length) histories[fieldKey] = normalized;
    }

    if (shouldSeedMissingHistories) {
      for (const fieldKey of collectFieldKeys(serviceConfig)) {
        if (histories[fieldKey]) continue;
        const text = getFromServiceConfig(serviceConfig, fieldKey);
        if (text.trim()) {
          histories[fieldKey] = [{ id: "v1", saved_at: isoNow(now), text }];
        }
      }
    }
    return histories;
  }

  function normalizeEditorMeta(rawMeta, serviceConfig, now, options) {
    const meta = asObject(rawMeta);
    const labels = { ...defaultSpeciesLabels(), ...asObject(meta.species_labels) };
    for (const speciesKey of Object.keys(serviceConfig.action_prompts)) {
      if (!labels[speciesKey]) {
        const found = DEFAULT_SPECIES.find((species) => species.key === speciesKey);
        labels[speciesKey] = found ? found.label : speciesKey;
      }
    }

    return {
      version: 2,
      active_species: normalizeSpeciesKey(meta.active_species) || Object.keys(serviceConfig.action_prompts)[0] || "cat",
      species_labels: labels,
      histories: normalizeHistories(meta.histories, serviceConfig, now, options),
      created_at: asString(meta.created_at) || isoNow(now),
      updated_at: isoNow(meta.updated_at || now),
    };
  }

  function normalizePromptState(input, now) {
    const payload = asObject(input);
    const serviceInput = payload.service_config || payload.prompt_config || payload.prompt || payload;
    const serviceConfig = normalizeServiceConfig(serviceInput);
    const hasEditorMeta = Boolean(payload.editor_meta && typeof payload.editor_meta === "object" && !Array.isArray(payload.editor_meta));
    return {
      service_config: serviceConfig,
      editor_meta: normalizeEditorMeta(payload.editor_meta, serviceConfig, now, {
        seedMissingHistories: !hasEditorMeta,
      }),
    };
  }

  function createDefaultPromptState(now) {
    const serviceConfig = normalizeServiceConfig({ action_prompts: defaultActionPromptMap() });
    return {
      service_config: serviceConfig,
      editor_meta: normalizeEditorMeta({}, serviceConfig, now),
    };
  }

  function importPromptPayload(payload, now) {
    return normalizePromptState(payload, now);
  }

  function normalizeStateForRead(state) {
    return normalizePromptState(state, state && state.editor_meta && state.editor_meta.updated_at);
  }

  function getFieldText(state, fieldKey) {
    return getFromServiceConfig(normalizeStateForRead(state).service_config, fieldKey);
  }

  function cardForAction(state, speciesKey, action) {
    const fieldKey = `action_prompts.${speciesKey}.${action.key}`;
    const text = getFromServiceConfig(state.service_config, fieldKey);
    const history = state.editor_meta.histories[fieldKey] || [];
    const lastVersion = history[history.length - 1] || null;
    const status = text.trim() ? "filled" : "empty";
    return {
      id: action.id,
      fieldKey,
      title: action.title,
      keyName: fieldKey,
      description: action.description,
      composition: "完整提示词，不再与其他提示词框拼接。",
      status,
      statusLabel: status === "filled" ? "已填写" : "空",
      isEmpty: status === "empty",
      versionCount: history.length,
      versionLabel: history.length ? `${history.length} 个版本` : "暂无版本",
      lastSavedAt: lastVersion ? lastVersion.saved_at : "",
    };
  }

  function promptCardsForSpecies(inputState, rawSpeciesKey) {
    const state = normalizeStateForRead(inputState);
    const speciesKey = normalizeSpeciesKey(rawSpeciesKey) || state.editor_meta.active_species;
    return ACTION_DEFS.map((action) => cardForAction(state, speciesKey, action));
  }

  function savePromptVersion(inputState, fieldKey, text, savedAt) {
    const now = isoNow(savedAt);
    const state = normalizeStateForRead(inputState);
    const next = deepClone(state);
    const cleanFieldKey = String(fieldKey || "");
    const cleanText = asString(text);
    setInServiceConfig(next.service_config, cleanFieldKey, cleanText);

    const history = next.editor_meta.histories[cleanFieldKey] || [];
    const latest = history[history.length - 1];
    if (!latest || latest.text !== cleanText) {
      const versionNumber = history.length + 1;
      history.push({ id: `v${versionNumber}`, saved_at: now, text: cleanText });
      next.editor_meta.histories[cleanFieldKey] = history;
    }
    const parsed = parseActionFieldKey(cleanFieldKey);
    if (parsed) next.editor_meta.active_species = parsed.speciesKey;
    next.editor_meta.updated_at = now;
    return next;
  }

  function updatePromptDraft(inputState, fieldKey, text, savedAt) {
    const now = isoNow(savedAt);
    const state = normalizeStateForRead(inputState);
    const next = deepClone(state);
    const cleanFieldKey = String(fieldKey || "");
    setInServiceConfig(next.service_config, cleanFieldKey, text);

    const parsed = parseActionFieldKey(cleanFieldKey);
    if (parsed) next.editor_meta.active_species = parsed.speciesKey;
    next.editor_meta.updated_at = now;
    return next;
  }

  function ensureSpecies(inputState, rawKey, rawLabel, savedAt) {
    const now = isoNow(savedAt);
    const key = normalizeSpeciesKey(rawKey);
    if (!key) return normalizeStateForRead(inputState);
    const state = normalizeStateForRead(inputState);
    const next = deepClone(state);
    if (!Object.prototype.hasOwnProperty.call(next.service_config.action_prompts, key)) {
      next.service_config.action_prompts[key] = emptyActionPrompts();
    }
    next.editor_meta.species_labels[key] = asString(rawLabel).trim() || key;
    next.editor_meta.active_species = key;
    next.editor_meta.updated_at = now;
    return next;
  }

  function deleteSpecies(inputState, rawSpeciesKey, savedAt) {
    const now = isoNow(savedAt);
    const speciesKey = normalizeSpeciesKey(rawSpeciesKey);
    const state = normalizeStateForRead(inputState);
    const next = deepClone(state);
    delete next.service_config.action_prompts[speciesKey];
    delete next.editor_meta.species_labels[speciesKey];
    for (const fieldKey of Object.keys(next.editor_meta.histories)) {
      if (fieldKey.startsWith(`action_prompts.${speciesKey}.`)) {
        delete next.editor_meta.histories[fieldKey];
      }
    }
    const remaining = Object.keys(next.service_config.action_prompts);
    if (next.editor_meta.active_species === speciesKey) {
      next.editor_meta.active_species = remaining[0] || "";
    }
    next.editor_meta.updated_at = now;
    return next;
  }

  function exportServiceConfig(inputState) {
    return deepClone(normalizeStateForRead(inputState).service_config);
  }

  function exportEditorBackup(inputState) {
    const state = normalizeStateForRead(inputState);
    return deepClone({
      service_config: state.service_config,
      editor_meta: state.editor_meta,
    });
  }

  return {
    ACTION_DEFS,
    DEFAULT_SPECIES,
    STATIC_CARD_DEFS: ACTION_DEFS,
    createDefaultPromptState,
    deleteSpecies,
    ensureSpecies,
    exportEditorBackup,
    exportServiceConfig,
    getFieldText,
    importPromptPayload,
    normalizePromptState,
    normalizeServiceConfig,
    promptCardsForSpecies,
    savePromptVersion,
    updatePromptDraft,
  };
});
