import { ReactElement, RefObject, createRef, useEffect, useState } from "react";
import { DataStructure, DeleteRequestBody, InsertRequestBody, sanitizeString, SchemaOptions, SelectRequestBody, TableConditionGroups, TableConditions, TableJoin, UpdateRequestBody } from "./DataStructure"
import axios from "axios";
import { Button } from "./Button";
import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/24/outline";
import { DataFieldset } from "./DataFieldset";
import { DataField } from "./DataField";
import { Notification, NotificationProps } from "@/src/components/base/Notification";
import CryptoJS from 'crypto-js';
import { DataDetail } from "./DataDetail";
import { assetEndpointURL } from "@/src/database/database";
import { format } from "date-fns";

export interface PrefillSetting{
  fieldId: string;
  value: any;
}

interface CustomSetting{
  customReadComponent?: ReactElement;
  renderCustomListComponent?: () => ReactElement;
  renderCustomComponent?: (retrievedValues?: any[], listData?: any[], updateData?: any[]) => ReactElement;
  filterQuery?: TableConditionGroups;
  disableEdit?: boolean;
  hiddenColumnInList?: string[];
  createAction?: string;
  createTitle?: string;
  disableHeaderActions?: boolean;
  disableCreateActions?: boolean;
  listTitle?: string;
  allowUserDelete?: boolean;
}

interface Props{
  endPointURL: string;
  insertEndPointURL: string;
  updateEndPointURL: string;
  deleteEndPointURL: string;
  uploadEndPointURL: string;
  assetEndpointURL: string;
  modelSchema: DataStructure[];
  collectionId: string;
  limit: number;
  page: number;
  viewType: "list" | "update" | "read" | "create";
  customSaveText?: string;
  hideBackButton?: boolean;
  hideHeader?: boolean;
  prefillSettings?: PrefillSetting[];
  customSetting?: CustomSetting;
  dataId?: string;
  userAccess?: "Admin" | "User";
  additionalClassName?: string;
  handlePagination: (type: "page" | "prev" | "next", pagesNumber: number, page?: number) => void;
  handleNavigate: (viewType: "list" | "update" | "read" | "create", dataId?: string, data?: any) => void;
  handleUpdateWorkflow?: (previousData: any, id: string) => void;
  handleCreateWorkflow?: (currentData: any, id: string) => void;
  handleDeleteWorkflow?: (currentData: any, id: string) => void;
}

export const DataTable = (props: Props) => {

  const [listData, setListData] = useState<any[]>();
  const [rowCount, setRowCount] = useState<number>(0);
  const [showNotification, setShowNotification] = useState(false);
  const [notificationProps, setNotificationProps] = useState<NotificationProps>({
    type: "Info",
    title: "",
    description: "",
  });
  const [updateData, setUpdateData] = useState<any>({});
  const [retrievedValues, setRetrievedValues] = useState<any>({});

  useEffect(() => {
    setRetrievedValues({});
    setUpdateData({});
    switch(props.viewType){
      case "list":
        handleData();
        handleRowCount();
        break;

      case "read":
      case "update":
        handleData();
        break;
    }
  }, [props]);

  const handleRowCount = async () => {
    const columns = ["count(*) as rowCount"];
    const formData = new FormData();
    const selectQuery: SelectRequestBody = {
      table: props.collectionId,
      columns: columns,
      where: props.customSetting?.filterQuery ? [props.customSetting.filterQuery] : undefined,
      limit: 1,
    };
    formData.append("query", JSON.stringify(selectQuery));

    try{
      const queryRes = await axios.post(props.endPointURL, formData, {
        headers: {
          'Content-Type': 'multipart/form-data',
        },
      });

      if(queryRes.data && queryRes.data["rows"]){
        setRowCount(queryRes.data['rows'][0].rowCount);
      }
    }catch(err) {
      console.log(err);
    }
  }

  const handleColumns = () => {
    const columns: string[] = [];
    props.modelSchema.map((eachSchema: DataStructure) => {
      // if(props.viewType === "list" && eachSchema.hideFromList) return;
      if(props.viewType === "read" && eachSchema.hideFromView) return;
      if(props.viewType === "list" && props.customSetting && props.customSetting.hiddenColumnInList && props.customSetting.hiddenColumnInList.includes(eachSchema.fieldId)) return;

      if(eachSchema.isConfirmation) return;

      switch(eachSchema.type){
        case "reference":
          columns.push(`${props.collectionId}.${eachSchema.fieldId}`);
          columns.push(`${eachSchema.referenceAlias ?? eachSchema.referenceId}.${eachSchema.referenceDisplay} as ${eachSchema.referenceAlias ?? eachSchema.referenceId}_${eachSchema.referenceDisplay}`)
          break;
        case "multiReference":
        case "multiValue":
        case "password":
          return;
        default:
          columns.push(`${props.collectionId}.${eachSchema.fieldId}`);
      }
    });
    return columns;
  }

  const handleLeftJoins = () => {
    const leftJoins: TableJoin[] = [];
    props.modelSchema.map((eachSchema: DataStructure) => {
      switch(eachSchema.type){
        case "reference":
          const localLeftJoin: TableJoin = {
            foreignTable: eachSchema.referenceId ?? "",
            foreignAlias: (eachSchema.referenceAlias ?? eachSchema.referenceId) ?? "",
            foreignTableId: "id",
            foreignId: eachSchema.fieldId,
            parentAlias: eachSchema.referenceParentAlias ?? (leftJoins.length > 0 ? props.collectionId : undefined),
            type: "LEFT"
          }
          leftJoins.push(localLeftJoin);
        default:
          return;
      }
    });

    return leftJoins;
  }

  const handleWhereConditions = () => {
    const conditionGroups: TableConditionGroups[] = [];
    if(props.dataId){
      if(props.viewType === "read" || props.viewType === "update"){
        const idCondition: TableConditions = {
          queryColumn: `${props.collectionId}.id`,
          value: props.dataId,
          operator: "="
        }

        const logicalOperator: any[] = [];
        if(props.customSetting && props.customSetting.filterQuery){
          logicalOperator.push("AND");
        }

        conditionGroups.push({
          condition: [idCondition],
          logicalOperator: logicalOperator.length > 0 ? logicalOperator : undefined
        })
      }
    }
    if(props.customSetting && props.customSetting.filterQuery){
      conditionGroups.push(props.customSetting.filterQuery)
    }
    return conditionGroups;
  }

  const hanldeMultiReferenceData = async (id: string) => {
    const multiReferenceSchema = props.modelSchema.filter((eachSchema) => eachSchema.type === "multiReference");

    const multiReferenceValue: any = {};
    
    await Promise.all(multiReferenceSchema.map(async (eachSchema) => {

      const formData = new FormData();
      const columns: string[] = [];
  
      if(eachSchema.multiReferenceEntryJoinId){
        columns.push(`${eachSchema.multiReferenceEntryId}.${eachSchema.multiReferenceEntryJoinId} as ${eachSchema.multiReferenceEntryId}_${eachSchema.multiReferenceEntryJoinId}`)
      }
      
      if(eachSchema.referenceDisplay){
        columns.push(`${eachSchema.referenceAlias ?? eachSchema.referenceId}.${eachSchema.referenceDisplay} as ${eachSchema.referenceAlias ?? eachSchema.referenceId}_${eachSchema.referenceDisplay}`);
      }
  
      const leftJoins: TableJoin[] = [];
      leftJoins.push({
        foreignTable: eachSchema.referenceId ?? "",
        foreignAlias: (eachSchema.referenceAlias ?? eachSchema.referenceId) ?? "",
        foreignTableId: "id",
        foreignId: eachSchema.multiReferenceEntryJoinId ?? "",
        type: "LEFT"
      });
  
      const conditionGroups: TableConditionGroups[] = [];
      const idCondition: TableConditions = {
        queryColumn: eachSchema.multiReferenceLocalForeignId ?? "",
        value: id,
        operator: "="
      };

      conditionGroups.push({
        condition: [idCondition]
      });
  
      const selectQuery: SelectRequestBody = {
        table: eachSchema.multiReferenceEntryId ?? "",
        columns: columns,
        tableJoin: leftJoins,
        where: conditionGroups
      }

      formData.append('query', JSON.stringify(selectQuery));

      try{
        const queryRes = await axios.post(props.endPointURL, formData, {
          headers: {
            'Content-Type': 'multipart/form-data',
          },
        });

        if(queryRes.data && queryRes.data['rows'] && queryRes.data['rows'].length > 0){
          const optionArray: SchemaOptions[] = [];
          queryRes.data['rows'].map((eachData: any) => {
            const localOption: SchemaOptions = {
              value: eachData[`${eachSchema.multiReferenceEntryId}_${eachSchema.multiReferenceEntryJoinId}`],
              label: eachData[`${eachSchema.referenceAlias ?? eachSchema.referenceId}_${eachSchema.referenceDisplay}`]
            }
            optionArray.push(localOption);
          });
          multiReferenceValue[eachSchema.fieldId] = optionArray;
        }
        
      }catch(err) {}

    }));

    return multiReferenceValue;
  }

  const hanldeMultiValueData = async (id: string) => {
    const multiValueSchema = props.modelSchema.filter((eachSchema) => eachSchema.type === "multiValue");

    const multiValueValue: any = {};
    
    await Promise.all(multiValueSchema.map(async (eachSchema) => {

      const formData = new FormData();
      const columns: string[] = [];
  
      if(eachSchema.multiReferenceEntryJoinId){
        columns.push(`${eachSchema.multiReferenceEntryId}.${eachSchema.multiReferenceEntryJoinId} as ${eachSchema.multiReferenceEntryId}_${eachSchema.multiReferenceEntryJoinId}`)
      }
      
      if(eachSchema.referenceDisplay){
        columns.push(`${eachSchema.referenceAlias ?? eachSchema.referenceId}.${eachSchema.referenceDisplay} as ${eachSchema.referenceAlias ?? eachSchema.referenceId}}_${eachSchema.referenceDisplay}`);
      }

      if(eachSchema.multiReferenceEntryDisplay){
        columns.push(`${eachSchema.multiReferenceEntryId}.${eachSchema.multiReferenceEntryDisplay} as ${eachSchema.multiReferenceEntryId}_${eachSchema.multiReferenceEntryDisplay}`)
      }
  
      const leftJoins: TableJoin[] = [];
      leftJoins.push({
        foreignTable: eachSchema.referenceId ?? "",
        foreignAlias: (eachSchema.referenceAlias ?? eachSchema.referenceId) ?? "",
        foreignTableId: "id",
        foreignId: eachSchema.multiReferenceEntryJoinId ?? "",
        type: "LEFT"
      });
  
      const conditionGroups: TableConditionGroups[] = [];
      const idCondition: TableConditions = {
        queryColumn: eachSchema.multiReferenceLocalForeignId ?? "",
        value: id,
        operator: "="
      };

      conditionGroups.push({
        condition: [idCondition]
      });
  
      const selectQuery: SelectRequestBody = {
        table: eachSchema.multiReferenceEntryId ?? "",
        columns: columns,
        tableJoin: leftJoins,
        where: conditionGroups
      }

      formData.append('query', JSON.stringify(selectQuery));

      try{
        const queryRes = await axios.post(props.endPointURL, formData, {
          headers: {
            'Content-Type': 'multipart/form-data',
          },
        });

        if(queryRes.data && queryRes.data['rows'] && queryRes.data['rows'].length > 0){
          const valueArray: any = {};
          queryRes.data['rows'].map((eachData: any) => {
            const localOption: SchemaOptions = {
              value: eachData[`${eachSchema.multiReferenceEntryId}_${eachSchema.multiReferenceEntryDisplay}`],
              label: eachData[`${eachSchema.referenceAlias ?? eachSchema.referenceId}_${eachSchema.referenceDisplay}`]
            }
            valueArray[eachData[`${eachSchema.multiReferenceEntryId}_${eachSchema.multiReferenceEntryJoinId}`]] = localOption
          });
          multiValueValue[eachSchema.fieldId] = valueArray;
        }
        
      }catch(err) {}

    }));

    return multiValueValue;
  }

  const handleData = async () => {
    const columns = handleColumns();
    const leftJoins = handleLeftJoins();
    const whereConditions = handleWhereConditions();
    const formData = new FormData();
    const selectQuery: SelectRequestBody = {
      table: props.collectionId,
      columns: columns.length > 0 ? columns : undefined,
      tableJoin: leftJoins.length > 0 ? leftJoins : undefined,
      limit: props.limit,
      page: props.viewType === "list" ? props.page : 1,
      where: whereConditions.length > 0 ? whereConditions : undefined
    };
    formData.append("query", JSON.stringify(selectQuery));

    try{
      const queryRes = await axios.post(props.endPointURL, formData, {
        headers: {
          'Content-Type': 'multipart/form-data',
        },
      });

      if(queryRes.data && queryRes.data["rows"]){

        if(props.viewType === "list"){
          setListData(queryRes.data['rows']);
        }else if(queryRes.data['rows'].length > 0){

          const rawData = {...queryRes.data['rows'][0]}

          if(props.dataId){
            const mutliReferenceValue = await hanldeMultiReferenceData(props.dataId);
            const multiValueValue = await hanldeMultiValueData(props.dataId);
            Object.assign(rawData, mutliReferenceValue);
            Object.assign(rawData, multiValueValue);
            
            props.modelSchema.filter((eachSchema: DataStructure) => {
              return eachSchema.type === "date";
            }).map((eachSchema: DataStructure) => {
              const date = new Date(rawData[eachSchema.fieldId]);
              rawData[eachSchema.fieldId] = format(date, 'yyyy-MM-dd');
            });
            
            props.modelSchema.filter((eachSchema: DataStructure) => {
              return eachSchema.type === "datetime-local";
            }).map((eachSchema: DataStructure) => {
              const date = new Date(rawData[eachSchema.fieldId]);
              rawData[eachSchema.fieldId] = format(date, 'yyyy-MM-dd HH:mm:ss');
            });
          }
          setRetrievedValues(rawData);
          setUpdateData(rawData);
        }
      }
    }catch(err) {
      console.log(err);
    }
  }

  

  const handleCheckUnique = async (fieldId: string, value: string, currentId?: string) => {
    const columns = ["count(*) as rowCount"];
    const formData = new FormData();
    if(currentId){
      const selectQuery: SelectRequestBody = {
        table: props.collectionId,
        columns: columns,
        where: [
          {
            condition: [
              {
                queryColumn: fieldId,
                value: value,
                operator: "=",
                logicalOperator: "AND"
              },
              {
                queryColumn: "id",
                value: currentId,
                operator: "!="
              }
            ],
          }
        ],
        limit: 1,
      };
      formData.append("query", JSON.stringify(selectQuery));
    }else{
      const selectQuery: SelectRequestBody = {
        table: props.collectionId,
        columns: columns,
        where: [
          {
            condition: [
              {
                queryColumn: fieldId,
                value: value,
                operator: "="
              }
            ]
          }
        ],
        limit: 1,
      };
      formData.append("query", JSON.stringify(selectQuery));
    }

    try{
      const queryRes = await axios.post(props.endPointURL, formData, {
        headers: {
          'Content-Type': 'multipart/form-data',
        },
      });

      console.log("Unique");
      console.log(queryRes.data);
      console.log(value);

      if(queryRes.data && queryRes.data["rows"]){
        if(queryRes.data['rows'][0].rowCount > 0){
          return false;
        }else{
          return true;
        }
      }
    }catch(err) {
      console.log(err);
    }
    return true;
  }

  const handleValidateData = async () => {
    const invalidFields: any = {};
    await Promise.all(props.modelSchema.map(async (eachSchema: DataStructure) => {

      // Check confirmation
      if(eachSchema.isConfirmation
        && (
          !updateData[eachSchema.fieldId] ||
          !updateData[eachSchema.confirmationField ?? ""] ||
          updateData[eachSchema.fieldId] !== updateData[eachSchema.confirmationField ?? ""]
        )
      ){

        const confirmationField = props.modelSchema.filter((eachConfirmationSchema) => eachConfirmationSchema.fieldId === eachSchema.confirmationField);

        if(confirmationField.length > 0 && eachSchema.type === "password" && !updateData[eachSchema.confirmationField ?? ""]){
          return;
        }
        else if(confirmationField.length > 0){
          if(!invalidFields['unmatched'])
            invalidFields['unmatched'] = [];
          invalidFields['unmatched'].push(`${confirmationField[0].displayName} and ${eachSchema.displayName}`);
        }
      }

      // Check Required
      if(eachSchema.required && updateData[eachSchema.fieldId] == undefined && (props.userAccess === "Admin" || !eachSchema.hideFromUser)){

        if(props.viewType === "update" && eachSchema.type === "password"){
          return;
        }else{
          if(!invalidFields['required'])
            invalidFields['required'] = [];
          invalidFields['required'].push(eachSchema.displayName);
        }
      }

      // Check unique
      if(eachSchema.unique && updateData[eachSchema.fieldId]){
        let checkUnique: boolean;
        if(props.viewType === "update"){
          checkUnique = await handleCheckUnique(eachSchema.fieldId, updateData[eachSchema.fieldId], updateData['id']);
        }else{
          checkUnique = await handleCheckUnique(eachSchema.fieldId, updateData[eachSchema.fieldId]);
        }

        if(!checkUnique){
          if(!invalidFields['unique'])
            invalidFields['unique'] = [];

          invalidFields['unique'].push(eachSchema.displayName);
        }
      }
    }));

    return invalidFields;
  }

  const handleFileUpload = async (file: any) => {
    const formData = new FormData();
    formData.append('file', file);

    try{
      const queryRes = await axios.post(props.uploadEndPointURL, formData, {
        headers: {
          'Content-Type': 'multipart/form-data',
        },
      });

      console.log(queryRes.data);
      if(queryRes.data && queryRes.data['path']){
        return queryRes.data['path']
      }else{
        return "";
      }
    }catch(err){
      console.log(err);
    }
  }

  const handleUpdateSubmit = async () => {
    const formData = new FormData();
    const columns: string[] = [];
    const valueArray: any[] = [];

    const allFields = props.modelSchema.map((eachSchema) => eachSchema.fieldId);

    const optionsFields = props.modelSchema.filter((eachSchema: DataStructure) => {
      return eachSchema.options && eachSchema.options.length > 0;
    }).map((eachSchema: DataStructure) => eachSchema.fieldId);

    const multiValueField = props.modelSchema.filter((eachSchema: DataStructure) => {
      return eachSchema.type === "multiValue"
    }).map((eachSchema: DataStructure) => eachSchema.fieldId);

    const multiReferenceField = props.modelSchema.filter((eachSchema: DataStructure) => {
      return eachSchema.type === "multiReference"
    }).map((eachSchema: DataStructure) => eachSchema.fieldId);

    const confirmationField = props.modelSchema.filter((eachSchema: DataStructure) => {
      return eachSchema.isConfirmation
    }).map((eachStructure: DataStructure) => eachStructure.fieldId);

    const passwordField = props.modelSchema.filter((eachSchema: DataStructure) => {
      return eachSchema.type === "password";
    }).map((eachSchema: DataStructure) => eachSchema.fieldId);

    const fileField = props.modelSchema.filter((eachSchema: DataStructure) => {
      return eachSchema.type === "file";
    }).map((eachSchema: DataStructure) => eachSchema.fieldId);

    const dateField = props.modelSchema.filter((eachSchema: DataStructure) => {
      return eachSchema.type === "date";
    }).map((eachSchema: DataStructure) => eachSchema.fieldId);

    const dateTimeField = props.modelSchema.filter((eachSchema: DataStructure) => {
      return eachSchema.type === "datetime-local";
    }).map((eachSchema: DataStructure) => eachSchema.fieldId);

    const fieldValidation = await handleValidateData();

    if(Object.keys(fieldValidation).length !== 0){
      const firstField = Object.keys(fieldValidation);
      switch(firstField[0]){
        case "required":
          setNotificationProps({
            type: "Error",
            title: "Invalid Data!",
            description: `(${fieldValidation[firstField[0]].join(", ")}) cannot be empty.`
          });
          break;

        case "unmatched":
          setNotificationProps({
            type: "Error",
            title: "Invalid Data!",
            description: `(${fieldValidation[firstField[0]].join(", ")}) is/are not matched.`
          });
          break;

        case "unique":
          setNotificationProps({
            type: "Error",
            title: "Invalid Data!",
            description: `(${fieldValidation[firstField[0]].join(", ")}) is/are not unique.`
          });
          break;
      }
      
      setShowNotification(true);
      return;
    }

    await Promise.all(Object.entries(updateData).map(async ([key, value]: any[]) => {
      if (fileField.includes(key) && typeof value === "object") {
        columns.push(key);
        const filePath = await handleFileUpload(value);

        valueArray.push(filePath);
      }
    }));

    Object.entries(updateData).map(([key, value]: any[]) => {
      if(multiValueField.includes(key) || multiReferenceField.includes(key) || confirmationField.includes(key)|| fileField.includes(key)){
        return;
      }else if(props.viewType === "update" && passwordField.includes(key) && !value) {
        return;
      }else if(props.viewType === "update" && fileField.includes(key)) {
        return;
      } else if(allFields.includes(key) && value){
        columns.push(key);

        let formattedValue = sanitizeString((value.value ?? value) as string);
        if(passwordField.includes(key)){
          formattedValue = CryptoJS.MD5(value).toString();
        }
        if(optionsFields.includes(key) && typeof value === "object" && Array.isArray(value)){
          formattedValue = value[0].value;
        }

        if(dateField.includes(key)){
          const date = new Date(value);

          formattedValue = format(date, 'yyyy-MM-dd');
        }

        if(dateTimeField.includes(key)){
          const date = new Date(value);

          formattedValue = format(date, 'yyyy-MM-dd HH:mm:ss');
        }
        valueArray.push(formattedValue);
      }
    });

    const hiddenField = (props.userAccess !== "Admin") ? props.modelSchema.filter((eachSchema: DataStructure) => {
      return eachSchema.hideFromUser;
    }) : [];

    hiddenField.map((eachSchema: DataStructure) => {
      if(eachSchema.defaultValue){
        columns.push(eachSchema.fieldId);
        valueArray.push(eachSchema.defaultValue());
      }
    })

    if(props.viewType === "create"){
      const insertQuery: InsertRequestBody = {
        table: props.collectionId,
        columns,
        values: [valueArray]
      }
      formData.append("query", JSON.stringify(insertQuery));
    }else{
      const updateQuery: UpdateRequestBody = {
        table: props.collectionId,
        columns,
        values: valueArray,
        where: [
          {
            condition: [
              {
                queryColumn: "id",
                value: props.dataId ?? "",
                operator: "="
              }
            ]
          }
        ]
      }
      formData.append("query", JSON.stringify(updateQuery));
    }
    try{
      const queryRes = await axios.post(props.viewType === "create" ? props.insertEndPointURL : props.updateEndPointURL, formData, {
        headers: {
          'Content-Type': 'multipart/form-data',
        },
      });

      if(queryRes.data && !queryRes.data["error"]){
        let valueId = queryRes.data.result['insertId'];
        if(props.viewType === "update" && props.dataId){
          valueId = props.dataId;
        }

        if(multiValueField.length > 0){
          await Promise.all(props.modelSchema.map(async (eachStructure: DataStructure) => {
            if(eachStructure.type === "multiValue" && updateData[eachStructure.fieldId]){
              if(props.viewType === "update" && props.dataId){
                const formData = new FormData();
                const deleteQuery: DeleteRequestBody = {
                  table: eachStructure.multiReferenceEntryId ?? "",
                  where: [
                    {
                      condition: [
                        {
                          queryColumn: eachStructure.multiReferenceLocalForeignId ?? "",
                          value: props.dataId ?? "",
                          operator: "="
                        }
                      ]
                    }
                  ]
                }
                formData.append("query", JSON.stringify(deleteQuery));
                try{
                  await axios.post(props.deleteEndPointURL, formData, {
                    headers: {
                      'Content-Type': 'multipart/form-data',
                    },
                  });
                }catch(err){}
              }
              const formData = new FormData();
              const entrytable = eachStructure.multiReferenceEntryId;
              const referenceColumn = eachStructure.multiReferenceEntryJoinId;
              const foreignColumn = eachStructure.multiReferenceLocalForeignId;
              const valueColumn = eachStructure.multiReferenceEntryDisplay;
              const localColumns: any[] = [referenceColumn, foreignColumn, valueColumn];
              const values: any[] = [];

              Object.entries(updateData[eachStructure.fieldId]).map(([key, value]: any[]) => {
                const localValue = [key, valueId, value?.value ?? value];
                values.push(localValue);
              });
              

              const insertQuery: InsertRequestBody = {
                table: entrytable ?? "",
                columns: localColumns,
                values: values
              }
              formData.append("query", JSON.stringify(insertQuery));
              
              await axios.post(props.insertEndPointURL, formData, {
                headers: {
                  'Content-Type': 'multipart/form-data',
                },
              });
            }
          }));
        }

        if(multiReferenceField.length > 0){
          await Promise.all(props.modelSchema.map(async (eachStructure: DataStructure) => {
            if(eachStructure.type === "multiReference" && updateData[eachStructure.fieldId]){
              if(props.viewType === "update" && props.dataId){
                const formData = new FormData();
                const deleteQuery: DeleteRequestBody = {
                  table: eachStructure.multiReferenceEntryId ?? "",
                  where: [
                    {
                      condition: [
                        {
                          queryColumn: eachStructure.multiReferenceLocalForeignId ?? "",
                          value: props.dataId ?? "",
                          operator: "="
                        }
                      ]
                    }
                  ]
                }
                formData.append("query", JSON.stringify(deleteQuery));
                try{
                  await axios.post(props.deleteEndPointURL, formData, {
                    headers: {
                      'Content-Type': 'multipart/form-data',
                    },
                  });
                }catch(err){}
              }
              const formData = new FormData();
              const entrytable = eachStructure.multiReferenceEntryId;
              const referenceColumn = eachStructure.multiReferenceEntryJoinId;
              const foreignColumn = eachStructure.multiReferenceLocalForeignId;
              const localColumns: any[] = [referenceColumn, foreignColumn];
              const values: any[] = [];

              updateData[eachStructure.fieldId].map((eachData) => {
                const value = [eachData.value, valueId];
                values.push(value);
              });
              

              const insertQuery: InsertRequestBody = {
                table: entrytable ?? "",
                columns: localColumns,
                values: values
              }
              formData.append("query", JSON.stringify(insertQuery));
              
              await axios.post(props.insertEndPointURL, formData, {
                headers: {
                  'Content-Type': 'multipart/form-data',
                },
              });
            }
          }));
        }

        if(props.handleUpdateWorkflow){
          props.handleUpdateWorkflow(retrievedValues, valueId);
        }

        if(props.handleCreateWorkflow){
          props.handleCreateWorkflow(updateData, valueId);
        }

        setNotificationProps({
          type: "Success",
          title: `${props.viewType === "create" ? "Created" : "Updated"} Successfully!`,
          description: `Data had been ${props.viewType === "create" ? "created" : "updated"} successfully.`
        });
        props.handleNavigate("list", props.viewType === "create" ? queryRes.data.result['insertId'] : props.dataId, updateData);
      }else{
        
        setNotificationProps({
          type: "Error",
          title: `${props.viewType === "create" ? "Create" : "Update"} Failed!`,
          description: `Data had not been ${props.viewType === "create" ? "created" : "updated"}.`
        });
        console.log(queryRes.data['error']);
        props.handleNavigate("list");
      }
      setShowNotification(true);
    }catch(err) {
      console.log(err);
    }
  }

  const handleDelete = async () => {
    const formData = new FormData();
    const deleteQuery: DeleteRequestBody = {
      table: props.collectionId,
      where: [
        {
          condition: [
            {
              queryColumn: "id",
              value: props.dataId ?? "",
              operator: "="
            }
          ]
        }
      ]
    }
    formData.append("query", JSON.stringify(deleteQuery));
    try{
      const queryRes = await axios.post(props.deleteEndPointURL, formData, {
        headers: {
          'Content-Type': 'multipart/form-data',
        },
      });
      if(queryRes.data && !queryRes.data['error']){
        
        setNotificationProps({
          type: "Success",
          title: `Deleted Successfully!`,
          description: `Data had been deleted successfully.`
        });
        props.handleNavigate("list", props.viewType === "create" ? queryRes.data.result['insertId'] : props.dataId);

        if(props.handleDeleteWorkflow){
          props.handleDeleteWorkflow(retrievedValues, props.dataId ?? "");
        }

      }else{

        setNotificationProps({
          type: "Error",
          title: `Delete Failed!`,
          description: `Data had not been deleted.`
        });
        console.log(queryRes.data['error']);
        props.handleNavigate("list");
      }
      setShowNotification(true);
    }catch(err) {}
  }

  const renderListTableHeader = () => {

    return (
      <thead>
        <tr className="border-b">
          {props.modelSchema.map((eachSchema: DataStructure, index: number) => {
            if(eachSchema.hideFromList) return;
            
            if(props.viewType === "list" && props.customSetting && props.customSetting.hiddenColumnInList && props.customSetting.hiddenColumnInList.includes(eachSchema.fieldId)) return;

            switch(eachSchema.type){
              case "multiReference":
              case "multiValue":
                return;
              default:
                return(<th key={index} className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">{eachSchema.displayName}</th>);
            }
          })}
        </tr>
      </thead>
    )
  }

  const renderListTableBody = () => {
    let columnCount = 0;
    props.modelSchema.map((eachSchema) => {
      if(eachSchema.hideFromList) return;
            
      if(props.viewType === "list" && props.customSetting && props.customSetting.hiddenColumnInList && props.customSetting.hiddenColumnInList.includes(eachSchema.fieldId)) return;

      switch(eachSchema.type){
        case "multiReference":
        case "multiValue":
          return;
        default:
          columnCount = columnCount + 1;
      }
    });
    return (
      <tbody>
          {listData && listData.map((eachData: any) => {
            
            return (
              <tr className="border-b cursor-pointer hover:bg-gray-100" onClick={() => {
                props.handleNavigate("read", eachData['id'], eachData);
              }}>
                {props.modelSchema.map((eachSchema: DataStructure, index: number) => {
                  if(eachSchema.hideFromList) return;
            
                  if(props.viewType === "list" && props.customSetting && props.customSetting.hiddenColumnInList && props.customSetting.hiddenColumnInList.includes(eachSchema.fieldId)) return;
                  
                  let fieldID = eachSchema.fieldId;
                  if(eachSchema.type === "reference")
                    fieldID = `${eachSchema.referenceAlias ?? eachSchema.referenceId}_${eachSchema.referenceDisplay}` ?? "";

                  let value = eachData[fieldID];
                  if(eachSchema.options){
                    const filteredOption = eachSchema.options.filter((eachOption) => eachOption.value == value);
                    if(filteredOption && filteredOption[0]){
                      value = filteredOption[0].label;
                    }
                  }
                  

                  if(((eachSchema.type === "reference" && eachSchema.referenceDisplayIsDate) || eachSchema.type === "date") && value){
                    const date = new Date(value);
          
                    value = format(date, 'yyyy-MM-dd');
                  }
          
                  if(((eachSchema.type === "reference" && eachSchema.referenceDisplayIsTime) || eachSchema.type === "datetime-local") && value){
                    const date = new Date(value);
          
                    value = format(date, 'yyyy-MM-dd HH:mm:ss');
                  }

                  switch(eachSchema.type){
                    case "multiReference":
                    case "multiValue":
                      return;
                    default:
                      return(<td key={index} className="whitespace-wrap px-3 py-3.5 text-left text-sm text-gray-800">{value}</td>);
                  }
                })}
              </tr>
            )
          })}
          {(!listData || listData.length < 1) && (
            <tr>
              <td colSpan={columnCount} className="px-3 py-3.5 text-center">
                No records found!
              </td>
            </tr>
          )}
      </tbody>
    )
  }

  const renderRangeAroundNumber = (number: number, max: number) => {
    const result: number[] = [];

    // Define the range based on the central number
    let start = Math.max(1, number - 2);
    let end = Math.min(max, number + 2);

    // Adjust the range for specific cases
    if (number <= 2) {
        start = 1;
        end = Math.min(max, start + 4); // Ensure end doesn't exceed max
    } else if (number >= (max-2)) {
        start = Math.max(1, number - 4); // Ensure start doesn't go below 1
        end = max;
    }

    // Adjust start and end to ensure exactly 5 elements
    if (end - start + 1 > 5) {
        if (start === 1) {
            end = start + 4;
        } else if (end === max) {
            start = end - 4;
        } else {
            start = number - 2;
            end = number + 2;
        }
    }

    // Generate numbers from start to end (ensure exactly 5 elements)
    while (result.length < 5) {
      if(start > max) break;
      result.push(start);
      start++;
    }

    return result;
  }

  const renderPagination = () => {
    const pageNumbers = Math.ceil(rowCount / props.limit);
    return (
      <div className="flex flex-row flex-wrap justify-between items-center pt-4 select-none">
        <div className="text-sm">
          Showing {rowCount} results
        </div>
        <div className="flex flex-row flex-wrap justify-end items-stretch">
          <div className="p-2 border rounded-l flex flex-row flex-wrap items-center text-primary-400 hover:bg-primary-400 hover:text-white cursor-pointer" onClick={() => {
            props.handlePagination("prev", pageNumbers);
          }}>
            <ChevronLeftIcon className="w-5 h-5 " />
          </div>
          {renderRangeAroundNumber(props.page, pageNumbers).map((value) => {
            return (
              <div className={`p-2 border flex flex-row flex-wrap justify-center items-center min-w-10 hover:bg-primary-400 hover:text-white ${value === props.page ? 'cursor-not-allowed bg-primary-400 text-white' : 'cursor-pointer'}`} onClick={() => {
                if(value !== props.page)
                  props.handlePagination("page", pageNumbers, value)
              }}>
                {value}
              </div>
            )
          })}
          <div className="p-2 border rounded-r flex flex-row flex-wrap items-center text-primary-400 hover:bg-primary-400 hover:text-white cursor-pointer" onClick={() => {
            props.handlePagination("next", pageNumbers);
          }}>
            <ChevronRightIcon className="w-5 h-5" />
          </div>
        </div>
      </div>
    )
  }

  const renderViewHeader = (title: string, actionComponent?: ReactElement) => {
    return (
      <div className="flex flex-row flex-wrap justify-between items-center pb-2 border-b">
        <h3 className="text-xl font-semibold text-primary-400">{title}</h3>
        <div className="flex flex-row flex-wrap sm:justify-end justify-start sm:mt-0 mt-3 items-center gap-2 sm:w-auto w-full">
          {actionComponent}
        </div>
      </div>
    )
  }

  const renderListView = () => {
    return(
      <>
        {!props.hideHeader && renderViewHeader(
          props.customSetting?.listTitle ?? 'Overview',
          (!props.customSetting?.disableHeaderActions && !props.customSetting?.disableCreateActions) ? <Button text={props.customSetting?.createAction ?? "Create"} size="md" additionalClassName="block rounded" onClick={() => {
            props.handleNavigate("create");
          }} /> : <></>
        )}
        <div className="max-w-full overflow-x-scroll">
        <table className="min-w-full max-w-full table-fixed">
          {renderListTableHeader()}
          {renderListTableBody()}
        </table>
        </div>
        {renderPagination()}
      </>
    )
  }

  const renderUpdateActions = () => {
    return (
      <>
        {!props.hideBackButton ? (
          <Button text="Back" bgColor="bg-white" color="text-primary-400" size="md" additionalClassName="block rounded border border-primary-400" onClick={() => {
            props.handleNavigate("list");
          }} />
        ): <></>}
        <Button text={props.customSaveText ?? "Save"} size="md" additionalClassName="block rounded" onClick={handleUpdateSubmit} />
      </>
    )
  }

  const renderUpdateView = () => {
    return (
      <>
        {!props.hideHeader && renderViewHeader(
          props.viewType === "create" ? `${props.customSetting?.createTitle ?? 'Create new row'}` : 'Edit row',
          props.customSetting?.disableHeaderActions ? <></> : renderUpdateActions()
        )}
        <DataFieldset columns={2} className="mt-4">
          <>
            {props.modelSchema.map((eachStructure: DataStructure, index: number) => {
              if(eachStructure.hideFromCRUD || (props.userAccess !== "Admin" &&eachStructure.hideFromUser)){
                return <></>
              }

              let prefillSettings: any;
              if(props.prefillSettings){
                props.prefillSettings.map((eachPrefill) => {
                  if(eachPrefill.fieldId == eachStructure.fieldId){
                    prefillSettings = eachPrefill.value;
                  }
                })
              }
              return (
                <DataField
                  dataStructure={eachStructure}
                  key={index}
                  onChange={(inputValue) => {
                    const localValue = {...updateData}
                    localValue[eachStructure.fieldId] = inputValue;
                    setUpdateData(localValue);
                  }}
                  value={eachStructure.fieldId in retrievedValues ? retrievedValues[eachStructure.fieldId] : prefillSettings}
                  hidden={prefillSettings !== undefined}
                  assetEndpointURL={props.assetEndpointURL}
                />
              )
            })}
          </>
        </DataFieldset>
        <div className="flex flex-wrap justify-start items-center flex-row gap-2 mt-4">
        {renderUpdateActions()}
        </div>
      </>
    )
  }

  const renderDetailAction = () => {
    return (
      <>
        {props.customSetting?.customReadComponent}
        {!props.hideBackButton ? (
          <Button text="Back" bgColor="bg-white" color="text-primary-400" size="md" additionalClassName="block rounded border border-primary-400" onClick={() => {
            props.handleNavigate("list");
          }} />
        ): <></>}
        {props.customSetting && !props.customSetting.disableEdit && <Button text={"Edit"} size="md" additionalClassName="block rounded" onClick={() => {props.handleNavigate("update")}} />}
        {(props.userAccess !== "User" || props.customSetting?.allowUserDelete) && <Button text={"Delete"} size="md" additionalClassName="block rounded" onClick={() => {handleDelete()}} color="text-white" bgColor="bg-rose-600" />}
      </>
    )
  }

  const renderDetailView = () => {
    return (
      <>
        {!props.hideHeader && renderViewHeader(
          `View #${props.dataId}`,
          renderDetailAction()
        )}
        <DataDetail
          modelSchema={props.modelSchema}
          values={retrievedValues}
          assetEndpointURL={assetEndpointURL}
        />
        {props.customSetting && props.customSetting.renderCustomListComponent && props.customSetting.renderCustomListComponent()}
      </>
    )
  }

  return (
    <>
      {props.customSetting?.renderCustomComponent ? props.customSetting.renderCustomComponent(retrievedValues, listData, updateData) : (
        <div className={props.additionalClassName}>
        {props.viewType === "list" && renderListView()}
        {(props.viewType === "create" || props.viewType === "update") && renderUpdateView()}
        {props.viewType === "read" && renderDetailView()}
        <Notification
          {...notificationProps}
          onClose={() => {
            setShowNotification(false);
          }}
          show={showNotification}
        />
      </div>
      )}
    </>
  )
}