import {
  mayRunSnplstTask
} from "./chunk-7OYOC7XY.js";

// termsetting/handlers/snplst.ts
function getHandler(self) {
  return {
    getPillName() {
      return self.term.name;
    },
    getPillStatus() {
      if (!self.term?.snps) return;
      const invalid = self.term.snps.reduce((i, j) => i + (j.invalid || !j.alleles ? 1 : 0), 0);
      if (invalid) return { text: invalid + " invalid" };
    },
    validateQ(data) {
      validateQ(data);
    },
    async showEditMenu(div) {
      await makeEditMenu(self, div);
    }
  };
}
async function makeEditMenu(self, div0) {
  const div = div0.append("div").style("margin", "15px");
  const select_ancestry = await mayRestrictAncestry(self, div);
  const inputUIholder = div.append("div");
  const textarea = inputUIholder.append("textarea").attr("rows", 5).attr("cols", 20).attr("placeholder", "Enter variants");
  const prompt = inputUIholder.append("div").style("opacity", 0.8).style("font-size", ".8em");
  prompt.append("div").html(
    `Enter a list of variants (dbSNP accessions). One variant per line. Max 500 allowed.${self.usecase.target == "dataDownload" ? "" : "<br>Effect alleles (alleles tested against in the analysis) can be specified here or<br>after variant validation."}`
  );
  const exampleInput = [
    { id: "rs1641548", effectAllele: "T" },
    { id: "rs4968204", effectAllele: "C" },
    { id: "rs9893249", effectAllele: "T" },
    { id: "rs1042522", effectAllele: "G" }
  ];
  const example1 = prompt.append("ul").style("display", "inline-block").style("list-style-type", "none").style("padding", "0px");
  const example2 = prompt.append("ul").style("display", self.usecase.target == "dataDownload" ? "none" : "inline-block").style("list-style-type", "none").style("padding", "0px").style("margin-left", "40px");
  example1.append("li").style("text-decoration", "underline").text("Example:");
  example2.append("li").style("text-decoration", "underline").text("Example (with effect alleles):");
  for (const variant of exampleInput) {
    example1.append("li").text(variant.id);
    example2.append("li").html(`${variant.id}&nbsp;${variant.effectAllele}`);
  }
  const validateBtn = inputUIholder.append("div").style("margin-top", "15px").append("button").text("Validate variants").on("click", async () => {
    const snps = parseSnpFromText(textarea);
    if (!snps.length) return window.alert("No valid variants");
    if (!self.term) {
      self.term = { id: makeId(), type: "snplst" };
    }
    if (self.term.snps) {
      for (const n of snps) {
        if (self.term.snps.find((i) => i.rsid == n.rsid)) continue;
        self.term.snps.push(n);
      }
    } else {
      self.term.snps = snps;
    }
    self.term.name = getTermName(self.term.snps);
    if (!self.q) self.q = {};
    self.doNotHideTipInMain = true;
    if (self.dom.pill_termname) {
      self.dom.pill_termname.text(self.term.name);
    }
    validateBtn.text("Validating...").property("disabled", true);
    try {
      await validateInput(self);
    } catch (e) {
      alert("Error: " + (e.message || e));
      validateBtn.text("Validate variants").property("disabled", false);
      return;
    }
    if (!Number.isFinite(self.q.AFcutoff)) {
      self.q.AFcutoff = 5;
      input_AFcutoff.property("value", self.q.AFcutoff);
    }
    if (!Number.isInteger(self.q.alleleType)) {
      self.q.alleleType = snps.find((snp) => snp.effectAllele) ? 2 : 0;
      select_alleleType.property("selectedIndex", self.q.alleleType);
      div.select(".sjpp-allele-type-hint").text(getAlleleTypeHint(select_alleleType));
    }
    if (!Number.isInteger(self.q.geneticModel)) {
      self.q.geneticModel = 0;
      select_geneticModel.property("selectedIndex", self.q.geneticModel);
    }
    if (!Number.isInteger(self.q.missingGenotype)) {
      self.q.missingGenotype = 0;
      select_missingGenotype.property("selectedIndex", self.q.missingGenotype);
    }
    if (select_ancestry) {
      self.q.restrictAncestry = select_ancestry.node().options[select_ancestry.property("selectedIndex")].__ancestry_obj;
    }
    await makeSampleSummary(self);
    inputUIholder.style("display", "none");
    editUIholder.style("display", "");
    if (self.dom.snplst_table) {
      renderSnpEditTable(self, select_alleleType);
    } else {
      initSnpEditTable(snplstTableDiv, self, select_alleleType);
    }
  });
  const editUIholder = div.append("div");
  editUIholder.append("p").append("span").html("&laquo; Back to input").attr("class", "sja_clbtext").on("click", () => {
    inputUIholder.style("display", "");
    editUIholder.style("display", "none");
    validateBtn.text("Validate variants").property("disabled", false);
  });
  const snplstTableDiv = editUIholder.append("div").style("margin-bottom", "25px");
  const [input_AFcutoff, select_alleleType, select_geneticModel, select_missingGenotype] = makeSnpSelect(
    editUIholder.append("div").attr("class", "sjpp-snp-select"),
    self,
    "snplst"
  );
  if (self.usecase.target == "dataDownload") editUIholder.select(".sjpp-snp-select").style("display", "none");
  const btnRow = editUIholder.append("div").style("margin-top", "20px");
  btnRow.append("button").text("Submit").on("click", async () => {
    self.term.snps = self.term.snps.filter((i) => !i.tobe_deleted);
    if (self.term.snps.find((snp) => !snp.effectAllele))
      return window.alert("Effect allele not specified for one or more variants");
    self.term.name = getTermName(self.term.snps);
    if (self.dom.pill_termname) {
      self.dom.pill_termname.text(self.term.name);
    }
    try {
      await validateInput(self);
    } catch (e) {
      alert("Error: " + (e.message || e));
    }
    {
      const v = Number(input_AFcutoff.property("value"));
      self.q.AFcutoff = v < 0 || v >= 100 ? 5 : v;
    }
    self.q.alleleType = select_alleleType.property("selectedIndex");
    self.q.geneticModel = select_geneticModel.property("selectedIndex");
    self.q.missingGenotype = select_missingGenotype.property("selectedIndex");
    if (select_ancestry) {
      self.q.restrictAncestry = select_ancestry.node().options[select_ancestry.property("selectedIndex")].__ancestry_obj;
    }
    await makeSampleSummary(self);
    if (self.dom.snplst_table) {
      renderSnpEditTable(self, select_alleleType);
    } else {
      initSnpEditTable(snplstTableDiv, self, select_alleleType);
    }
    delete self.doNotHideTipInMain;
    self.api.runCallback();
    self.view.updateUI();
  });
  btnRow.append("span").style("padding-left", "15px").style("opacity", 0.8).style("font-size", ".8em").text(
    self.usecase.target == "dataDownload" ? "" : "Variants will be treated as separate covariates in the regression model"
  );
  if (self.term?.snps?.length) {
    initSnpEditTable(snplstTableDiv, self, select_alleleType);
    inputUIholder.style("display", "none");
  } else {
    editUIholder.style("display", "none");
  }
}
function initSnpEditTable(div, self, select_alleleType) {
  self.dom.snplst_table = div.append("table");
  renderSnpEditTable(self, select_alleleType);
  div.append("p").style("opacity", 0.8).style("font-size", ".8em").style("display", self.usecase.target == "dataDownload" ? "none" : "block").html("Effect alleles are highlighted in red. Click on an allele to set as the effect allele.");
}
function renderSnpEditTable(self, select_alleleType) {
  self.dom.snplst_table.selectAll("*").remove();
  const title_tr = self.dom.snplst_table.append("tr").style("opacity", 0.4);
  const col_titles = [
    { title: "Variants" },
    { title: "# genotyped<br>samples" },
    { title: "Reference allele<br>(frequency)" },
    { title: "Alternative allele(s)<br>(frequency)" },
    { title: "Genotype<br>(frequency)" },
    { title: "Delete" }
  ];
  if (self.usecase.target == "dataDownload") col_titles.splice(4, 1);
  col_titles.forEach((c) => {
    title_tr.append("td").html(c.title).style("font-size", ".8em").style("text-align", "center").style("padding", "8px");
  });
  if (!self.term.snps) throw `Missing term.snps [snplst.ts renderSnpEditTable()]`;
  for (const [i, snp] of self.term.snps.entries()) {
    let invalid_snp = false;
    if (snp.invalid || !snp.alleles && !snp.gt2count) invalid_snp = "NOT ANNNOTATED IN COHORT";
    const rowcolor = (i + 2) % 2 ? "#eee" : "#fff";
    const tr = self.dom.snplst_table.append("tr").style("background", rowcolor);
    tr.append("td").text(snp.rsid).style("padding", "8px");
    const sample_count = invalid_snp ? invalid_snp : snp.gt2count ? Object.values(snp.gt2count).reduce((a, b) => a + b, 0) : `Missing sample count for snp`;
    tr.append("td").style("text-align", "center").text(sample_count);
    const ref_allele_td = tr.append("td");
    const alt_allele_td = tr.append("td");
    if (!invalid_snp) {
      if (!snp.alleles) throw `Missing alleles for snp = ${snp} [snplst.ts renderSnpEditTable()]`;
      const effectAllele = self.q.snp2effAle ? self.q.snp2effAle[snp.rsid] : void 0;
      const refAllele = snp.alleles.find((s) => s.isRef);
      const altAlleles = snp.alleles.filter((s) => !s.isRef);
      if (self.usecase.target == "dataDownload") {
        const allele_freq = (refAllele.count * 100 / (sample_count * 2)).toFixed(2);
        const refAlleleText = `${refAllele.allele} (${allele_freq}%)`;
        ref_allele_td.style("text-align", "center").style("padding", "8px").text(refAlleleText);
        const altAllelesText = altAlleles.map((al) => {
          const allele_freq2 = (al.count * 100 / (sample_count * 2)).toFixed(2);
          return `${al.allele} (${allele_freq2}%)`;
        }).join(", ");
        alt_allele_td.style("text-align", "center").style("padding", "8px").text(altAllelesText);
      } else {
        createAlleleBtn(refAllele, ref_allele_td, effectAllele, sample_count, snp, rowcolor, tr);
        for (const al of altAlleles.values()) {
          createAlleleBtn(al, alt_allele_td, effectAllele, sample_count, snp, rowcolor, tr);
        }
      }
    }
    if (self.usecase.target != "dataDownload") {
      const gt_td = tr.append("td");
      if (!invalid_snp) {
        const refGT = self.q.snp2refGrp ? self.q.snp2refGrp[snp.rsid] : void 0;
        if (!snp.gt2count) throw `Missing gt2count for snp = ${snp} [snplst.ts renderSnpEditTable()]`;
        for (const [gt, freq] of Object.entries(snp.gt2count)) {
          const gt_freq = (freq * 100 / sample_count).toFixed(2);
          const gt_div = gt_td.append("div").style("display", "inline-block").style("border", gt == refGT ? "3px solid #ff0000" : `3px solid ${rowcolor}`);
          gt_div.append("div").style("display", "inline-block").style("padding", "3px 5px").text(`${gt}`);
          gt_div.append("div").style("display", "inline-block").style("padding", "0px 6px 0px 2px").style("font-size", ".8em").text(`(${gt_freq}%)`);
        }
      }
    }
    const snp_checkbox = tr.append("td").style("text-align", "center").append("input").attr("type", "checkbox").on("change", () => {
      snp.tobe_deleted = snp_checkbox.node().checked;
    });
  }
  function createAlleleBtn(al, td, effectAllele, sample_count, snp, rowcolor, tr) {
    const allele_freq = (al.count * 100 / (sample_count * 2)).toFixed(2);
    const allele_div = td.style("text-align", "center").append("button").style("display", "inline-block").style("margin", "0px 3px").style("padding", "3px 7px").style("border-radius", "3px").style("background-color", "#d9ead3").style("border", al.allele == effectAllele ? "3px solid #ff0000" : `3px solid ${rowcolor}`).on("mouseover", () => {
      if (snp.effectAllele && snp.effectAllele == al.allele) return;
      else {
        allele_div.style("background-color", "#fff2cc").style("cursor", "pointer");
      }
    }).on("mouseout", () => {
      if (snp.effectAllele && snp.effectAllele == al.allele) return;
      else {
        allele_div.style("background", "#d9ead3");
      }
    }).on("click", () => {
      snp.effectAllele = al.allele;
      tr.selectAll("button").style("border", "none");
      allele_div.style("border", "3px solid #ff0000").style("background", "#d9ead3");
      select_alleleType.selectAll("option").nodes()[2].selected = "selected";
    });
    allele_div.append("div").style("display", "inline-block").text(`${al.allele}  `);
    allele_div.append("div").style("display", "inline-block").style("margin", "0px 5px").style("font-size", ".8em").text(`(${allele_freq}%)`);
  }
}
function getTermName(snps) {
  if (snps.length == 1) {
    const s = snps[0];
    if (s.rsid) return s.rsid;
    return s.chr + ":" + s.pos;
  }
  return snps.length + " variant" + (snps.length > 1 ? "s" : "");
}
function parseSnpFromText(textarea) {
  const text = textarea.property("value");
  const snps = [];
  for (const tmp of text.trim().split("\n")) {
    const [rsid, ale] = tmp.trim().split(/[\s\t]/);
    if (!rsid) continue;
    if (snps.find((i) => i.rsid == rsid)) continue;
    const s = { rsid };
    if (ale) s.effectAllele = ale;
    snps.push(s);
  }
  if (snps.length > 500) return snps.slice(0, 500);
  return snps;
}
function snp2text(self) {
  if (!self.term || !self.term.snps) return "";
  const lines = [];
  for (const a of self.term.snps) {
    const l = a.rsid + (a.effectAllele ? " " + a.effectAllele : "");
    lines.push(l);
  }
  return lines.join("\n");
}
async function validateInput(self) {
  const data = await self.vocabApi.validateSnps({ text: snp2text(self) });
  if (data.error) throw data.error;
  self.q.cacheid = data.cacheid;
  if (!self.term.snps) throw `Missing term.snps [snplst.ts validateInput()]`;
  for (const [i, s] of self.term.snps.entries()) {
    const s1 = data.snps[i];
    s.snpid = s1.snpid;
    s.invalid = s1.invalid;
  }
}
async function makeSampleSummary(self) {
  const body = { cacheid: self.q.cacheid };
  const extraFilters = [];
  let f;
  if (self.q.restrictAncestry) {
    if (self.filter) {
      f = structuredClone(self.filter);
      const idx = f.lst.findIndex((i) => i.tvs?.term?.id == self.q.restrictAncestry.tvs.term.id);
      if (idx != -1) {
        f.lst[idx] = { type: "tvs", tvs: self.q.restrictAncestry.tvs };
      } else {
        extraFilters.push({ type: "tvs", tvs: self.q.restrictAncestry.tvs });
      }
    } else {
      extraFilters.push({ type: "tvs", tvs: self.q.restrictAncestry.tvs });
    }
  } else {
    if (self.filter) extraFilters.push(self.filter);
  }
  const filter = { type: "tvslst", join: "and", lst: [...extraFilters] };
  if (f) filter.lst.push(f);
  const data = await self.vocabApi.getCategories(self.term, filter, body);
  mayRunSnplstTask(
    {
      term: self.term,
      q: self.q
    },
    //quick fix for $id being required in BaseTw type
    data
  );
}
function validateQ(data) {
  if (!Number.isFinite(data.q.AFcutoff)) throw "AFcutoff is not number";
  if (data.q.AFcutoff < 0 || data.q.AFcutoff > 100) throw "AFcutoff is not within 0 to 100";
  if (![0, 1, 2].includes(data.q.alleleType)) throw "alleleType value is not one of 0/1";
  if (![0, 1, 2, 3].includes(data.q.geneticModel)) throw "geneticModel value is not one of 0/1";
  if (![0, 1].includes(data.q.missingGenotype)) throw "missingGenotype value is not one of 0/1";
}
async function fillTW(tw, vocabApi) {
  try {
    validateQ(tw);
  } catch (e) {
    throw "snplst validateQ(): " + e;
  }
  if (!Array.isArray(tw.term.snps)) throw "tw.term.snps[] is not an array";
  if (tw.term.snps.length == 0) throw "tw.term.snps[] array is 0 length";
  if (!tw.term.name) tw.term.name = getTermName(tw.term.snps);
  if (tw.id == void 0 || tw.id == "") {
    if (tw.term.id == void 0 || tw.term.id == "") {
      tw.term.id = makeId();
    }
    tw.id = tw.term.id;
  } else {
    if (tw.term.id == void 0 || tw.term.id == "") {
      tw.term.id = tw.id;
    }
  }
  await validateInput({
    term: tw.term,
    q: tw.q,
    vocabApi
  });
}
function makeId() {
  return "snplst" + Math.random();
}
function makeSnpSelect(div, self, termtype) {
  let input_AFcutoff_label, input_AFcutoff, select_missingGenotype;
  {
    input_AFcutoff_label = div.append("div").style("opacity", 0.5).style("font-size", ".8em").style("margin-bottom", "2px").text("EFFECT ALLELE FREQUENCY CUTOFF");
    self.dom.input_AFcutoff_label = input_AFcutoff_label;
    const row = div.append("div");
    input_AFcutoff = row.append("input").attr("type", "number").attr("min", 0).attr("max", 100).property("value", 5).style("border-color", "#858585").style("margin", "0px");
    row.append("span").text("%");
    let text;
    if (termtype == "snplst") {
      text = "Variants below this cutoff are discarded";
    } else {
      if (self.usecase.regressionType == "linear") {
        text = "Variants below this cutoff are analyzed by the Wilcoxon rank sum test";
      } else if (self.usecase.regressionType == "logistic") {
        text = "Variants below this cutoff are analyzed by the Fisher's exact test";
      } else {
        text = "Variants below this cutoff are analyzed by the cumulative incidence test";
      }
    }
    row.append("span").style("margin-left", "10px").style("opacity", 0.5).style("font-size", ".7em").style("font-style", "italic").text(text);
  }
  div.append("div").style("margin-top", "15px").style("opacity", 0.5).style("font-size", ".8em").style("margin-bottom", "2px").text("SET EFFECT ALLELE AS");
  const select_alleleType = div.append("select");
  select_alleleType.append("option").text("Minor allele");
  select_alleleType.append("option").text("Alternative allele");
  if (termtype !== "snplocus") {
    select_alleleType.append("option").text("Custom allele");
  }
  div.append("div").attr("class", "sjpp-allele-type-hint").style("display", "inline-block").style("margin-left", "15px").style("opacity", 0.5).style("font-size", ".7em").style("font-style", "italic").text(getAlleleTypeHint(select_alleleType));
  select_alleleType.on("change", async () => {
    div.select(".sjpp-allele-type-hint").text(getAlleleTypeHint(select_alleleType));
    self.q.alleleType = select_alleleType.property("selectedIndex");
    if (termtype !== "snplocus") {
      if (!self.term.snps) throw `Missing term.snps [snplst.ts makeSnpSelect()]`;
      for (const snp of self.term.snps) {
        snp.effectAllele = false;
      }
      await makeSampleSummary(self);
      renderSnpEditTable(self, select_alleleType);
    }
  });
  div.append("div").style("margin-top", "15px").style("margin-bottom", "2px").style("opacity", 0.5).style("font-size", ".8em").text("GENETIC MODEL");
  const select_geneticModel = div.append("select");
  select_geneticModel.append("option").text("Additive: EE=2, EN=1, NN=0");
  select_geneticModel.append("option").text("Dominant: EE=1, EN=1, NN=0");
  select_geneticModel.append("option").text("Recessive: EE=1, EN=0, NN=0");
  select_geneticModel.append("option").text("By genotype: EE and EN compared to NN");
  div.append("div").style("display", "inline-block").style("margin-left", "15px").style("opacity", 0.5).style("font-size", ".7em").style("font-style", "italic").html("E = effect allele; N = non-effect allele");
  const missingGenotypeLabel = div.append("div").style("margin-top", "15px").style("opacity", 0.5).style("font-size", ".7em");
  if (termtype == "snplocus") {
    missingGenotypeLabel.text("Samples with missing genotypes are dropped.");
  } else {
    missingGenotypeLabel.text("MISSING GENOTYPE").style("font-size", ".8em").style("margin-bottom", "2px");
    select_missingGenotype = div.append("select");
    select_missingGenotype.append("option").text("Impute as homozygous for non-effect allele");
    select_missingGenotype.append("option").text("Drop sample");
  }
  if (Number.isInteger(self.q?.alleleType)) select_alleleType.property("selectedIndex", self.q.alleleType);
  if (Number.isFinite(self.q?.AFcutoff)) input_AFcutoff.property("value", self.q.AFcutoff);
  if (Number.isInteger(self.q?.geneticModel)) select_geneticModel.property("selectedIndex", self.q.geneticModel);
  if (select_missingGenotype && Number.isInteger(self.q?.missingGenotype))
    select_missingGenotype.property("selectedIndex", self.q.missingGenotype);
  return [input_AFcutoff, select_alleleType, select_geneticModel, select_missingGenotype];
}
async function mayRestrictAncestry(self, holder) {
  if (self.q?.doNotRestrictAncestry) return;
  const tdbcfg = await self.vocabApi.getTermdbConfig();
  if (!tdbcfg.restrictAncestries) return;
  const row = holder.append("div").style("margin-bottom", "15px");
  self.dom.restrictAncestriesRow = row;
  row.append("span").text("Restrict analysis to").style("margin-right", "5px").style("opacity", 0.5);
  const select = row.append("select");
  for (const ancestry of tdbcfg.restrictAncestries) {
    const opt = select.append("option").text(ancestry.name);
    opt.node().__ancestry_obj = ancestry;
  }
  if (self.q && self.q.restrictAncestry) {
    const i = tdbcfg.restrictAncestries.findIndex((i2) => i2.name == self.q.restrictAncestry.name);
    if (i == -1) throw "unknown restrictAncestry: " + self.q.restrictAncestry.name;
    select.property("selectedIndex", i);
  }
  return select;
}
function getAlleleTypeHint(select_alleleType) {
  let hint;
  const i = select_alleleType.property("selectedIndex");
  if (i == 0) {
    hint = "For multi-allelic variants, the second most common allele is used as the effect allele";
  } else if (i == 1) {
    hint = "For multi-allelic variants, the most common alternative allele is used as the effect allele";
  } else {
    hint = "";
  }
  return hint;
}

export {
  getHandler,
  fillTW,
  makeSnpSelect,
  mayRestrictAncestry
};
//# sourceMappingURL=chunk-T5BDYCFO.js.map
