import axios from "axios";
import i18n from "i18next";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_NEW_DOCUMENT } from "../../graphql/documents.queries";
import { axiosAuthInterceptorId } from "../../utils/CleanAxios";
import client from "../../utils/GraphQLClient";
import exifr from "exifr";
import { store } from "../../redux/store";

//Context: currentUserEmail, bodyshop, jobid, invoiceid

//Required to prevent headers from getting set and rejected from Cloudinary.
var cleanAxios = axios.create();
cleanAxios.interceptors.request.eject(axiosAuthInterceptorId);

export const handleUpload = (ev, context, notification) => {
  logImEXEvent("document_upload", { filetype: ev.file.type });

  const { onError, onSuccess, onProgress } = ev;
  const { bodyshop, jobId } = context;

  const fileName = ev.file.name || ev.filename;

  let key = `${bodyshop.id}/${jobId}/${replaceAccents(fileName).replace(/[^A-Z0-9]+/gi, "_")}-${new Date().getTime()}`;
  let extension = fileName.split(".").pop();
  uploadToCloudinary(key, extension, ev.file.type, ev.file, onError, onSuccess, onProgress, context, notification);
};

export const uploadToCloudinary = async (
  key,
  extension,
  fileType,
  file,
  onError,
  onSuccess,
  onProgress,
  context,
  notification
) => {
  const { bodyshop, jobId, billId, uploaded_by, callback, tagsArray } = context;

  //Set variables for getting the signed URL.
  let timestamp = Math.floor(Date.now() / 1000);
  let public_id = key;
  let tags = `${bodyshop.imexshopid},${tagsArray ? tagsArray.map((tag) => `${tag},`) : ""}`;
  //  let eager = import.meta.env.VITE_APP_CLOUDINARY_THUMB_TRANSFORMATIONS;

  //Get the signed url.

  const upload_preset = fileType.startsWith("video") ? "incoming_upload_video" : "incoming_upload";

  const signedURLResponse = await axios.post("/media/sign", {
    public_id: public_id,
    tags: tags,
    timestamp: timestamp,
    upload_preset: upload_preset
  });

  if (signedURLResponse.status !== 200) {
    if (onError) onError(signedURLResponse.statusText);
    notification.error({
      title: i18n.t("documents.errors.getpresignurl", {
        message: signedURLResponse.statusText
      })
    });
    return;
  }

  //Build request to end to cloudinary.
  var signature = signedURLResponse.data;
  var options = {
    headers: { "X-Requested-With": "XMLHttpRequest" },
    onUploadProgress: (e) => {
      if (onProgress) onProgress({ percent: (e.loaded / e.total) * 100 });
    }
  };

  const formData = new FormData();
  formData.append("file", file);

  formData.append("upload_preset", upload_preset);

  formData.append("api_key", import.meta.env.VITE_APP_CLOUDINARY_API_KEY);
  formData.append("public_id", public_id);
  formData.append("tags", tags);
  formData.append("timestamp", timestamp);
  formData.append("signature", signature);

  const cloudinaryUploadResponse = await cleanAxios.post(
    `${import.meta.env.VITE_APP_CLOUDINARY_ENDPOINT_API}/${DetermineFileType(fileType)}/upload`,
    formData,
    {
      ...options
    }
  );

  if (cloudinaryUploadResponse.status !== 200) {
    if (onError) {
      onError(cloudinaryUploadResponse.statusText);
    }

    try {
      axios.post("/newlog", {
        message: "client-cloudinary-upload-error",
        type: "error",
        user: store.getState().user.email,
        object: cloudinaryUploadResponse
      });
    } catch {
      // NO OP
    }

    notification.error({
      title: i18n.t("documents.errors.insert", {
        message: cloudinaryUploadResponse.statusText
      })
    });
    return;
  }

  //Insert the document with the matching key.
  let takenat;
  if (fileType.includes("image")) {
    try {
      const exif = await exifr.parse(file);

      takenat = exif && exif.DateTimeOriginal;
    } catch {
      console.log("Unable to parse image file for EXIF Data");
    }
  }
  const documentInsert = await client.mutate({
    mutation: INSERT_NEW_DOCUMENT,
    variables: {
      docInput: [
        {
          ...(jobId ? { jobid: jobId } : {}),
          ...(billId ? { billid: billId } : {}),
          uploaded_by: uploaded_by,
          key: key,
          type: fileType,
          extension: cloudinaryUploadResponse.data.format || extension,
          bodyshopid: bodyshop.id,
          size: cloudinaryUploadResponse.data.bytes || file.size,
          takenat
        }
      ]
    }
  });
  if (!documentInsert.errors) {
    if (onSuccess)
      onSuccess({
        uid: documentInsert.data.insert_documents.returning[0].id,
        name: documentInsert.data.insert_documents.returning[0].name,
        status: "done",
        key: documentInsert.data.insert_documents.returning[0].key
      });
    notification.success({
      key: "docuploadsuccess",
      title: i18n.t("documents.successes.insert")
    });
    if (callback) {
      callback();
    }
  } else {
    if (onError) onError(JSON.stringify(documentInsert.errors));
    notification.error({
      title: i18n.t("documents.errors.insert", {
        message: JSON.stringify(documentInsert.errors)
      })
    });
    return;
  }
};

//Also needs to be updated in media JS and mobile app.
export function DetermineFileType(filetype) {
  if (!filetype) return "auto";
  else if (filetype.startsWith("image")) return "image";
  else if (filetype.startsWith("video")) return "video";
  else if (filetype.startsWith("application/pdf")) return "image";
  else if (filetype.startsWith("application")) return "raw";

  return "auto";
}

function replaceAccents(str) {
  // Verifies if the String has accents and replace them
  if (str.search(/[\xC0-\xFF]/g) > -1) {
    str = str
      .replace(/[\xC0-\xC5]/g, "A")
      .replace(/[\xC6]/g, "AE")
      .replace(/[\xC7]/g, "C")
      .replace(/[\xC8-\xCB]/g, "E")
      .replace(/[\xCC-\xCF]/g, "I")
      .replace(/[\xD0]/g, "D")
      .replace(/[\xD1]/g, "N")
      .replace(/[\xD2-\xD6\xD8]/g, "O")
      .replace(/[\xD9-\xDC]/g, "U")
      .replace(/[\xDD]/g, "Y")
      .replace(/[\xDE]/g, "P")
      .replace(/[\xE0-\xE5]/g, "a")
      .replace(/[\xE6]/g, "ae")
      .replace(/[\xE7]/g, "c")
      .replace(/[\xE8-\xEB]/g, "e")
      .replace(/[\xEC-\xEF]/g, "i")
      .replace(/[\xF1]/g, "n")
      .replace(/[\xF2-\xF6\xF8]/g, "o")
      .replace(/[\xF9-\xFC]/g, "u")
      .replace(/[\xFE]/g, "p")
      .replace(/[\xFD\xFF]/g, "y");
  }

  return str;
}
