import {
  rehydrateFilter
} from "./chunk-SKMFMGCD.js";
import {
  findParent,
  getFilter,
  getFilterItemByTag,
  getSamplelstTW
} from "./chunk-SA7APTJR.js";
import {
  importPlot
} from "./chunk-SKREEF3H.js";
import {
  StoreApi,
  StoreBase
} from "./chunk-M3J4MINX.js";
import {
  CustomError
} from "./chunk-JNITUVXP.js";

// mass/store.ts
var idPrefix = "_MASS_AUTOID_" + Math.random().toString().slice(-6);
var id = 0;
var usedPlotIds = /* @__PURE__ */ new Set();
function getId() {
  return idPrefix + "_" + id++;
}
var navHeaderModes = /* @__PURE__ */ new Set([
  "with_tabs",
  // default, shows tabs cohort/charts/filter etc
  "hidden",
  // no header
  "search_only",
  // ?
  "hide_search",
  // ?
  "with_cohortHtmlSelect",
  // only show cohort toggle as <select>
  "only_buttons"
]);
var defaultState = {
  nav: {
    header_mode: "with_tabs",
    activeTab: 0
    // -1 for no active tab and all closed
  },
  // will be ignored if there is no dataset termdb.selectCohort
  // or value will be set to match a filter node that has been tagged
  // as 'cohortfilter' in state.termfilter.filter
  activeCohort: 0,
  search: { isVisible: true },
  plots: [],
  termfilter: {
    filter: {
      type: "tvslst",
      in: true,
      join: "",
      lst: []
    }
  },
  reuse: {
    customTermQ: {
      byId: {},
      // non-dictionary terms do not have a term.id,
      // save by term.type + name?
      byName: {}
    }
  },
  groups: [],
  // element: {name=str, filter={}}, to show in Groups tab
  customTerms: [],
  // element: {name=str, term={}}, able to attach more attr to object if needed
  autoSave: true
};
var MassStore = class extends StoreBase {
  constructor(opts, api) {
    super(opts);
    // expected class-specific props
    this.defaultState = defaultState;
    this.plotAdjusters = /* @__PURE__ */ new WeakMap();
    this.app = opts.app;
    this.api = api;
    this.type = "store";
    let savedState = {};
    try {
      const key = window.navigator.webdriver && window["SJPP_E2E_STORAGE_STATES_KEY"];
      const savedStateStr = key && window.localStorage.getItem("SJPP_E2E_STORAGE_STATES") || "{}";
      savedState = JSON.parse(savedStateStr)[key]?.state || {};
    } catch (_) {
      savedState = {};
    }
    this.state = this.copyMerge(this.toJson(defaultState), opts.state, savedState);
    this.prevGeneratedId = 0;
  }
  static {
    this.type = "store";
  }
  validateOpts(opts) {
    const s = opts.state;
    if (s.vocab.dslabel) {
      if (!s.vocab.genome) throw ".state[.vocab].genome missing";
    } else {
      if (!Array.isArray(s.vocab.terms)) throw "vocab.terms must be an array of objects";
    }
    return opts;
  }
  validateState() {
    if (!navHeaderModes.has(this.state.nav.header_mode)) throw "invalid state.nav.header_mode";
  }
  async init() {
    try {
      this.state.termdbConfig = await this.app.vocabApi.getTermdbConfig();
      await this.setTermfilter();
      await this.rehydrateGroups();
      await this.app.vocabApi.main({
        termfilter: JSON.parse(JSON.stringify(this.state.termfilter)),
        termdbConfig: this.state.termdbConfig
      });
      const invalidPlots = [];
      for (const [i, savedPlot] of this.state.plots.entries()) {
        let plot;
        try {
          const _ = await importPlot(savedPlot.chartType);
          plot = await _.getPlotConfig(savedPlot, this.app, this.state.activeCohort);
        } catch (e) {
          this.app.printError(e);
          console.error(`getPlotConfig() failed: ${e}`);
        }
        if (!plot) {
          invalidPlots.push(i);
          continue;
        }
        this.state.plots[i] = plot;
        if (!("id" in plot)) plot.id = `_AUTOID_${id++}_${i}`;
        if (plot.mayAdjustConfig) {
          plot.mayAdjustConfig(plot);
          this.plotAdjusters.set(plot, plot.mayAdjustConfig);
          delete plot.mayAdjustConfig;
        }
      }
      if (invalidPlots.length) {
        for (const i of invalidPlots) {
          this.state.plots.splice(i, 1);
        }
      }
    } catch (e) {
      console.log("store.init() error", e);
      throw e;
    }
  }
  setId(item) {
    item.$id = this.prevGeneratedId++;
    if (item.$lst) {
      for (const subitem of item.$lst) {
        this.setId(subitem);
      }
    }
  }
  async setTermfilter() {
    let filterUiRoot = getFilterItemByTag(this.state.termfilter.filter, "filterUiRoot");
    if (!filterUiRoot) {
      this.state.termfilter.filter.tag = "filterUiRoot";
      filterUiRoot = this.state.termfilter.filter;
    }
    await Promise.all(rehydrateFilter(this.state.termfilter.filter, this.app.vocabApi));
    if (!this.state.termdbConfig.selectCohort) {
      this.state.activeCohort = -1;
      if (this.state.activeTab === 0) this.state.activeTab = 1;
      if (this.state.nav.header_mode === "with_cohortHtmlSelect") {
        console.warn(`no termdbConfig.selectCohort to use for nav.header_mode = 'with_cohortHtmlSelect'`);
        this.state.nav.header_mode = "search_only";
      }
    } else {
      let cohortFilter = getFilterItemByTag(this.state.termfilter.filter, "cohortFilter");
      if (!cohortFilter) {
        cohortFilter = {
          tag: "cohortFilter",
          type: "tvs",
          tvs: {
            term: JSON.parse(JSON.stringify(this.state.termdbConfig.selectCohort.term)),
            values: this.state.activeCohort == -1 ? [] : this.state.termdbConfig.selectCohort.values[this.state.activeCohort].keys.map((key) => {
              return { key, label: key };
            })
          }
        };
        this.state.termfilter.filter = {
          type: "tvslst",
          in: true,
          join: "and",
          lst: [cohortFilter, filterUiRoot]
        };
      } else {
        const sorter = (a, b) => a < b ? -1 : 1;
        cohortFilter.tvs.values.sort((a, b) => a.key < b.key ? -1 : 1);
        const keysStr = JSON.stringify(cohortFilter.tvs.values.map((v) => v.key).sort(sorter));
        const i = this.state.termdbConfig.selectCohort.values.findIndex(
          (v) => keysStr == JSON.stringify(v.keys.sort(sorter))
        );
        if (this.state.activeCohort !== -1 && this.state.activeCohort !== 0 && i !== this.state.activeCohort) {
          console.log("Warning: cohortFilter will override the state.activeCohort due to mismatch");
        }
        this.state.activeCohort = i;
      }
    }
  }
  async rehydrateGroups() {
    const lst = [];
    for (const g of this.state.groups) {
      lst.push(...rehydrateFilter(g.filter, this.app.vocabApi));
    }
    await Promise.all(lst);
  }
};
MassStore.prototype.actions = {
  // Type '{ app_refresh(this: MassStore, action?: {}): Promise<void>; tab_set(action: any): void; cohort_set(action: any): void; plot_prep(action: any): Promise<void>; ... 13 more ...; delete_group({ name }: { ...; }): void; }' is not assignable to type '(action: { [prop: string]: any; type: string; }) => void | Promise<void>'.
  // Object literal may only specify known properties, and 'app_refresh' does not exist in type '(action: { [prop: string]: any; type: string; }) => void | Promise<void>'.
  async app_refresh(action) {
    this.state = this.copyMerge(this.toJson(this.state), action.state || {});
    const subactionPlotIds = /* @__PURE__ */ new Set();
    const promises = [];
    if (action.subactions) {
      for (const a of action.subactions) {
        promises.push(this.actions[a.type].call(this, a));
        if (a.type.startsWith("plot_")) subactionPlotIds.add(a.id);
      }
    }
    await Promise.all(promises);
    for (const plot of this.state.plots) {
      const mayAdjustConfig = this.plotAdjusters.get(plot);
      if (mayAdjustConfig && !subactionPlotIds.has(plot.id)) {
        mayAdjustConfig(plot, action.config);
      }
    }
  },
  tab_set(action) {
    this.state.nav.activeTab = action.activeTab;
  },
  cohort_set(action) {
    this.state.activeCohort = action.activeCohort;
    const cohort = this.state.termdbConfig.selectCohort.values[action.activeCohort];
    const cohortFilter = getFilterItemByTag(this.state.termfilter.filter, "cohortFilter");
    if (!cohortFilter) throw `No item tagged with 'cohortFilter'`;
    cohortFilter.tvs.values = cohort.keys.map((key) => {
      return { key, label: key };
    });
  },
  // dispatch "plot_prep" action to produce a 'initiating' UI of this plot, for user to fill in additional details to launch the plot
  // example: table, scatterplot which requires user to select two terms
  async plot_prep(action) {
    if (usedPlotIds.has(action.id)) delete action.id;
    const plot = {
      // rx.getComponents() relies on parsing dot-separated key names that breaks if a key has a dot,
      // the plot.id value should be assumed to be auto-generated and to not have any non-rx usage expectations
      id: "id" in action && !action.id.includes(".") ? action.id : getId()
    };
    usedPlotIds.add(plot.id);
    if (!action.config) throw ".config{} missing for plot_prep";
    if (action.config.chartType && Object.keys(action.config).length == 1) {
      const _ = await importPlot(action.config.chartType);
      const config = await _.getPlotConfig(action.config, this.app, this.state.activeCohort);
      action.config = Object.assign(config, action.config);
    }
    Object.assign(plot, action.config);
    this.state.plots.push(plot);
  },
  async plot_create(action) {
    if (usedPlotIds.has(action.id)) delete action.id;
    const _ = await importPlot(action.config.chartType);
    const plot = await _.getPlotConfig(action.config, this.app, this.state.activeCohort);
    if (!("id" in action) || action.id.includes(".")) action.id = getId();
    plot.id = action.id;
    usedPlotIds.add(plot.id);
    if (plot.mayAdjustConfig) {
      plot.mayAdjustConfig(plot);
      this.plotAdjusters.set(plot, plot.mayAdjustConfig);
      delete plot.mayAdjustConfig;
    }
    this.state.plots.push(plot);
    if (plot.sections) {
      for (const section of plot.sections) {
        for (const p of section.plots) {
          p.parentId = plot.id;
          if (!p.id) p.id = getId();
          const _2 = await importPlot(p.chartType);
          const config = await _2.getPlotConfig(p, this.app, this.state.activeCohort);
          this.state.plots.push(config);
        }
      }
    }
  },
  plot_edit(action) {
    const plot = this.state.plots.find((p) => p.id === action.id);
    if (!plot) {
      throw new CustomError(`missing plot config for id='${action.id}' in store.plot_edit()`, {
        name: "MISSING_PLOT_CONFIG",
        level: "warn"
      });
    }
    this.copyMerge(plot, action.config, action.opts ? action.opts : {});
    const mayAdjustConfig = this.plotAdjusters.get(plot);
    if (mayAdjustConfig) mayAdjustConfig(plot, action.config);
    if (action.config && "cutoff" in action.config) {
      plot.cutoff = action.config.cutoff;
    } else {
      delete plot.cutoff;
    }
    if (!action.parentId && plot.parentId) action.parentId = plot.parentId;
  },
  plot_delete(action) {
    const i = this.state.plots.findIndex((p) => p.id === action.id);
    if (i !== -1) {
      this.state.plots.splice(i, 1);
      const plot = this.state.plots[i];
      if (!action.parentId && plot?.parentId) action.parentId = plot.parentId;
    }
  },
  plot_nestedEdits(action) {
    const plot = this.state.plots.find((p) => p.id === action.id);
    if (!plot) {
      throw new CustomError(`missing plot config for id='${action.id}' in store.plot_edit_nested`, {
        name: "MISSING_PLOT_CONFIG",
        level: "warn"
      });
    }
    for (const edit of action.edits) {
      const lastKey = edit.nestedKeys.pop();
      const obj = edit.nestedKeys.reduce((obj2, key) => obj2[key], plot);
      obj[lastKey] = edit.value;
    }
    if (!action.parentId && plot.parentId) action.parentId = plot.parentId;
  },
  // TODO: delete this action? does not seem to be used
  async plot_splice(action) {
    for (const a of action.subactions) {
      await this.actions[a.type].call(this, a);
    }
  },
  filter_replace(action) {
    if ("filter0" in action) {
      this.state.termfilter.filter0 = action.filter0;
      return;
    }
    const replacementFilter = action.filter ? action.filter : { type: "tvslst", join: "", in: 1, lst: [] };
    if (!action.filter.tag) {
      this.state.termfilter.filter = replacementFilter;
    } else {
      const filter = getFilterItemByTag(this.state.termfilter.filter, action.filter.tag);
      if (!filter) throw `cannot replace missing filter with tag '${action.filter.tag}'`;
      const parent = findParent(this.state.termfilter.filter, filter.$id);
      if (parent == filter) {
        this.state.termfilter.filter = replacementFilter;
      } else {
        const i = parent.lst.indexOf(filter);
        parent.lst[i] = replacementFilter;
      }
    }
    if (this.app.opts.app?.onFilterChange) this.app.opts.app.onFilterChange(this.state.plots);
  },
  cache_termq({ termId, q }) {
    if (!termId) throw `missing termId for caching custom term.q`;
    if (!q?.reuseId) throw `missing or empty tw.q.reuseId as cache identifier for term='${termId}'`;
    const cache = this.state.reuse.customTermQ.byId;
    if (!cache[termId]) cache[termId] = {};
    cache[termId][q.reuseId] = q;
    for (const plot of this.state.plots) {
      if (!(plot.chartType in getTwsByChartType)) continue;
      const twlst = getTwsByChartType[plot.chartType](plot);
      for (const tw of twlst) {
        if (tw?.q?.reuseId === q.reuseId) tw.q = q;
      }
    }
  },
  uncache_termq({ term, q }) {
    if (!term.id) throw `missing term.id for uncaching custom term.q`;
    if (!q.reuseId) throw `missing qname as uncache identifier for term.id='${term.id}'`;
    const cache = this.state.reuse.customTermQ.byId[term.id];
    if (!cache) throw `missing term.q cache for term.id='${term.id}`;
    if (!(q.reuseId in cache)) console.warn(`q.reuseId='${q.cacheid}' not cached for term.id='${term.id}'`);
    else {
      delete cache[q.reuseId];
      for (const plot of this.state.plots) {
        if (!(plot.chartType in getTwsByChartType)) continue;
        const twlst = getTwsByChartType[plot.chartType](plot);
        for (const tw of twlst) {
          if (tw.q.reuseId === q.reuseId) {
            delete tw.q.reuseId;
            delete tw.q.name;
          }
        }
      }
    }
  },
  add_customTerm(action) {
    this.state.customTerms.push(action.obj);
  },
  delete_customTerm({ name }) {
    const i = this.state.customTerms.findIndex((i2) => i2.name == name);
    if (i != -1) this.state.customTerms.splice(i, 1);
  },
  add_group(action) {
    if (this.state.nav.header_mode != "hidden") {
      const group = action.obj;
      const name = `Group ${this.state.groups.length + 1}`;
      const samplelstTW = getSamplelstTW([group]);
      const appGroup = {
        name,
        filter: getFilter(samplelstTW),
        plotId: group.plotId
      };
      this.state.groups.push(appGroup);
      this.state.nav.activeTab = 1;
    } else if ("plotId" in action.obj) {
      const plot = this.state.plots.find((p) => p.id == action.obj.plotId);
      if (plot.groups) {
        action.obj.index = plot.groups.length;
        action.obj.name = `Group ${plot.groups.length + 1}`;
        plot.groups.push(action.obj);
      }
    }
  },
  rename_group(action) {
    const index = action.index;
    const newName = action.newName;
    if (this.state.nav.header_mode != "hidden") {
      this.state.groups[index].name = newName;
    } else {
      for (const plot of this.state.plots) {
        if (plot?.groups) {
          plot.groups[index].name = newName;
        }
      }
    }
  },
  change_color_group(action) {
    const index = action.index;
    const newColor = action.newColor;
    if (this.state.nav.header_mode != "hidden") {
      this.state.groups[index].color = newColor;
    } else {
      for (const plot of this.state.plots) {
        if (plot?.groups) {
          plot.groups[index].color = newColor;
        }
      }
    }
  },
  delete_group({ name }) {
    if (this.state.nav.header_mode != "hidden") {
      const i = this.state.groups.findIndex((i2) => i2.name == name);
      if (i != -1) this.state.groups.splice(i, 1);
    } else {
      for (const plot of this.state.plots) {
        if (plot?.groups) {
          const j = plot.groups.findIndex((j2) => j2.name == name);
          if (j != -1) plot.groups.splice(j, 1);
        }
      }
    }
  }
};
var getNestedChartSeriesDataTws = (plot) => [plot.term0, plot.term, plot.term2].filter((d) => !!d);
var getTwsByChartType = {
  summary: getNestedChartSeriesDataTws,
  survival: getNestedChartSeriesDataTws,
  cuminc: getNestedChartSeriesDataTws,
  regression: (plot) => [plot.outcome, ...plot.independent].filter((d) => !!d),
  matrix: (plot) => plot.termgroups.reduce((arr, grp) => {
    arr.push(...grp.lst);
    return arr;
  }, [])
};
var storeInit = StoreApi.getInitFxn(MassStore);

export {
  storeInit
};
//# sourceMappingURL=chunk-HKSNADSX.js.map
