import { Observable } from "rxjs";
import { map, switchMap, tap } from "rxjs/operators";
import { Tip } from "./models/types";
import { EnoFactory } from "./EnoFactory";
import { send } from "./send";
import { IEnSrvOptions } from "./IEnSrvOptions";
import { checkBatchForError } from "./error";
import { get } from "lodash";
import { getLangs } from "./locale";

export interface IFormulaOptions {
  context?: Tip;
  contextBranch?: Tip;
  vars?: object;
  lang?: string | string[];
}

// Evaluates a formula on EnSrv
export function evalFormula(
  formulaStr: string,
  enSrvOptions: IEnSrvOptions,
  formulaOptions?: IFormulaOptions
): Observable<string[]> {
  const enoFactory = new EnoFactory("op/formula", "security/policy/op");
  enoFactory.setField("op/formula:formula", [formulaStr]);
  if (formulaOptions) {
    if (formulaOptions.context) {
      enoFactory.setField("op/formula:context", [formulaOptions.context]);
      if (formulaOptions.contextBranch) {
        enoFactory.setField("op/formula:context-branch", [
          formulaOptions.contextBranch,
        ]);
      }
    }
    if (formulaOptions.vars) {
      enoFactory.setField("op/formula:vars", [
        JSON.stringify(formulaOptions.vars),
      ]);
    }
  }

  return getLangs(enSrvOptions, get(formulaOptions, "lang")).pipe(
    map((langs) => {
      enoFactory.setField("op/formula:lang", langs);
      return enoFactory.makeEno();
    }),
    switchMap((formulaOp) => {
      return send([formulaOp], enSrvOptions).pipe(
        tap(checkBatchForError),
        map((batch) => {
          for (let i = 0; i < batch.length; i++) {
            const eno = batch[i];
            if (
              eno.getType() === "response/formula" &&
              eno.getFieldStringValue("response/formula:op") === formulaOp.tip
            ) {
              return eno.getFieldValues("response/formula:result");
            }
          }
          throw new Error("error/message/server/internal");
        })
      );
    })
  );
}
