import axios from "axios";
import exifr from "exifr";
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";

//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 extension = fileName.split(".").pop();
  let key = `${bodyshop.id}/${jobId}/${replaceAccents(fileName).replace(/[^A-Z0-9]+/gi, "_")}-${new Date().getTime()}.${extension}`;

  uploadToS3(key, extension, ev.file.type, ev.file, onError, onSuccess, onProgress, context, notification).catch(
    (error) => {
      console.error("Error uploading file to S3", error);
      notification.error({
        title: i18n.t("documents.errors.insert", {
          message: error.message
        })
      });
    }
  );
};

//Handles only 1 file at a time.
export const uploadToS3 = async (
  key,
  extension,
  fileType,
  file,
  onError,
  onSuccess,
  onProgress,
  context,
  notification
) => {
  const { bodyshop, jobId, billId, uploaded_by, callback } = context;

  //Get the signed url allowing us to PUT to S3.
  const signedURLResponse = await axios.post("/media/imgproxy/sign", {
    filenames: [key],
    bodyshopid: bodyshop.id,
    jobid: jobId
  });

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

  //Key should be same as we provided to maintain backwards compatibility.
  const { presignedUrl: preSignedUploadUrlToS3, key: s3Key, contentType } = signedURLResponse.data.signedUrls[0];

  const options = {
    onUploadProgress: (e) => {
      if (onProgress) onProgress({ percent: (e.loaded / e.total) * 100 });
    },
    headers: {
      ...(contentType ? { "Content-Type": fileType } : {})
    }
  };

  try {
    const s3UploadResponse = await cleanAxios.put(preSignedUploadUrlToS3, file, options);
    //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 (error) {
        console.log("Unable to parse image file for EXIF Data", error.message);
      }
    }

    const documentInsert = await client.mutate({
      mutation: INSERT_NEW_DOCUMENT,
      variables: {
        docInput: [
          {
            ...(jobId ? { jobid: jobId } : {}),
            ...(billId ? { billid: billId } : {}),
            uploaded_by: uploaded_by,
            key: s3Key,
            type: fileType,
            extension: s3UploadResponse.data.format || extension,
            bodyshopid: bodyshop.id,
            size: s3UploadResponse.data.bytes || file.size, //Leftover from Cloudinary. We don't do any optimization on upload, so it will always be 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;
    }
  } catch (error) {
    console.log("Error uploading file to S3", error.message, error.stack);
    notification.error({
      title: i18n.t("documents.errors.insert", {
        message: error.message
      })
    });
    if (onError) onError(JSON.stringify(error.message));
  }
};

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;
}
