
import axios from "axios";
import * as MediaLibrary from "expo-media-library";
import env from "../env";
import { client } from "../graphql/client";
import { INSERT_NEW_DOCUMENT } from "../graphql/documents.queries";
import { axiosAuthInterceptorId } from "./CleanAxios";
//import { splitClient } from "../components/screen-main/screen-main.component";

//Context: currentUserEmail, bodyshop, jobid, invoiceid

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

export const handleUpload = async (ev, context) => {
  const { mediaId, onError, onSuccess, onProgress } = ev;
  const { bodyshop, jobId, photo } = context;
  try {
    console.log("**** I GOT HERE AT LEAST ****")
    const imageData = await MediaLibrary.getAssetInfoAsync(mediaId);
    const imageUri = imageData.localUri || imageData.uri

    const newFile = await (
      await fetch(imageUri)
    ).blob();

    let key = `${bodyshop.id}/${jobId}/${replaceAccents(
      imageData.filename || imageUri.split("/").pop()
    ).replace(/[^A-Z0-9]+/gi, "_")}-${new Date().getTime()}.${extension}`


    const res =
      await uploadToImgproxy(
        key,
        mediaId,
        imageData,
        extension,
        newFile.type, //Filetype
        newFile, //File
        onError,
        onSuccess,
        onProgress,
        context
      )

    return res;
  } catch (error) {
    console.log("Error creating upload promise", error.message, error.stack);
    if (onError) onError(error.message);

    return {
      success: false,
      error: error.message,
      stack: error.stack,
      mediaId,
    };
  }
};

export const uploadToImgproxy = async (
  key,
  mediaId,
  imageData,
  extension,
  fileType,
  file,
  onError,
  onSuccess,
  onProgress,
  context
) => {
  const { bodyshop, jobId, uploaded_by } = context;

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

  if (signedURLResponse.status !== 200) {
    console.log("Error Getting Signed URL", signedURLResponse.statusText);
    if (onError) onError(signedURLResponse.statusText);
    return { success: false, error: signedURLResponse.statusText };
  }
  const { presignedUrl: preSignedUploadUrlToS3, key: s3Key } =
    signedURLResponse.data.signedUrls[0];

  let options = {
    headers: {
      "Content-Type": fileType,
      "Content-Length": file.size,
    },
    transformRequest: [(data) => data], //Dave had this magical solution because Axios makes no sense.
    onUploadProgress: (e) => {
      if (onProgress) onProgress({ percent: e.loaded / e.total, loaded: e.loaded });
    },
  };


  try {
    await new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();
      xhr.open("PUT", preSignedUploadUrlToS3);
      xhr.setRequestHeader("Content-Type", fileType);

      xhr.upload.onprogress = (event) => {
        console.log("*** ~ awaitnewPromise ~ event:", event);
        if (onProgress && event.lengthComputable) {
          onProgress({ percent: event.loaded / event.total, loaded: event.loaded });
        }
      };

      xhr.onload = () => {
        if (xhr.status === 200) {
          resolve();
        } else {
          reject(new Error(`Upload failed: ${xhr.statusText}`));
        }
      };

      xhr.onerror = (req, event) => {
        reject(new Error("Network error"));
      };

      xhr.send(file);
    });
  } catch (error) {
    console.log("Error uploading to S3", error.message, error.stack);
    if (onError) onError(error.message);

    return {
      success: false,
      error: error.message,
      stack: error.stack,
      mediaId,
    };
  }

  const documentInsert = await client.mutate({
    mutation: INSERT_NEW_DOCUMENT,
    variables: {
      docInput: [
        {
          ...(jobId ? { jobid: jobId } : {}),
          uploaded_by: uploaded_by,
          key: s3Key,
          type: fileType,
          extension: extension,
          bodyshopid: bodyshop.id,
          size: file.size,
          ...(imageData.creationTime
            ? { takenat: new Date(imageData.creationTime) }
            : {}),
        },
      ],
    },
  });

  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,
      });
  } else {
    if (onError) onError(JSON.stringify(documentInsert.errors));
    return {
      success: false,
      error: JSON.stringify(documentInsert.errors),
      mediaId,
    };
  }

  return { success: true, mediaId };
};

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

export function formatBytes(a, b = 2) {
  if (0 === a || !a || isNaN(a)) return "0 Bytes";
  const c = 0 > b ? 0 : b,
    d = Math.floor(Math.log(a) / Math.log(1024));

  const parsedFloat = parseFloat((a / Math.pow(1024, d)).toFixed(c))
  if (isNaN(parsedFloat)) {
    return "0 Bytes";
  }
  return (
    parsedFloat +
    " " +
    ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"][d]
  );
}

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