/*
 This file is part of GNU Taler
 (C) 2022-2025 Taler Systems S.A.

 GNU Taler is free software; you can redistribute it and/or modify it under the
 terms of the GNU General Public License as published by the Free Software
 Foundation; either version 3, or (at your option) any later version.

 GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

 You should have received a copy of the GNU General Public License along with
 GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
import type {
  AmlProgramRequirement,
  AvailableMeasureSummary,
  KycCheckInformation,
  MeasureInformation,
} from "@gnu-taler/taler-util";

export type MeasureInfo = ProcedureMeasure | FormMeasure | InfoMeasure;

export type ProcedureMeasure = {
  type: "procedure";
  name: string;
  programName: string;
  program: AmlProgramRequirement;
  context?: object;
};
export type FormMeasure = {
  type: "form";
  name: string;
  programName?: string;
  program?: AmlProgramRequirement;
  checkName: string;
  check: KycCheckInformation;
  context?: object;
};
export type InfoMeasure = {
  type: "info";
  name: string;
  checkName: string;
  check: KycCheckInformation;
  context?: object;
};
export type UiMeasureInformation = {
  procedures: ProcedureMeasure[];
  forms: FormMeasure[];
  info: InfoMeasure[];
};
/**
 * Take a list of measures and fills it with information from server for the UI
 *
 * If measureList is not present then measureMap is going to be used
 * If measureMap is not present then serverMeasures.roots is going to be used
 *
 * @param serverMeasures reference from the server, where the real info is
 * @param opts.measureList a list of measures from which the information is needed
 * @param opts.measureMap a map of measures from which the information is needed
 * @returns
 */
export function computeMeasureInformation(
  serverMeasures: AvailableMeasureSummary | undefined,
  opts: {
    measureList?: MeasureInformation[];
    measureMap?: Record<string, MeasureInformation> | undefined;
  } = {},
): UiMeasureInformation {
  const init: UiMeasureInformation = { forms: [], procedures: [], info: [] };
  if (!serverMeasures) {
    return init;
  }

  type MeasuerList = [string | undefined, MeasureInformation][];
  const measures: MeasuerList = opts.measureList
    ? opts.measureList.map((m) => [undefined, m]) // we don't have the names in this case
    : opts.measureMap
      ? Object.entries(opts.measureMap)
      : Object.entries(serverMeasures.roots);

  return measures.reduce((prev, [key, value]) => {
    const measure = buildMeasureInformation(serverMeasures, key, value);
    if (measure) {
      switch (measure.type) {
        case "procedure": {
          prev.procedures.push(measure);
          break;
        }
        case "form": {
          prev.forms.push(measure);
          break;
        }
        case "info": {
          prev.info.push(measure);
          break;
        }
      }
    }
    return prev;
  }, init);
}

/**
 *
 * @param serverMeasures server information about measures, checks and programs
 * @param name the name of the measure
 * @param measure the incomplete measure
 * @returns
 */
function buildMeasureInformation(
  serverMeasures: AvailableMeasureSummary,
  name: string | undefined,
  measure: MeasureInformation,
): MeasureInfo | undefined {
  if (measure.check_name !== "SKIP") {
    if (!measure.prog_name) {
      const r: MeasureInfo = {
        type: "info",
        name: name ?? "",
        context: measure.context,
        checkName: measure.check_name,
        check: serverMeasures.checks[measure.check_name],
        // custom: true,
      };
      return r;
    } else {
      const r: MeasureInfo = {
        type: "form",
        name: name ?? "",
        context: measure.context,
        programName: measure.prog_name,
        program: serverMeasures.programs[measure.prog_name],
        checkName: measure.check_name,
        check: serverMeasures.checks[measure.check_name],
        // custom: false,
      };
      return r;
    }
  } else {
    if (!measure.prog_name) {
      console.error(`ERROR: program name can't be empty for measure "${name}"`);
      return undefined;
    }
    const r: MeasureInfo = {
      type: "procedure",
      name: name ?? "",
      context: measure.context,
      programName: measure.prog_name,
      program: serverMeasures.programs[measure.prog_name],
      // custom: false,
    };
    return r;
  }
}
