// 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/files File management store for listing, uploading,
 *   renaming, downloading, and deleting log/filter/rule files.
 */

import { defineStore } from "pinia";
import apiClient from "@/api/client.js";
import moment from "moment";
import apiError from "@/module/apiError.js";
import Swal from "sweetalert2";
import {
  uploadFailedFirst,
  uploadFailedSecond,
  uploadloader,
  uploadSuccess,
  deleteSuccess,
} from "@/module/alertModal.js";
import { useLoadingStore } from "@/stores/loading";

/** Map of file type to API path segment. */
const FILE_TYPE_PATHS = {
  log: "logs",
  filter: "filters",
  "log-check": "log-checks",
  "filter-check": "filter-checks",
};

/**
 * Returns the API base path for a file type and ID.
 * @param {string} type - The file type.
 * @param {number} id - The file ID.
 * @returns {string} The API base path.
 */
function getFileApiBase(type, id) {
  return `/api/${FILE_TYPE_PATHS[type]}/${id}`;
}

/** Pinia store for file CRUD operations and upload workflow. */
export const useFilesStore = defineStore("filesStore", {
  state: () => ({
    allEventFiles: [
      {
        parentLog: "",
        fileType: "",
        ownerName: "",
      },
    ],
    switchFilesTagData: {
      ALL: ["Log", "Filter", "Rule", "Design"],
      DISCOVER: ["Log", "Filter", "Rule"],
      COMPARE: ["Log", "Filter"],
      DESIGN: ["Log", "Design"],
    },
    filesTag: "ALL",
    httpStatus: 200,
    uploadId: null,
    allUploadDetail: null,
    uploadLogId: null,
    uploadFileName: null,
    allDependentsData: null,
  }),
  getters: {
    /**
     * Get allFiles and switch files tag
     */
    allFiles: (state) => {
      let result = state.allEventFiles;
      const data = state.switchFilesTagData;
      const filesTag = state.filesTag;

      result = result.filter((file) => data[filesTag].includes(file.fileType));

      return result;
    },
    /**
     * Get upload preview
     */
    uploadDetail: (state) => {
      return state.allUploadDetail;
    },
    /**
     * Get dependents of files data
     */
    dependentsData: (state) => {
      return state.allDependentsData;
    },
  },
  actions: {
    /**
     * Fetch All Files api
     */
    async fetchAllFiles() {
      const api = "/api/files";

      try {
        const response = await apiClient.get(api);

        this.allEventFiles = response.data;
        this.allEventFiles.forEach((o) => {
          let icon = "";
          let fileType = "";
          let parentLog = o.name;

          switch (o.type) {
            case "log":
              icon = "work_history";
              fileType = "Log";
              parentLog = o.name;
              break;
            case "filter":
              icon = "tornado";
              fileType = "Filter";
              parentLog = o.parent.name;
              break;
            case "log-check":
            case "filter-check":
              icon = "local_police";
              fileType = "Rule";
              parentLog = o.parent.name;
              break;
            case "design":
              icon = "shape_line";
              fileType = "Design";
              parentLog = o.name;
              break;
          }
          o.icon = icon;
          o.parentLog = parentLog;
          o.fileType = fileType;
          o.ownerName = o.owner.name;
          o.updated_base = o.updated_at;
          o.accessed_base = o.accessed_at;
          o.updated_at = moment(o.updated_at)
            .utcOffset("+08:00")
            .format("YYYY-MM-DD HH:mm");
          o.accessed_at = o.accessed_at
            ? moment(o.accessed_at)
                .utcOffset("+08:00")
                .format("YYYY-MM-DD HH:mm")
            : null;
        });
      } catch (error) {
        apiError(error, "Failed to load the files.");
      }
    },
    /**
     * Uploads a CSV log file (first stage).
     * @param {Object} fromData - The form data to send to the backend.
     */
    async upload(fromData) {
      const api = "/api/logs/csv-uploads";
      const config = {
        data: true,
        headers: {
          "Content-Type": "multipart/form-data",
        },
      };

      uploadloader(); // Show loading progress bar
      try {
        const response = await apiClient.post(api, fromData, config);

        this.uploadId = response.data.id;
        this.$router.push({ name: "Upload" });
        Swal.close(); // Close the loading progress bar
      } catch (error) {
        if (error.response?.status === 422) {
          // msg: 'not in UTF-8' | 'insufficient columns' | 'the csv file is empty' | 'the filename does not ends with .csv'
          // type: 'encoding' | 'insufficient_columns' | 'empty' | 'name_suffix'
          const detail = error.response.data.detail;
          if (Array.isArray(detail) && detail.length > 0) {
            const loc = detail[0].loc;
            uploadFailedFirst(detail[0].type, detail[0].msg, Array.isArray(loc) && loc.length > 2 ? loc[2] : undefined);
          }
        } else {
          Swal.close(); // Close the loading progress bar
          apiError(error, "Failed to upload the files.");
        }
      }
    },
    /**
     * Fetch upload detail
     */
    async getUploadDetail() {
      const uploadId = this.uploadId;
      const api = `/api/logs/csv-uploads/${uploadId}`;

      try {
        const response = await apiClient.get(api);
        this.allUploadDetail = response.data.preview;
      } catch (error) {
        apiError(error, "Failed to get upload detail.");
      }
    },
    /**
     * Adds a log from an uploaded CSV log file (second stage).
     * @param {Object} data - The request payload for the backend.
     */
    async uploadLog(data) {
      const uploadId = this.uploadId;
      const api = `/api/logs/csv-uploads/${uploadId}`;

      uploadloader(); // Show loading progress bar
      try {
        const response = await apiClient.post(api, data);

        this.uploadLogId = response.data.id;
        Swal.close(); // Close the loading progress bar
        await this.rename(); // Rename the file
        await uploadSuccess();
        this.$router.push({ name: "Files" });
      } catch (error) {
        if (error.response?.status === 422) {
          const raw = error.response.data?.detail;
          const detail = Array.isArray(raw) ? [...raw] : [];

          uploadFailedSecond(detail);
        } else {
          Swal.close(); // Close the loading progress bar
          apiError(error, "Failed to upload the log files.");
        }
      }
    },
    /**
     * Rename a Log
     * @param {string} type - The file type ('log', 'filter', 'log-check', or 'filter-check').
     * @param {number} id - The file ID.
     * @param {string} fileName - The file name.
     */
    async rename(type, id, fileName) {
      // If uploadLogId exists, set id and type accordingly; then check the file type.
      if (this.uploadId && this.uploadFileName) {
        type = "log";
        id = this.uploadLogId;
        fileName = this.uploadFileName;
      }

      const data = { name: fileName };
      try {
        await apiClient.put(`${getFileApiBase(type, id)}/name`, data);
        this.uploadFileName = null;
        await this.fetchAllFiles();
      } catch (error) {
        apiError(error, "Failed to rename.");
      }
    },
    /**
     * Get the Dependents of the files
     * @param {string} type - The file type ('log', 'filter', 'log-check', or 'filter-check').
     * @param {number} id - The file ID.
     */
    async getDependents(type, id) {
      try {
        const response = await apiClient.get(
          `${getFileApiBase(type, id)}/dependents`,
        );
        this.allDependentsData = response.data;
      } catch (error) {
        apiError(error, "Failed to get Dependents of the files.");
      }
    },
    /**
     * Delete file
     * @param {string} type - The file type ('log', 'filter', 'log-check', or 'filter-check').
     * @param {number} id - The file ID.
     */
    async deleteFile(type, id) {
      if (id === null || id === undefined || Number.isNaN(Number(id))) {
        console.error("Delete File API Error: invalid id");
        return;
      }
      const loading = useLoadingStore();
      loading.isLoading = true;
      try {
        await apiClient.delete(getFileApiBase(type, id));
        await this.fetchAllFiles();
        await deleteSuccess();
      } catch (error) {
        apiError(error, "Failed to delete.");
      } finally {
        loading.isLoading = false;
      }
    },
    /**
     * Removes a deletion record permanently.
     * @param {number} id - The file ID.
     */
    async deletionRecord(id) {
      let api = "";

      const loading = useLoadingStore();
      loading.isLoading = true;
      api = `/api/deletion/${id}`;
      try {
        await apiClient.delete(api);
      } catch (error) {
        apiError(error, "Failed to Remove a Deletion Record.");
      } finally {
        loading.isLoading = false;
      }
    },
    /**
     * Download file as CSV
     * @param {string} type - The file type ('log', 'filter', 'log-check', or 'filter-check').
     * @param {number} id - The file ID.
     * @param {string} fileName - The file name.
     */
    async downloadFileCSV(type, id, fileName) {
      if (type !== "log" && type !== "filter") return;
      try {
        const response = await apiClient.get(`${getFileApiBase(type, id)}/csv`);
        const csvData = response.data;
        const blob = new Blob([csvData], { type: "text/csv" });
        const url = globalThis.URL.createObjectURL(blob);
        const link = document.createElement("a");

        link.href = url;
        link.download = `${fileName}.csv`;
        link.click();
        globalThis.URL.revokeObjectURL(url);
      } catch (error) {
        apiError(error, "Failed to download.");
      }
    },
  },
});
