import { SELFSURVEYSERVICE_DOCUMENT_URL } from "react-native-digitcamera/src/Api/ApiUrls";
import {
  UEEndTimer,
  UELogEvent,
  UEStartTimer,
} from "react-native-digitcamera/src/common/libEventLog/UserExperior/LibUserExperior";
import {
  DTLogEvent,
  DTLogError,
} from "../common/libEventLog/Dynatrace/LibDynatrace";
import Database from "../../../../Components/common/DAsyncStorage";
import DeviceInfo from "react-native-device-info";
import { forwardRef, useEffect, useState } from "react";
import { Alert, Platform } from "react-native";
import { async } from "validate.js";
import RNFetchBlob from "rn-fetch-blob";
import { offlinePIDataToRealm } from "react-native-digitcamera/src/Screens/OfflineFlow/CommonFunc";
import { isCVFlow } from "react-native-digitcamera/src/common/Constants";
const { getImei } = require("react-native-imei");
const iOS = Platform.OS === "ios";
var uri;
/* 
[IMPORTANT]
directly importing redux store as store
gives access to disptach/getState methods

I am using this because we cannot use hooks in this file/non react component/plain js file
*/

import store from "react-native-digitcamera/src/redux/store";
import moment from "moment";
import { updateNotificationContent } from "react-native-digitcamera/src/Screens/SilentUpload/BackgroundTasksUtils";
import CONFIGS from "./SilentUpload/NotificationsConfig";
export async function UploadFile(
  mediaUri,
  DocumentImageName,
  ext,
  failedCallBack,
  uploadSuccess,
  uploadFailure
) {
  var MAC = "";
  var deviceName = "";
  var IP = "";
  var apiLevel = "";
  var IMEI = "";
  let make = "";
  let model = "";
  try {
    var file = "file://";
    var urlArray = mediaUri.split("file://");
    var androidUri = urlArray[urlArray.length - 1];
    uri = iOS ? mediaUri.replace("file://", "") : file + androidUri;
    console.log("*** UploadFile : " + DocumentImageName + " uri: " + uri);
    var PILocation = await Database.get("PILocation");
    var currentLatitude = PILocation?.lat;
    var currentLongitude = PILocation?.long;
    var PIVechicleData = await Database.get("PIVechicleData");
    var activityId = PIVechicleData?.activityId;
    var quoteNumber = PIVechicleData?.quoteNumber;
    var regNumber = PIVechicleData?.licensePlateNumber;
    var activityTypeId = PIVechicleData?.activityTypeId;
    var PIOdometerData = await Database.get("PIOdometerData");

    deviceName = await DeviceInfo.getDeviceName();
    console.log("*** UploadFile getDeviceName: " + deviceName);
    if (activityId == undefined || activityId.length == 0) {
      //this will be background process now so calling Alert.alert wont be supported
      //Alert.alert("Oops!", "Something went wrong, please retry again");
      if (failedCallBack !== undefined) {
        failedCallBack();
      }
    }

    DeviceInfo.getManufacturer().then((manfacturer) => {
      make = manfacturer;
      if (make === undefined || make === null) {
        make = "NA";
      }
    });

    if (iOS) {
      /*
       * getModel() method lags on IOS devices, getDeviceId() is recommended for get device model name;
       */
      model = DeviceInfo.getDeviceId();
    } else {
      model = DeviceInfo.getModel();
    }

    if (model === undefined || model === null) {
      model = "NA";
    }

    MAC = await DeviceInfo.getMacAddress();
    console.log("*** UploadFile getMacAddress: " + MAC);
    IP = await DeviceInfo.getIpAddress();
    console.log("*** UploadFile getIpAddress: " + IP);

    apiLevel = await DeviceInfo.getApiLevel();
    console.log("*** UploadFile getApiLevel: " + apiLevel);
    if (!iOS) {
      if (apiLevel > 28) {
        IMEI = await DeviceInfo.getAndroidId();
      } else {
        const imeiList = await getImei();
        if (imeiList !== undefined) {
          IMEI = imeiList[0];
        }
      }
    }
    console.log("*** UploadFile getImei / getAndroidId: " + IMEI);

    // Calling getFileSize
    getFileSize(uri, DocumentImageName);

    UELogEvent(`uploading started ${DocumentImageName}`, {
      uri: JSON.stringify(uri),
      documentName: JSON.stringify(DocumentImageName),
      ImageName: JSON.stringify(activityId + "_" + DocumentImageName + ext),
    });

    console.log("IMAGE NAME  ", activityId + "_" + DocumentImageName + ext);

    const xhr = new XMLHttpRequest();
    const formData = new FormData();

    console.log(`${DocumentImageName} IMAGE URI ->`, uri);

    await initStatusRealm(
      uri,
      DocumentImageName,
      activityId,
      activityTypeId,
      quoteNumber,
      regNumber
    );

    /*
      [WORKAROUND] [NOT A FIX]
      For some reason, X number of images randomly stuck in uploading state
      I have not figure out the issue, but following workaround fixes the issue for now.

      For Images only, if status of the document/image stays uploading for one minute straight
      We can consider it *failed*, and update status to *failed* in the media status db and redux store under *failedData*
      
      or 

      if status of the document/image stays failed for one minute straight and has not been added in redux store
      we add it forcefully.

      above case happens only when we are either reuploading the documents again or we are uploading the documents 
      via offline flow
    */

    resolveUploadingStatus(uri, DocumentImageName, activityId);

    const started_at = new Date();

    let counter = 0;
    // Start to track api call
    UEStartTimer(SELFSURVEYSERVICE_DOCUMENT_URL);
    DTLogEvent(`${DocumentImageName} Upload Started `);

    const handleProgress = async (event) => {
      let progress = Math.round(event.loaded * 100) / event.total;
      const { uploadSpeed, secondsRemaining } = getEstimatedTime(
        event.loaded,
        event.total,
        started_at
      );
      store.dispatch({
        type: "UPDATE_UPLOAD_PROGRESS_DATA",
        payload: {
          activityId: activityId,
          imageName: DocumentImageName,
          progress: progress,
          uploadSpeed: uploadSpeed,
          secondsRemaining: secondsRemaining,
        },
      });
      // Start to track api call
      // UEStartTimer(SELFSURVEYSERVICE_DOCUMENT_URL);
      console.log(
        "DocumentImageName : ",
        DocumentImageName + " uploaded percentage: ",
        Math.round(event.loaded * 100) / event.total
      );

      if (DocumentImageName && DocumentImageName.includes("VDO")) {
        await Database.update("PIVideoCompleteStatus", {
          completePercentage: progress,
          uploadInfo: getEstimatedTime(event.loaded, event.total, started_at),
        });

        // for update notification
        updateNotificationContent(CONFIGS.BG_KILLED_VIDEO_UPLOAD, {
          max: 100,
          value: progress,
          indeterminate: false,
        });
      }

      if (Math.trunc(progress * 0.1) === counter) {
        console.log(
          `[LOG] . UploadFile . ${DocumentImageName} . Progress - `,
          progress
        );
        DTLogEvent(`${DocumentImageName} - Progress - ${progress}`);
        UELogEvent(`${DocumentImageName} - Progress`, {
          Value: `${progress.toFixed(3)}`,
        });
        counter++;
      }

      console.log(
        "File Uploaded => ",
        Math.round(event.loaded * 100) / event.total
      );
    };
    const documentUploadData = {
      activityId: activityId,
      data: null,
      dataUploadStatus: 0,
      fullName: activityId + "_" + DocumentImageName,
      geoLocation: {
        isBlocked: "",
        deviceName: deviceName,
        latitude: currentLatitude,
        longitude: currentLongitude,
        operatingSystem: DeviceInfo.getSystemVersion(),
        timezone: "Asia/Kolkata",
        deviceTime: null,
        ipAddress: IP,
        deviceIMEI: IMEI,
        macAddress: MAC,
        documentSource: "DigitEdge",
        make: make,
        model: model,
      },
      isLast: "false",
      isLastData: "false",
      isLastDocument: "false",
      isSurveyor: "false",
      manualOdometerReading:
        DocumentImageName == "ODO" ? PIOdometerData?.readings : "",
      source: "DIGITEDGE",
    };

    formData.append("file", {
      uri: uri,
      type: "image/jpeg",
      fileName: activityId + "_" + DocumentImageName,
      name: activityId + "_" + DocumentImageName + ext,
    });
    formData.append("documentData", JSON.stringify(documentUploadData));

    UELogEvent("UploadFile, Documentdata", {
      documentData: JSON.stringify(documentUploadData),
    });

    // UELogEvent("started xhr events in uploadImage", {
    //   formdata: JSON.stringify(formData),
    // });

    xhr.upload.addEventListener("progress", handleProgress);

    xhr.addEventListener("load", async (load) => {
      UEEndTimer(SELFSURVEYSERVICE_DOCUMENT_URL);
      UELogEvent("** UploadFile xhr load", {
        status: JSON.stringify(xhr.status),
        response: JSON.stringify(xhr.response),
      });

      /*
       * /document service is returning status 500
       * once the upload is done
       * if status is 200 video file uploads successfully
       * if status is 500, upload failed.
       */

      console.log(
        `[INFO] . UploadFile . ${DocumentImageName} . Response Status -`,
        xhr.status
      );

      if (xhr.status === 200) {
        //remove from failed data
        store.dispatch({
          type: "REMOVE_FAILED_DATA",
          payload: {
            imageName: DocumentImageName,
            activityId: activityId,
          },
        });

        //remove from uploading data
        store.dispatch({
          type: "REMOVE_UPLOADING_DATA",
          payload: {
            imageName: DocumentImageName,
            activityId: activityId,
          },
        });
        DTLogEvent(
          `${DocumentImageName} Uploaded Successfully | status: ${xhr.status} `
        );
        await updateStatusRealm(
          activityId,
          DocumentImageName,
          "uploaded",
          activityTypeId,
          regNumber
        );
        var PIUploadedCount = await Database.get("PIUploadedCount");
        var uploadedImageCount = PIUploadedCount?.uploadedCount;
        console.log("Uploaded", DocumentImageName);
        uploadedImageCount = uploadedImageCount + 1;
        await Database.update("PIUploadedCount", {
          uploadedCount: uploadedImageCount,
        });

        await offlinePIDataToRealm(false);
        console.log("*** UploadFile, Video upload success.");
        await uploadSuccess();
        UELogEvent("** UploadFile xhr load, if xhr.status 200", {
          status: JSON.stringify(xhr.status),
          response: JSON.stringify(xhr.response),
        });

        return true;
      } else {
        // For
        store.dispatch({
          type: "ADD_FAILED_DATA",
          payload: {
            uri: uri,
            imageName: DocumentImageName,
            activityId: activityId,
          },
        });
        DTLogError(`${DocumentImageName} Upload Abort | status: ${xhr.status}`);
        await updateStatusRealm(
          activityId,
          DocumentImageName,
          "failed",
          activityTypeId,
          regNumber
        );
        offlinePIDataToRealm(false);
        console.log(`*** UploadFile Video upload Failed, status ${xhr.status}`);
        UELogEvent("** UploadFile xhr load if xhr.status 500", {
          status: JSON.stringify(xhr.status),
          response: JSON.stringify(xhr.response),
        });
        await uploadFailure();
        // return false;
      }
    });

    xhr.addEventListener("abort", async () => {
      DTLogError(`${DocumentImageName} Upload Abort | status: ${xhr.status}`);
      UEEndTimer(SELFSURVEYSERVICE_DOCUMENT_URL);
      UELogEvent("UploadFile xhr abort", {
        status: JSON.stringify(xhr.status),
      });
      await updateStatusRealm(
        activityId,
        DocumentImageName,
        "failed",
        activityTypeId,
        regNumber
      );
      await uploadFailure();

      offlinePIDataToRealm(false);
      // return false;
    });

    xhr.addEventListener("error", async (error) => {
      DTLogError(`${DocumentImageName} Upload Error | status: ${xhr.status}`);
      UEEndTimer(SELFSURVEYSERVICE_DOCUMENT_URL);
      UELogEvent("UploadFile xhr error", {
        status: JSON.stringify(xhr.status),
        error: JSON.stringify(error),
      });
      await updateStatusRealm(
        activityId,
        DocumentImageName,
        "failed",
        activityTypeId,
        regNumber
      );
      await uploadFailure();
      offlinePIDataToRealm(false);
      console.log("*** startUpolading error: " + JSON.stringify(error));
      return false;
    });

    xhr.addEventListener("timeout", async () => {
      DTLogError(`${DocumentImageName} Upload Timeout | status: ${xhr.status}`);
      UEEndTimer(SELFSURVEYSERVICE_DOCUMENT_URL);
      UELogEvent("UploadFile xhr timeout", {
        status: JSON.stringify(xhr.status),
      });

      await updateStatusRealm(
        activityId,
        DocumentImageName,
        "failed",
        activityTypeId,
        regNumber
      );
      await uploadFailure();

      offlinePIDataToRealm(false);
      // return false;
    });

    xhr.open("POST", SELFSURVEYSERVICE_DOCUMENT_URL);
    xhr.setRequestHeader(
      "Accept",
      "*/*",
      "Content-Type",
      "multipart/form-data"
    );
    xhr.send(formData);
    UEStartTimer(SELFSURVEYSERVICE_DOCUMENT_URL);
  } catch (error) {
    console.log("UPLOAD ERROR", error);
    DTLogError(
      `${DocumentImageName} UploadFile Catch Error - ${error?.messsage ?? ""}`
    );
    await updateStatusRealm(
      activityId,
      DocumentImageName,
      "failed",
      activityTypeId,
      regNumber
    );
    await uploadFailure();
    offlinePIDataToRealm(false);
    console.log("*** UploadFile catch: " + error);
  }
}

const getFileSize = async (uri, imageName) => {
  /*
   * Using RNFetchBlob.fs.stat(uri) method which retrieves meta information
   * of the file of the *uri*
   * stats.size returns size of the file in bytes
   * the method, getFileSizeInMB converts the bytes into MB
   */
  try {
    const stats = await RNFetchBlob.fs.stat(uri);
    console.log(
      `Camera.js ${imageName} file size: `,
      stats?.size,
      `bytes, ${getFileSizeInMB(stats?.size)} mb`
    );
    UELogEvent(`Camera.js ${imageName} file size in bytes`, {
      "FileSize (bytes)": JSON.stringify(stats?.size),
      "FileSize (MegaBytes)": JSON.stringify(getFileSizeInMB(stats?.size)),
    });
  } catch (e) {
    console.log("Error in UploadFile, getFileSize", e);
  }
};

const getFileSizeInMB = (bytes) => {
  /* converts bytes to megabytes */
  return bytes * 0.000001;
};

const getEstimatedTime = (loaded, total, started_at) => {
  var seconds_elapsed = (new Date().getTime() - started_at.getTime()) / 1000;
  var bytes_per_second = seconds_elapsed ? loaded / seconds_elapsed : 0;
  var Kbytes_per_second = bytes_per_second / 1024;
  var remaining_bytes = total - loaded;
  var seconds_remaining = seconds_elapsed
    ? remaining_bytes / bytes_per_second
    : "calculating";
  return {
    uploadSpeed: Kbytes_per_second,
    secondsRemaining: seconds_remaining,
  };
};

const initStatusRealm = async (
  uri,
  DocumentImageName,
  activityId,
  activityTypeId,
  quoteNumber,
  regNumber
) => {
  store.dispatch({
    type: "ADD_UPLOADING_DATA",
    payload: {
      uri: uri,
      imageName: DocumentImageName,
      activityId: activityId,
    },
  });

  const oldStatus = await Database.get("PIMediaStatus");

  const imageObj = [
    {
      status: "uploading", // uploading, uploaded, failed
      uri: uri,
      imageName: DocumentImageName,
    },
  ];

  const currentActivityObj = [
    {
      activityId: activityId,
      activityTypeId: activityTypeId,
      policyNumber: quoteNumber,
      regNumber: regNumber,
      activityStatus: "IN-PROGRESS", // PENDING, IN-PROGRESS, COMPLETED  // this is the status of activity not a perticular image
      mode: "ONLINE", // ONLINE, OFFLINE  // have to make dyanamic
      mediaUpload: imageObj,
      date: moment().format("DD MMM YYYY"),
    },
  ];

  const newObj = await addMediaStatusObj(oldStatus, currentActivityObj);
  console.log("newObj : ", JSON.stringify(newObj, null, "  "));
  await Database.set("PIMediaStatus", newObj);
  const PIMediaStatus = await Database.get("PIMediaStatus");
  console.log(
    "PIMediaStatus initStatusRealm",
    JSON.stringify(PIMediaStatus, null, "  ")
  );
};

const resolveUploadingStatus = async (uri, DocumentImageName, activityId) => {
  if (DocumentImageName !== "VDO") {
    setTimeout(async () => {
      const msArray = await Database.get("PIMediaStatus");
      const existingItem = msArray.find(
        (item) =>
          item.imageName === DocumentImageName &&
          (item.activityId === activityId || item.activityId === 0)
      );
      console.log("UploadFile uploding -> failed :", existingItem);
      if (existingItem) {
        if (existingItem.status === "uploading") {
          await updateStatusRealm(
            activityId,
            DocumentImageName,
            "failed",
            activityTypeId
          );
          // adding failed data into redux
          store.dispatch({
            type: "ADD_FAILED_DATA",
            payload: {
              uri: uri,
              imageName: DocumentImageName,
              activityId: activityId,
            },
          });
        } else if (existingItem.status === "failed") {
          store.dispatch({
            type: "ADD_FAILED_DATA",
            payload: {
              uri: uri,
              imageName: DocumentImageName,
              activityId: activityId,
            },
          });
        }
      }
    }, 1000 * 60);
  }
};

const updateStatusRealm = async (
  activityId,
  DocumentImageName,
  newStatus,
  activityTypeId,
  regNumber
) => {
  try {
    const PIMediaStatus = await Database.get("PIMediaStatus");

    // getting current activity obj
    const uploadObj = PIMediaStatus.find((item) => {
      return item.activityId === activityId;
    });
    console.log(
      "*** UploadFile updateStatusRealm uploadObj: " + JSON.stringify(uploadObj)
    );
    if (uploadObj != null) {
      var newObj;
      // updating values of Activity
      newObj = {
        ...uploadObj,
        activityId: activityId,
        activityTypeId: activityTypeId,
        regNumber: regNumber,
      };
      // updating value of image
      var updatedImageInfo = newObj.mediaUpload.map((item) => {
        if (item.imageName == DocumentImageName && item.status !== "uploaded") {
          return {
            ...item,
            imageName: DocumentImageName,
            status: newStatus,
            uri: uri,
          };
        }
        return item;
      });

      // final array = previousImages + currentImages // rcb1 ,rcb2 ,vdo
      var finalImages = [{ ...uploadObj, mediaUpload: updatedImageInfo }];

      // for removing last item
      // did this for removing duplication and old obj
      PIMediaStatus.pop();

      console.log(
        "PIMediaStatus after pop() : ",
        JSON.stringify(PIMediaStatus, null, "  ")
      );

      // adding new updated item
      var finalArray = PIMediaStatus.concat(finalImages);
      console.log("finalArray : ", JSON.stringify(finalArray, null, "  "));

      // setting all data in DB
      await Database.set("PIMediaStatus", finalArray);
      const alog = await Database.get("PIMediaStatus");
      console.log("PIMediaStatus", JSON.stringify(alog, null, "  "));
    }
  } catch (error) {
    console.log("Error in updateStatusRealm :", error);
  }
};

const addMediaStatusObj = async (oldStatus, currentActivityObj) => {
  try {
    console.log("currentActivityObj", currentActivityObj);
    var objectTobeUpdate = currentActivityObj[0]?.mediaUpload[0];
    var newObj;

    console.log("oldStatus", JSON.stringify(oldStatus, null, " "));
    console.log(
      "currentActivityObj",
      JSON.stringify(currentActivityObj, null, " ")
    );

    // this will excute only if there is no activity in DB
    if (oldStatus == null || oldStatus == undefined) {
      console.log(
        "oldStatus == null || oldStatus == undefined",
        currentActivityObj
      );
      return currentActivityObj;
    }

    // this will check for activity is present or not
    console.log(
      "currentActivityObj[0]?.activityId : ",
      currentActivityObj[0]?.activityId,
      JSON.stringify(oldStatus, null, "  ")
    );
    var isActivityAlreadyExist = oldStatus.find((item) => {
      return item.activityId === currentActivityObj[0]?.activityId;
    });

    console.log("isActivityAlreadyExist", !!isActivityAlreadyExist);

    if (!!isActivityAlreadyExist) {
      // checking is current image object is already exist or not
      const isImageExist = isActivityAlreadyExist?.mediaUpload?.some((item) => {
        item.imageName === objectTobeUpdate?.imageName;
      });

      // this will check if any image with same name is exist or not

      const isImageAlreadyExist = isActivityAlreadyExist?.mediaUpload?.filter(
        (imageItem) => {
          return imageItem.imageName != objectTobeUpdate.imageName;
        }
      );
      var updatedImageWithOutDuplicates = [
        ...isImageAlreadyExist,
        objectTobeUpdate,
      ];

      newObj = [
        {
          ...isActivityAlreadyExist,
          mediaUpload: updatedImageWithOutDuplicates,
        },
      ];
      console.log(
        "Activity is present and new Image obj : ",
        JSON.stringify(newObj, null, "  ")
      );
      oldStatus.pop();
      return oldStatus.concat(newObj); // adding previous Activity + current different
    } else {
      console.log(
        "Activity is not present so Adding  New Activity ",
        JSON.stringify(currentActivityObj, null, "  ")
      );
      return oldStatus.concat(currentActivityObj);
    }
  } catch (error) {
    console.log("uploadFile.js Error in addMediaObj ", error);
  }
};

export default UploadFile;
