// The Lucia project.
// Copyright 2023-2026 DSP, inc.  All rights reserved.
// Authors:
//   chiayin.kuo@dsp.im (chiayin), 2023/1/31
//   imacat.yang@dsp.im (imacat), 2023/9/23
//   cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/**
 * @module stores/conformance Conformance checking store for managing
 *   rule definitions, conformance check results, and report data.
 */

import { defineStore } from "pinia";
import moment from "moment";
import { Decimal } from "decimal.js";
import abbreviateNumber from "@/module/abbreviateNumber.js";
import apiClient from "@/api/client.js";
import apiError from "@/module/apiError.js";

/**
 * Returns the API base path for the current conformance check,
 * prioritizing temp IDs over created IDs, and filter over log.
 * @param {object} state - The store state.
 * @returns {string} The API base path.
 */
function getCheckApiBase(state) {
  const {
    conformanceFilterTempCheckId,
    conformanceLogTempCheckId,
    conformanceFilterCreateCheckId,
    conformanceLogCreateCheckId,
  } = state;
  if (conformanceFilterTempCheckId !== null)
    return `/api/temp-filter-checks/${conformanceFilterTempCheckId}`;
  if (conformanceLogTempCheckId !== null)
    return `/api/temp-log-checks/${conformanceLogTempCheckId}`;
  if (conformanceFilterCreateCheckId !== null)
    return `/api/filter-checks/${conformanceFilterCreateCheckId}`;
  if (conformanceLogCreateCheckId !== null)
    return `/api/log-checks/${conformanceLogCreateCheckId}`;
  return "";
}

/**
 * Returns the API path for conformance params/temp-checks,
 * prioritizing filter over log.
 * @param {object} state - The store state.
 * @returns {{prefix: string, idParam: string}} The API prefix and ID query param.
 */
function getFileTypeApi(state) {
  const { conformanceFilterId, conformanceLogId } = state;
  if (conformanceFilterId !== null) {
    return { prefix: "filter", idParam: `filter_id=${conformanceFilterId}` };
  }
  return { prefix: "log", idParam: `log_id=${conformanceLogId}` };
}

/** Pinia store for conformance checking and rule management. */
export const useConformanceStore = defineStore("conformanceStore", {
  state: () => ({
    conformanceLogId: null, // Log file
    conformanceFilterId: null, // Filter file
    conformanceLogTempCheckId: null, // Temporary check ID for log (before saving)
    conformanceFilterTempCheckId: null, // Temporary check ID for filter (before saving)
    conformanceLogCreateCheckId: null, // Created check ID for log (rule, after saving)
    conformanceFilterCreateCheckId: null, // Created check ID for filter (rule, after saving)
    allConformanceTask: [],
    allCfmSeqStart: [],
    allCfmSeqEnd: [],
    allProcessingTime: {},
    allWaitingTime: {},
    allCycleTime: {},
    allConformanceTempReportData: null,
    allRouteFile: null,
    allIssueTraces: null,
    allTaskSeq: null,
    allCases: null,
    allLoopTraces: null,
    allLoopTaskSeq: null,
    allLoopCases: null,
    selectedRuleType: "Have activity", // radio
    selectedActivitySequence: "Start & End", // radio
    selectedMode: "Directly follows", // radio
    selectedProcessScope: "End to end", // radio
    selectedActSeqMore: "All", // radio
    selectedActSeqFromTo: "From", // radio
    infinite404: null,
    isStartSelected: null, // Whether start is selected in linked start & end selection
    isEndSelected: null, // Whether end is selected in linked start & end selection
    conformanceRuleData: null, // create checkId's data to save
    isUpdateConformance: false, // Show modal after successful save
    conformanceFileName: null, // File name displayed in the save success modal
  }),
  getters: {
    conformanceAllTasks: (state) => {
      return state.allConformanceTask;
    },
    conformanceTask: (state) => {
      return state.allConformanceTask.map((i) => i.label);
    },
    cfmSeqStart: (state) => {
      return state.allCfmSeqStart;
    },
    cfmSeqEnd: (state) => {
      return state.allCfmSeqEnd;
    },
    cfmPtEteWhole: (state) => {
      return state.allProcessingTime.end_to_end.whole;
    },
    cfmPtEteStart: (state) => {
      return state.allProcessingTime.end_to_end.starts_with;
    },
    cfmPtEteEnd: (state) => {
      return state.allProcessingTime.end_to_end.ends_with;
    },
    cfmPtEteSE: (state) => {
      return state.allProcessingTime.end_to_end.start_end;
    },
    cfmPtPStart: (state) => {
      return state.allProcessingTime.partial.starts_with;
    },
    cfmPtPEnd: (state) => {
      return state.allProcessingTime.partial.ends_with;
    },
    cfmPtPSE: (state) => {
      return state.allProcessingTime.partial.start_end;
    },
    cfmWtEteWhole: (state) => {
      return state.allWaitingTime.end_to_end.whole;
    },
    cfmWtEteStart: (state) => {
      return state.allWaitingTime.end_to_end.starts_with;
    },
    cfmWtEteEnd: (state) => {
      return state.allWaitingTime.end_to_end.ends_with;
    },
    cfmWtEteSE: (state) => {
      return state.allWaitingTime.end_to_end.start_end;
    },
    cfmWtPStart: (state) => {
      return state.allWaitingTime.partial.starts_with;
    },
    cfmWtPEnd: (state) => {
      return state.allWaitingTime.partial.ends_with;
    },
    cfmWtPSE: (state) => {
      return state.allWaitingTime.partial.start_end;
    },
    cfmCtEteWhole: (state) => {
      return state.allCycleTime.end_to_end.whole;
    },
    cfmCtEteStart: (state) => {
      return state.allCycleTime.end_to_end.starts_with;
    },
    cfmCtEteEnd: (state) => {
      return state.allCycleTime.end_to_end.ends_with;
    },
    cfmCtEteSE: (state) => {
      return state.allCycleTime.end_to_end.start_end;
    },
    conformanceTempReportData: (state) => {
      return state.allConformanceTempReportData;
    },
    routeFile: (state) => {
      return state.allRouteFile;
    },
    issueTraces: (state) => {
      return state.allIssueTraces;
    },
    taskSeq: (state) => {
      return state.allTaskSeq;
    },
    cases: (state) => {
      if (state.allCases !== null) {
        return state.allCases.map((c) => {
          const facets = c.facets.map((fac) => {
            const copy = { ...fac };
            switch (copy.type) {
              case "dummy": //sonar-qube
              case "duration-list":
                copy.value = copy.value.map((v) =>
                  v === null
                    ? null
                    : abbreviateNumber(new Decimal(v.toFixed(2))),
                );
                copy.value = copy.value.map((v) => (v ?? "").trim()).join(", ");
                break;
              default:
                break;
            }
            return copy;
          });
          const attributes = c.attributes.map((att) => {
            const copy = { ...att };
            switch (copy.type) {
              case "date":
                copy.value =
                  copy.value === null
                    ? null
                    : moment(copy.value).format("YYYY/MM/DD HH:mm:ss");
                break;
              case "float":
                copy.value =
                  copy.value === null
                    ? null
                    : new Decimal(copy.value).toFixed(2);
                break;
              default:
                break;
            }
            return copy;
          });
          return {
            ...c,
            started_at: moment(c.started_at).format("YYYY/MM/DD HH:mm"),
            completed_at: moment(c.completed_at).format("YYYY/MM/DD HH:mm"),
            facets,
            attributes,
          };
        });
      }
      return [];
    },
    loopTraces: (state) => {
      return state.allLoopTraces;
    },
    loopTaskSeq: (state) => {
      return state.allLoopTaskSeq;
    },
    loopCases: (state) => {
      if (state.allLoopCases !== null) {
        return state.allLoopCases.map((c) => {
          const attributes = c.attributes.map((att) => {
            const copy = { ...att };
            switch (copy.type) {
              case "date":
                copy.value =
                  copy.value === null
                    ? null
                    : moment(copy.value).format("YYYY/MM/DD HH:mm:ss");
                break;
              case "float":
                copy.value =
                  copy.value === null
                    ? null
                    : new Decimal(copy.value).toFixed(2);
                break;
              default:
                break;
            }
            return copy;
          });
          return {
            ...c,
            started_at: moment(c.started_at).format("YYYY/MM/DD HH:mm"),
            completed_at: moment(c.completed_at).format("YYYY/MM/DD HH:mm"),
            attributes,
          };
        });
      }
      return [];
    },
  },
  actions: {
    /**
     * fetch Log Conformance Parameters api for Rule Settings.
     */
    async getConformanceParams() {
      const { prefix, idParam } = getFileTypeApi(this);
      const api = `/api/${prefix}-checks/params?${idParam}`;
      try {
        const response = await apiClient.get(api);
        this.allConformanceTask = response.data.tasks;
        this.allCfmSeqStart = response.data.sources;
        this.allCfmSeqEnd = response.data.sinks;
        this.allProcessingTime = response.data.processing_time;
        this.allWaitingTime = response.data.waiting_time;
        this.allCycleTime = response.data.cycle_time;
      } catch (error) {
        apiError(error, "Failed to load the Conformance Parameters.");
      }
    },
    /**
     * Creates a new temporary check for a log.
     * @param {object} data - The request payload for the backend.
     */
    async addConformanceCheckId(data) {
      const { prefix, idParam } = getFileTypeApi(this);
      const api = `/api/temp-${prefix}-checks?${idParam}`;

      try {
        const response = await apiClient.post(api, data);
        if (prefix === "filter") {
          this.conformanceFilterTempCheckId = response.data.id;
        } else {
          this.conformanceLogTempCheckId = response.data.id;
        }
      } catch (error) {
        apiError(error, "Failed to add the Temporary Check for a file.");
      }
    },
    /**
     * Get the Temporary Log Conformance Report
     * @param {boolean} getRouteFile - Whether called to get the route file (used outside the Conformance page).
     */
    async getConformanceReport(getRouteFile = false) {
      const api = getCheckApiBase(this);
      try {
        const response = await apiClient.get(api);
        if (getRouteFile) {
          this.allRouteFile = response.data.file;
        } else {
          this.allConformanceTempReportData = response.data;
        }
      } catch (error) {
        apiError(error, "Failed to Get the Temporary Log Conformance Report.");
      }
    },
    /**
     * Get the detail of a temporary log conformance issue.
     * @param {number} issueNo - The issue number.
     */
    async getConformanceIssue(issueNo) {
      const api = `${getCheckApiBase(this)}/issues/${issueNo}`;
      try {
        const response = await apiClient.get(api);
        this.allIssueTraces = response.data.traces;
      } catch (error) {
        apiError(
          error,
          "Failed to Get the detail of a temporary log conformance issue.",
        );
      }
    },
    /**
     * Get the Trace Details of a Temporary Log Conformance lssue.
     * @param {number} issueNo - The issue number.
     * @param {number} traceId - The trace ID.
     * @param {number} start - The starting index for loading traces.
     */
    async getConformanceTraceDetail(issueNo, traceId, start) {
      const api = `${getCheckApiBase(this)}/issues/${issueNo}/traces/${traceId}?start=${start}&page_size=20`;
      try {
        const response = await apiClient.get(api);
        this.allTaskSeq = response.data.task_seq;
        this.allCases = response.data.cases;
        return response.data.cases;
      } catch (error) {
        if (error.response?.status === 404) {
          this.infinite404 = 404;
          return null;
        }
        apiError(
          error,
          "Failed to Get the detail of a temporary log conformance issue.",
        );
        return null;
      }
    },
    /**
     * Get the Details of a Temporary Log Conformance Loop.
     * @param {number} loopNo - The loop number.
     */
    async getConformanceLoop(loopNo) {
      const api = `${getCheckApiBase(this)}/loops/${loopNo}`;
      try {
        const response = await apiClient.get(api);
        this.allLoopTraces = response.data.traces;
      } catch (error) {
        apiError(
          error,
          "Failed to Get the detail of a temporary log conformance loop.",
        );
      }
    },
    /**
     * Get the Trace Details of a Temporary Log Conformance Loops.
     * @param {number} loopNo - The loop number.
     * @param {number} traceId - The trace ID.
     * @param {number} start - The starting index for loading traces.
     */
    async getConformanceLoopsTraceDetail(loopNo, traceId, start) {
      const api = `${getCheckApiBase(this)}/loops/${loopNo}/traces/${traceId}?start=${start}&page_size=20`;
      try {
        const response = await apiClient.get(api);
        this.allLoopTaskSeq = response.data.task_seq;
        this.allLoopCases = response.data.cases;
        return response.data.cases;
      } catch (error) {
        if (error.response?.status === 404) {
          this.infinite404 = 404;
          return null;
        }
        apiError(
          error,
          "Failed to Get the detail of a temporary log conformance loop.",
        );
        return null;
      }
    },
    /**
     * Add a New Log Conformance Check, Save the log file.
     * @param {string} value file's name
     */
    async addConformanceCreateCheckId(value) {
      const { prefix, idParam } = getFileTypeApi(this);
      const api = `/api/${prefix}-checks?${idParam}`;
      const data = {
        name: value,
        rule: this.conformanceRuleData,
      };

      try {
        const response = await apiClient.post(api, data);
        if (prefix === "filter") {
          this.conformanceFilterCreateCheckId = response.data.id;
          this.conformanceFilterTempCheckId = null;
        } else {
          this.conformanceLogCreateCheckId = response.data.id;
          this.conformanceLogTempCheckId = null;
        }
      } catch (error) {
        apiError(error, "Failed to add the Conformance Check for a file.");
      }
    },
    /**
     * Update an Existing Conformance Check, log and filter
     */
    async updateConformance() {
      const api = getCheckApiBase(this);
      const data = this.conformanceRuleData;

      try {
        const response = await apiClient.put(api, data);
        this.isUpdateConformance = response.status === 200;
        this.conformanceLogTempCheckId = null;
        this.conformanceFilterTempCheckId = null;
      } catch (error) {
        apiError(error, "Failed to update an Existing Conformance.");
      }
    },
    /**
     * Set the state value of conformance log creation check ID
     * @param {string} createCheckID
     */
    setConformanceLogCreateCheckId(createCheckID) {
      this.conformanceLogCreateCheckId = createCheckID;
    },
  },
});
