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,
  wait,
} 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";
import { event } from "react-native-reanimated";
import { initMediaFile } from "react-native-digitcamera/src/Screens/SilentUploadV2/DatabaseHelpers";
export async function UploadFile(
  mediaUri,
  DocumentImageName,
  ext,
  uploadItem,
  failedCallBack,
  uploadSuccess,
  uploadFailure
) {
  var MAC = "";
  var deviceName = "";
  var IP = "";
  var apiLevel = "";
  var IMEI = "";
  let make = "";
  let model = "";
  try {
    UELogEvent("UploadFile, before Documentdata", {
      toBeCreate: "",
    });
    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 activityId = uploadItem.mObjActivityId;
    var quoteNumber = uploadItem.mObjActivityId;
    var regNumber = uploadItem.mRegNumber;
    var activityTypeId = uploadItem.mObjActivityId;
    var imageMode = uploadItem.mMode;
    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 {
        try {
          const imeiList = await getImei();
          if (imeiList !== undefined) {
            IMEI = imeiList[0];
          }
        } catch (error) {
          UELogEvent("UploadFile, error", {
            imeiList: "" + JSON.stringify(error),
          });
        }
      }
    }
    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(uploadItem);

    /*
      [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
    */

    const started_at = new Date();

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

    const handleProgressEvent = async (event) => {
      try {
        let progress = Math.round(event.loaded * 100) / event.total;
        UELogEvent("handleProgressEvent, progress start", {
          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
        );
        UELogEvent("handleProgressEvent, currentProgress", {
          currentProgress: "" + Math.round(event.loaded * 100) / event.total,
        });
      } catch (error) {
        UELogEvent("handleProgressEvent, error", {
          error: "" + JSON.stringify(error),
        });
      }
    };

    const handleLoadEvent = (event) => {
      const onLoad = async (load) => {
        try {
          UEStartTimer(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 || xhr.status === 204 || xhr.status === 400) {
            await updateStatusRealm("uploaded", uploadItem);
            setTimeout(() => {
              const makeSuccess = async () => {
                await uploadSuccess();
              };
              makeSuccess();
            }, 1000);
            UEEndTimer(SELFSURVEYSERVICE_DOCUMENT_URL);

            DTLogEvent(
              `${DocumentImageName} Uploaded Successfully | status: ${xhr.status} `
            );
            var PIUploadedCount = await Database.get("PIUploadedCount");
            var uploadedImageCount = PIUploadedCount?.uploadedCount;
            console.log("Uploaded", DocumentImageName);
            uploadedImageCount = uploadedImageCount + 1;
            await Database.update("PIUploadedCount", {
              uploadedCount: uploadedImageCount,
            });

            console.log("*** UploadFile, Video upload success.");
            UELogEvent("** UploadFile xhr load, if xhr.status 200", {
              status: JSON.stringify(xhr.status),
              response: JSON.stringify(xhr.response),
            });
          } else {
            UEEndTimer(SELFSURVEYSERVICE_DOCUMENT_URL);
            await updateStatusRealm("failed", uploadItem);
            setTimeout(() => {
              const MakeFailure = async () => {
                await uploadFailure();
              };
              MakeFailure();
            }, 1000);

            DTLogError(
              `${DocumentImageName} Upload Abort | status: ${xhr.status}`
            );
            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),
            });
          }
        } catch (error) {
          UELogEvent("handleLoadEvent, error", {
            error: "" + JSON.stringify(error),
          });
        }
      };
      onLoad(event);
    };

    const handleAbortEvent = (event) => {
      const onAbort = async () => {
        try {
          await updateStatusRealm("failed", uploadItem);
          setTimeout(() => {
            const MakeFailure = async () => {
              await uploadFailure();
            };
            MakeFailure();
          }, 1000);

          DTLogError(
            `${DocumentImageName} Upload Abort | status: ${xhr.status}`
          );
          UEEndTimer(SELFSURVEYSERVICE_DOCUMENT_URL);
          UELogEvent("UploadFile xhr abort", {
            status: JSON.stringify(xhr.status),
          });
          // return false;
        } catch (error) {
          UELogEvent("handleAbortEvent, error", {
            error: "" + JSON.stringify(error),
          });
        }
      };
      onAbort();
    };

    const handleErrorEvent = () => {
      const errorEvent = async (error) => {
        try {
          await updateStatusRealm("failed", uploadItem);
          setTimeout(() => {
            const MakeFailure = async () => {
              await uploadFailure();
            };
            MakeFailure();
          }, 1000);

          DTLogError(
            `${DocumentImageName} Upload Error | status: ${xhr.status}`
          );

          UELogEvent("UploadFile xhr error", {
            status: JSON.stringify(xhr.status),
            error: JSON.stringify(error),
          });
          console.log("*** startUpolading error: " + JSON.stringify(error));
        } catch (error) {
          UELogEvent("handleErrorEvent, error", {
            error: "" + JSON.stringify(error),
          });
        }
      };
      errorEvent();
    };

    const handleTimeoutEvent = () => {
      const timeoutEvent = async () => {
        try {
          await updateStatusRealm("failed", uploadItem);
          setTimeout(() => {
            const MakeFailure = async () => {
              await uploadFailure();
            };
            MakeFailure();
          }, 1000);

          DTLogError(
            `${DocumentImageName} Upload Timeout | status: ${xhr.status}`
          );

          UELogEvent("UploadFile xhr timeout", {
            status: JSON.stringify(xhr.status),
          });

          // return false;
        } catch (error) {
          UELogEvent("handleTimeoutEvent, error", {
            error: "" + JSON.stringify(error),
          });
        }
      };
      timeoutEvent();
    };

    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",
      uploadMode: imageMode,
    };

    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", handleProgressEvent);
    xhr.addEventListener("load", handleLoadEvent);
    xhr.addEventListener("abort", handleAbortEvent);
    xhr.addEventListener("error", handleErrorEvent);
    xhr.addEventListener("timeout", handleTimeoutEvent);

    xhr.open("POST", SELFSURVEYSERVICE_DOCUMENT_URL);
    xhr.setRequestHeader(
      "Accept",
      "*/*",
      "Content-Type",
      "multipart/form-data"
    );
    xhr.send(formData);
  } catch (error) {
    await updateStatusRealm("failed", uploadItem);

    console.log("UPLOAD ERROR", error);
    UELogEvent("UploadFile, error", {
      error: "" + JSON.stringify(error),
    });

    DTLogError(
      `${DocumentImageName} UploadFile Catch Error - ${error?.messsage ?? ""}`
    );
    setTimeout(() => {
      const MakeFailure = async () => {
        await uploadFailure();
      };
      MakeFailure();
    }, 1000);
    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) {
    UELogEvent("UploadFile, error", {
      getFileSize: "" + JSON.stringify(e),
    });
    console.log("Error in UploadFile, getFileSize", e);
  }
};

const getFileSizeInMB = (bytes) => {
  if (!bytes) {
    return 0;
  }

  /* converts bytes to megabytes */
  return bytes * 0.000001;
};

const getEstimatedTime = (loaded, total, started_at) => {
  try {
    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,
    };
  } catch (error) {
    UELogEvent("UploadFile, error", {
      getEstimatedTime: "" + JSON.stringify(error),
    });
    return {
      uploadSpeed: 0,
      secondsRemaining: 0,
    };
  }
};

const initStatusRealm = async (uploadItem) => {
  var uri = uploadItem.uri;
  var DocumentImageName = uploadItem.imageName;
  var activityId = uploadItem.mObjActivityId;
  var quoteNumber = uploadItem.mObjActivityId;
  var regNumber = uploadItem.mRegNumber;
  var activityTypeId = uploadItem.mObjActivityId;
  try {
    store.dispatch({
      type: "ADD_UPLOADING_DATA",
      payload: {
        uri: uri,
        imageName: DocumentImageName,
        activityId: activityId,
      },
    });
  } catch (error) {
    UELogEvent("UploadFile, error", {
      initStatusRealm: "" + JSON.stringify(error),
    });
  }
};

const updateStatusRealm = async (newStatus, uploadItem) => {
  try {
    var uri = uploadItem.uri;
    var DocumentImageName = uploadItem.imageName;
    var activityId = uploadItem.mObjActivityId;
    var quoteNumber = uploadItem.mObjActivityId;
    var regNumber = uploadItem.mRegNumber;
    var activityTypeId = uploadItem.mObjActivityId;
    const PIMediaStatus = await Database.get("PIMediaStatus");

    // getting current activity obj
    var uploadObj = PIMediaStatus.find((item) => {
      return item.activityId === activityId;
    });

    // other activity
    var otherActivity = PIMediaStatus.filter((item) => {
      return item.activityId !== activityId;
    });

    if (uploadObj != null) {
      var newObj;
      // updating values of Activity
      newObj = {
        ...uploadObj,
      };
      // updating value of image
      var updatedImageInfo = newObj.mediaUpload.map((item) => {
        if (item.imageName == DocumentImageName && item.status !== "uploaded") {
          const newItem = {
            ...item,
            imageName: DocumentImageName,
            status: newStatus,
            uri: uri,
          };
          console.log("*** UploadFile updateStatusRealm newItem: ", newItem);

          return newItem;
        }
        return item;
      });

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

      // adding new updated item
      console.log(
        "allActivityObj : ",
        JSON.stringify(allActivityObj, null, "  ")
      );

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

export default UploadFile;
