(function (root, factory) {
  "use strict";

  const api = Object.freeze(factory());
  if (typeof module === "object" && module.exports) module.exports = api;
  if (root) root.MbnCandleOptionsWorkflow = api;
})(typeof globalThis === "object" ? globalThis : this, function () {
  "use strict";

  const CANDLE_BUILDER_KEY = "s8010";
  const supportedSources = Object.freeze([
    "legacy-stock-workflow",
    "legacy-fixed-workflow",
    "legacy-industry-workflow",
    "legacy-overseas-workflow",
    "legacy-named-foreign-stock-workflow",
    "legacy-trading-halt-workflow"
  ]);

  function normalizeOptions(ma5, ma20) {
    if (typeof ma5 !== "boolean" || typeof ma20 !== "boolean") {
      throw new TypeError("Global moving-average flags must be boolean values.");
    }
    return Object.freeze({ ma5, ma20 });
  }

  function expectedGraphicType(ma5, ma20) {
    const options = normalizeOptions(ma5, ma20);
    const flags = [];
    if (options.ma5) flags.push("MA5");
    if (options.ma20) flags.push("MA20");
    return flags.join(",");
  }

  function hasAdapters(value) {
    return value && typeof value === "object" && !Array.isArray(value) &&
      value.stock && value.fixed && value.industry && value.overseas && value.tradingHalt;
  }

  function recreateCandleEntry(value, ma5, ma20, adapters, now = new Date()) {
    const movingAverages = normalizeOptions(ma5, ma20);
    if (!value || typeof value !== "object" || Array.isArray(value) ||
        value.builderKey !== CANDLE_BUILDER_KEY || typeof value.enabled !== "boolean" ||
        !hasAdapters(adapters)) return null;
    const operator = value.operator;
    if (!operator || typeof operator !== "object" || !supportedSources.includes(operator.source)) return null;
    const options = {
      id: value.id,
      fadeDuration: value.fadeDuration,
      ma5: movingAverages.ma5,
      ma20: movingAverages.ma20
    };
    let recreated = null;
    try {
      switch (operator.source) {
        case "legacy-stock-workflow": {
          const cut = adapters.stock.getStockCut(operator.cutId);
          if (cut?.kind !== "candle") return null;
          recreated = adapters.stock.createStockPlaylistEntry(operator.stock, cut.id, options);
          break;
        }
        case "legacy-fixed-workflow": {
          const action = adapters.fixed.actions.find(item => item.id === operator.actionId);
          if (action?.builderKey !== CANDLE_BUILDER_KEY || action.available !== true) return null;
          recreated = adapters.fixed.createFixedPlaylistEntry(action.id, { ...options, now });
          break;
        }
        case "legacy-industry-workflow": {
          const cut = adapters.industry.cuts.find(item => item.id === operator.cutId);
          if (cut?.kind !== "candle") return null;
          recreated = adapters.industry.createIndustryPlaylistEntry(operator.market, cut.id, {
            ...options,
            selected: operator.selected,
            pair: operator.pair,
            now
          });
          break;
        }
        case "legacy-overseas-workflow": {
          const action = adapters.overseas.stockActions.find(item => item.id === operator.actionId);
          if (operator.kind !== "stock" || action?.kind !== "candle") return null;
          recreated = adapters.overseas.createOverseasStockPlaylistEntry(
            operator.identity,
            action.id,
            options);
          break;
        }
        case "legacy-named-foreign-stock-workflow": {
          const action = adapters.overseas.stockActions.find(item => item.id === operator.actionId);
          if (operator.kind !== "stock" || action?.kind !== "candle" ||
              !operator.rawSelection || typeof operator.verifiedAt !== "string") return null;
          const canonical = adapters.overseas.createOverseasStockPlaylistEntry(
            operator.identity,
            action.id,
            options);
          recreated = {
            ...canonical,
            operator: Object.freeze({
              ...operator,
              identity: canonical.operator.identity,
              movingAverages: canonical.operator.movingAverages
            })
          };
          break;
        }
        case "legacy-trading-halt-workflow": {
          const action = adapters.tradingHalt.getAction(operator.actionId);
          if (action?.kind !== "candle") return null;
          recreated = adapters.tradingHalt.createTradingHaltPlaylistEntry(
            operator.tradingHalt,
            action.id,
            options);
          break;
        }
        default:
          return null;
      }
    } catch {
      return null;
    }

    return recreated ? { ...recreated, enabled: value.enabled } : null;
  }

  function entryAlreadyMatches(value, ma5, ma20) {
    const graphicType = expectedGraphicType(ma5, ma20);
    return value?.selection?.graphicType === graphicType &&
      value?.graphicType === graphicType &&
      value?.operator?.movingAverages?.ma5 === ma5 &&
      value?.operator?.movingAverages?.ma20 === ma20;
  }

  function synchronizePlaylist(values, ma5, ma20, adapters, now = new Date()) {
    normalizeOptions(ma5, ma20);
    if (!Array.isArray(values)) throw new TypeError("A playlist array is required.");
    const invalidIds = [];
    let changed = false;
    const playlist = values.map(value => {
      if (value?.builderKey !== CANDLE_BUILDER_KEY) return value;
      if (entryAlreadyMatches(value, ma5, ma20)) return value;
      const recreated = recreateCandleEntry(value, ma5, ma20, adapters, now);
      if (!recreated) {
        invalidIds.push(typeof value?.id === "string" ? value.id : "");
        return value;
      }
      changed = true;
      return recreated;
    });
    return Object.freeze({
      playlist: Object.freeze(playlist),
      changed,
      valid: invalidIds.length === 0,
      invalidIds: Object.freeze(invalidIds)
    });
  }

  return {
    candleBuilderKey: CANDLE_BUILDER_KEY,
    supportedSources,
    normalizeOptions,
    expectedGraphicType,
    recreateCandleEntry,
    synchronizePlaylist
  };
});
