"use client";

import React, { useCallback, useMemo, useState } from "react";
import SearchBox, { Criterion } from "../SearchBox";
import { useTranslation } from "react-i18next";
import SearchResults, { Column } from "../SearchResults";
import EditNote from "@mui/icons-material/EditNote";
import { moneyFormatter, timestampToDateStringV2 } from "@/app/utils/formatUtil"
import { Button, ButtonGroup, Stack, Tab, Tabs, TabsProps, Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, TextField, CardContent, Typography, Divider, Card, ThemeProvider } from "@mui/material";
import FileUploadIcon from '@mui/icons-material/FileUpload';
import AddIcon from '@mui/icons-material/Add';
import { deleteInvoice, importIssuedInovice, importReceivedInovice, updateInvoice } from "@/app/api/invoices/actions";
import { deleteDialog, errorDialogWithContent, successDialog } from "../Swal/CustomAlerts";
import { invoiceList, issuedInvoiceList, issuedInvoiceSearchForm, receivedInvoiceList, receivedInvoiceSearchForm } from "@/app/api/invoices";
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
import { GridCellParams, GridColDef, GridEventListener, GridRenderEditCellParams, GridRowId, GridRowModes, GridRowModesModel, GridValueFormatterParams } from "@mui/x-data-grid";
import { useGridApiRef } from "@mui/x-data-grid";
import StyledDataGrid from "../StyledDataGrid";

import { uniq } from "lodash";
import CreateInvoiceModal from "./CreateInvoiceModal";
import { ProjectResult } from "@/app/api/projects";
import { IMPORT_INVOICE, IMPORT_RECEIPT } from "@/middleware";
import InvoiceSearchLoading from "./InvoiceSearchLoading";
import { NumberFormatValues, NumericFormat } from "react-number-format";
import theme from "@/theme";
import { SMALL_ROW_THEME } from "@/theme/colorConst";

interface CustomMoneyComponentProps {
  params: GridRenderEditCellParams;
}

interface Props {
  invoices: invoiceList[];
  projects: ProjectResult[];
  abilities: string[];
}

type InvoiceListError = {
  [field in keyof invoiceList]?: string;
};

type invoiceListRow = Partial<
  invoiceList & {
    _isNew: boolean;
    _error: InvoiceListError;
  }
>;

interface SubComponents {
  Loading: typeof InvoiceSearchLoading;
}

type SearchQuery = Partial<Omit<issuedInvoiceSearchForm, "id">>;
type SearchParamNames = keyof SearchQuery;

type SearchQuery2 = Partial<Omit<receivedInvoiceSearchForm, "id">>;
type SearchParamNames2 = keyof SearchQuery2;

const InvoiceSearch: React.FC<Props> & SubComponents = ({ invoices, projects, abilities }) => {
  console.log(uniq(invoices.map((invoice) => invoice.teamCodeName)))
  const { t } = useTranslation("Invoice");

  const [filteredIvoices, setFilterInovices] = useState(invoices);
  const apiRef = useGridApiRef();
  const [autoRedirectToFirstPage, setAutoRedirectToFirstPage] = useState(true);

  const searchCriteria: Criterion<SearchParamNames>[] = useMemo(
    () => [
      { label: t("Invoice No"), paramName: "invoiceNo", type: "text" },
      { label: t("Project Code"), paramName: "projectCode", type: "text" },
      {
        label: t("Team"),
        paramName: "team",
        type: "select",
        options: uniq(invoices.map((invoice) => invoice.teamCodeName)),
      },
      { label: t("Issue Date"), label2: t("Issue Date To"), paramName: "invoiceDate", type: "dateRange" },
      { label: t("Settle Date"), label2: t("Settle Date To"), paramName: "dueDate", type: "dateRange" },
      {
        label: t("Settled"),
        paramName: "settled",
        type: "select",
        options: [t("Settled"), t("Unsettled")],
      },
    ],
    [t, invoices],
  );

  const onReset = useCallback(() => {
    // setFilteredIssuedInvoices(issuedInvoice);
    setAutoRedirectToFirstPage(() => true)
    setFilterInovices(invoices)
  }, [invoices]);

  function concatListOfObject(obj: any[]): string {
    return obj.map(obj => `Cannot find "${obj.paymentMilestone}" in ${obj.invoiceNo}`).join(", ")
  }

  function concatListOfObject2(obj: any[]): string {
    return obj.map(obj => `"${obj.projectCode}" does not match with ${obj.invoicesNo}`).join(", ")
  }

  const handleImportClick = useCallback(async (event: any) => {
    try {
      const file = event.target.files[0];

      if (!file) {
        console.log('No file selected');
        return;
      }

      if (file.type !== 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') {
        console.log('Invalid file format. Only XLSX files are allowed.');
        return;
      }

      setLoading(true)
      const formData = new FormData();
      formData.append('multipartFileList', file);

      const response = await importIssuedInovice(formData);
      
      // response: status, message, projectList, emptyRowList, invoiceList

      if (response.status) {
        successDialog(t("Import Success"), t).then(() => {
          setLoading(false)
          window.location.reload();
        });
      } else {
        handleImportError(response);
        setLoading(false)
      }
    } catch (err) {
      console.log(err);
      return false;
    }
  }, []);

  const [modelOpen, setModelOpen] = useState<boolean>(false);

  const handleAddInvoiceClick = useCallback(() => {
    setModelOpen(true)
  },[])

  const handleModalClose = useCallback(() => {
    setModelOpen(false)
  },[])

  const handleImportError = (response: any) => {
    if (response.emptyRowList.length >= 1) {
      showErrorDialog(
        t("Import Fail"),
        t(`Please fill the mandatory field at Row <br> ${response.emptyRowList.join(", ")}`)
      );
    } else if (response.projectList.length >= 1) {
      showErrorDialog(
        t("Import Fail"),
        t(`Please check the corresponding project code <br> ${response.projectList.join(", ")}`)
      );
    } else if (response.invoiceList.length >= 1) {
      showErrorDialog(
        t("Import Fail"),
        t(`Please check the corresponding Invoice No. The invoice is imported. <br>`) + `${response.invoiceList.join(", ")}`
      );
    } else if (response.duplicateItem.length >= 1) {
      showErrorDialog(
        t("Import Fail"),
        t(`Please check the corresponding Invoice No. The below invoice has duplicated number. <br>`)+ `${response.duplicateItem.join(", ")}`
      );
    } else if (response.paymentMilestones.length >= 1) {
      showErrorDialog(
        t("Import Fail"),
        t(`The payment milestone does not match with records. Please check the corresponding Invoice No. <br>`) + `${concatListOfObject(response.paymentMilestones)}`
      );
    }
  };

  const showErrorDialog = (title: string, content: string) => {
    errorDialogWithContent(title, content, t).then(() => {
      window.location.reload();
    });
  };

  const handleRecImportClick = useCallback(async (event:any) => {
    try {

      const file = event.target.files[0];

      if (!file) {
        console.log('No file selected');
        return;
      }

      if (file.type !== 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') {
        console.log('Invalid file format. Only XLSX files are allowed.');
        return;
      }

      const formData = new FormData();
      formData.append('multipartFileList', file);
      setLoading(true)

      const response = await importReceivedInovice(formData)
      console.log(response)

      if (response.status) {
        successDialog(t("Import Success"), t).then(() => {
          window.location.reload()
        })
        setLoading(false)
      }else{
        if (response.emptyRowList.length >= 1){
          errorDialogWithContent(t("Import Fail"), 
          t(`Please fill the mandatory field at Row <br> ${response.emptyRowList.join(", ")}`), t)
          .then(() => {
            window.location.reload()
          })
        }
        else if (response.projectList.length >= 1){
          errorDialogWithContent(t("Import Fail"), 
          t(`Please check the corresponding project code <br> ${response.projectList.join(", ")}`), t)
          .then(() => {
            // window.location.reload()
          })
        }
        else if (response.invoiceList.length >= 1){
          errorDialogWithContent(t("Import Fail"), 
          t(`Please check the corresponding Invoice No. The invoice has not yet issued. <br>`)+ `${response.invoiceList.join(", ")}`, t)
          .then(() => {
            window.location.reload()
          })
        }
        else if (response.duplicateItem.length >= 1){
          errorDialogWithContent(t("Import Fail"), 
          t(`Please check the corresponding Invoice No. The below invoice has duplicated number. <br>`)+ `${response.duplicateItem.join(", ")}`, t)
          .then(() => {
            window.location.reload()
          })
        }else if (response.paymentMilestones.length >= 1){
          errorDialogWithContent(t("Import Fail"), 
          t(`The payment milestone does not match with records. Please check the corresponding Invoice No. <br>`)+ `${concatListOfObject2(response.paymentMilestones)}`, t)
          .then(() => {
            window.location.reload()
          })
        }
        setLoading(false)
      }
    }catch(error){
      console.log(error)
    }
    
  }, []);

  const [selectedRow, setSelectedRow] = useState<invoiceListRow[] | []>([]);
  const [dialogOpen, setDialogOpen] = useState(false);

  const handleButtonClick = (row: invoiceList) => {
    console.log(row)
    setSelectedRow([row]);
    setDialogOpen(true);
    setRowModesModel((model) => ({
      ...model,
      [row.id]: { mode: GridRowModes.Edit, fieldToFocus: "issuedAmount" },
    }));
  };

  const handleCloseDialog = () => {
    setDialogOpen(false);
  };

  const handleDeleteInvoice = useCallback(() => {
    deleteDialog(async() => {
      //console.log(selectedRow[0])
      await deleteInvoice(selectedRow[0].id!!)
      setDialogOpen(false);
      const result = await successDialog("Delete Success", t);
      if (result) {
        window.location.reload()
      }
    }, t)
  }, [selectedRow]);


  const handleSaveDialog = async () => {
    // setDialogOpen(false);
    const response = await updateInvoice(selectedRow[0])
    setDialogOpen(false);

    setAutoRedirectToFirstPage(() => false)
    successDialog(t("Update Success"), t).then(() => {
      // window.location.reload()

      setFilterInovices((prev) => {
        const tempInvoices = [...prev]
        const invoiceId = tempInvoices.findIndex((invoice) => invoice.id === response.id)
        if (invoiceId >= 0) {
          const updatedInvoice = tempInvoices[invoiceId]
          updatedInvoice.invoiceNo = response?.invoiceNo
          updatedInvoice.issuedAmount = response?.issuedAmount
          updatedInvoice.issuedDate = timestampToDateStringV2(response.issuedDate)!!
          updatedInvoice.receiptDate = timestampToDateStringV2(response.receiptDate)!!
          updatedInvoice.receivedAmount = response?.receivedAmount
          tempInvoices[invoiceId] = updatedInvoice
        }
        
        return tempInvoices
      })
    })
    // console.log(selectedRow[0])
    // setSelectedRow([]);
  };

  // Money format : 000,000,000.00
  const CustomMoneyFormat = useCallback((value: number) => {
      if (value) {
        return moneyFormatter.format(value);
      } else {
        return ""
      }
  }, [])

  const CustomMoneyComponent: React.FC<CustomMoneyComponentProps> = ({ params }) => {
    const { id, value, field } = params;
    const ref = React.useRef();

    const handleValueChange = (newValue: NumberFormatValues) => {
      apiRef.current.setEditCellValue({ id, field, value: newValue.value });
    };

    return <NumericFormat
      fullWidth
      prefix="HK$"
      onValueChange={(values) => {
        console.log(values)
        handleValueChange(values)
      }}
      customInput={TextField}
      thousandSeparator
      valueIsNumericString
      decimalScale={2}
      fixedDecimalScale
      value={value}
      inputRef={ref}
      InputProps={{
        sx: {
          '& .MuiInputBase-input': {
            textAlign: 'right',
            mb: 2
          }
        }
      }}
    />;
  }

  const combinedColumns = useMemo<Column<invoiceList>[]>(
    () => [
      { 
        name: "invoiceNo", 
        label: t("Edit"),
        onClick: (row: invoiceList) => (
          handleButtonClick(row)
        ),
        buttonIcon: <EditOutlinedIcon />
      },
      { name: "invoiceNo", label: t("Invoice No") },
      { name: "projectCode", label: t("Project Code") },
      { name: "projectName", label: t("Project Name") },
      { name: "team", label: t("Team") },
      { name: "issuedDate", label: t("Issue Date") },
      { name: "issuedAmount", label: t("Amount (HKD)"), type: 'money', needTranslation: true },
      { name: "receiptDate", label: t("Settle Date") },
      { name: "receivedAmount", label: t("Actual Received Amount (HKD)"), type: 'money', needTranslation: true },
    ],
    [t]
  )

  const editCombinedColumns = useMemo<GridColDef[]>(
    () => [
      { field: "invoiceNo", headerName: t("Invoice No"), editable: true, flex: 0.5 },
      { field: "projectCode", headerName: t("Project Code"), editable: false, flex: 0.3 },
      { field: "projectName", headerName: t("Project Name"), flex: 1 },
      { field: "team", headerName: t("Team"), flex: 0.4 },
      { field: "issuedDate", 
        headerName: t("Issue Date"), 
        editable: true, 
        flex: 0.4,  
        // type: 'date', 
        // valueGetter: (params) => {
        //   // console.log(params.row.issuedDate)
        //   return new Date(params.row.issuedDate)
        // },
      },
      { field: "issuedAmount", 
        headerName: t("Amount (HKD)"), 
        editable: true, 
        flex: 0.5,
        type: 'number',
        renderEditCell: (params: GridRenderEditCellParams) => {
          return <CustomMoneyComponent params={params} />
        },
        valueFormatter: (params: GridValueFormatterParams) => {
          return CustomMoneyFormat(params.value as number)
        }
      },
      { 
        field: "receiptDate", 
        headerName: t("Settle Date"), 
        editable: true,
        flex: 0.4,
        // renderCell: (params) => {
        //   console.log(params)
        //   return (
        //     <LocalizationProvider dateAdapter={AdapterDayjs}>
        //       <DatePicker
        //         value={dayjs(params.value)}
        //       />
        //     </LocalizationProvider>
        //   );
        // }
      },
      { field: "receivedAmount", 
        headerName: t("Actual Received Amount (HKD)"), 
        editable: true, 
        flex: 0.5,
        type: 'number',
        renderEditCell: (params: GridRenderEditCellParams) => {
          return <CustomMoneyComponent params={params} />
        },
        valueFormatter: (params: GridValueFormatterParams) => {
          return CustomMoneyFormat(params.value as number)
        }
      },
    ],
    [t]
  )

  function convertDateFormat(dateString: string) {
    // Split the input date string by '/'
    const parts = dateString.split('/');

    // Extract day, month, and year
    const day = parts[0];
    const month = parts[1];
    const year = parts[2];

    // Return the formatted date string
    return `${year}-${month}-${day}`;
}

  function isDateInRange(dateToCheck: string, startDate: string, endDate: string, dash: boolean): boolean {

    if ((!startDate || startDate === "Invalid Date") && (!endDate || endDate === "Invalid Date")) {
      return true;
    }
    let tempDateToCheckObj
    if(dash){
      tempDateToCheckObj = new Date(dateToCheck)
    }else{
      tempDateToCheckObj = new Date(convertDateFormat(dateToCheck))
    }
    

    const dateToCheckObj = tempDateToCheckObj;
    const startDateObj = new Date(startDate);
    const endDateObj = new Date(endDate);

    return ((!startDate || startDate === "Invalid Date") || dateToCheckObj >= startDateObj) && ((!endDate || endDate === "Invalid Date") || dateToCheckObj <= endDateObj);
  }

  const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({});

  const validateInvoiceEntry = (
    entry: Partial<invoiceList>,
  ): InvoiceListError | undefined => {
    // Test for errors
    const error: InvoiceListError = {};
  
    console.log(entry)
    if (!entry.issuedAmount) {
      error.issuedAmount = "Please input issued amount ";
    } else if (!entry.issuedAmount) {
      error.receivedAmount = "Please input received amount";
    } else if (entry.invoiceNo === "") {
      error.invoiceNo = "Please input invoice number";
    } else if (!entry.issuedDate) {
      error.issuedDate = "Please input issue date";
    } else if (!entry.receiptDate){
      error.receiptDate = "Please input receipt date";
    }

  
    return Object.keys(error).length > 0 ? error : undefined;
  }

  const validateRow = useCallback(
    (id: GridRowId) => {
      const row = apiRef.current.getRowWithUpdatedValues(
        id,
        "",
      )

      const error = validateInvoiceEntry(row);
      console.log(error)
      // Test for warnings

      apiRef.current.updateRows([{ id, _error: error }]);
      return !error;
    },
    [apiRef],
  );

  const handleEditStop = useCallback<GridEventListener<"rowEditStop">>(
    (params, event) => {
      // console.log(params.id)
      if (validateRow(params.id) !== undefined || !validateRow(params.id)) {
        const row = apiRef.current.getRowWithUpdatedValues(
          params.id,
          "",
        )
        console.log(row)
        setSelectedRow(() => [{...row}] as invoiceList[])

        setRowModesModel((model) => ({
          ...model,
          [params.id]: { mode: GridRowModes.View},
        }));

        event.defaultMuiPrevented = true;
      }
      // console.log(row)
    },
    [validateRow],
  );

  const isAddInvoiceRightExist = () => {
    const importRight = [IMPORT_INVOICE, IMPORT_RECEIPT].some((ability) => abilities.includes(ability))
    return importRight
  }

  const [loading, setLoading] = React.useState(false);

  return (
    <>
      {loading && <InvoiceSearch.Loading />}
      
      {
        !loading &&
        isAddInvoiceRightExist() &&
        <Stack
          direction="row"
          justifyContent="right"
          flexWrap="wrap"
          spacing={2}
        >
        <ButtonGroup variant="contained">
          <Button 
            startIcon={<AddIcon />} 
            variant="outlined" 
            component="label"
            onClick={handleAddInvoiceClick}
            >
            {t("Create Invoice")}
          </Button>
          <Button startIcon={<FileUploadIcon />} variant="contained" component="label">
            <input
              id='importExcel'
              type='file'
              accept='.xlsx, .csv'
              hidden
              onChange={(event) => {handleImportClick(event)}}
            />
            {t("Import Invoice Issue Summary")}
          </Button>
          <Button startIcon={<FileUploadIcon />} component="label" variant="contained">
          <input
              id='importExcel'
              type='file'
              accept='.xlsx, .csv'
              hidden
              onChange={(event) => {handleRecImportClick(event)}}
            />
            {t("Import Invoice Amount Receive Summary")}
          </Button>
        </ButtonGroup>
      </Stack>
      }
      
      {
        !loading &&
        // tabIndex == 0 &&
        <SearchBox
          criteria={searchCriteria}
          onSearch={(query) => {
            // console.log(query.team)
            // console.log(invoices[0].team)
            setFilterInovices(
              invoices.filter(
                (s) => (s.invoiceNo.toLowerCase().includes(query.invoiceNo.toLowerCase())) 
                && (s.projectCode.toLowerCase().includes(query.projectCode.toLowerCase()))
                && (query.team === "All" || query.team.includes(s.team))
                && (isDateInRange(s.issuedDate, query.invoiceDate ?? undefined, query.invoiceDateTo ?? undefined, false)) 
                && (isDateInRange(s.receiptDate, query.dueDate ?? undefined, query.dueDateTo ?? undefined, false))
                && (query.settled === "All" || (query.settled === t("Settled")) === s.settled)
              ),
            );
            setAutoRedirectToFirstPage(() => true)
          }}
          onReset={onReset}
        />
      }

      
      {
        !loading &&
        <>
        <Divider sx={{ paddingBlockStart: 2 }} />
        <Card sx={{ display: "block" }}>
          <CardContent>
          <Stack direction="row" justifyContent="space-between">
            <Typography variant="h6">{t('Total Issued Amount (HKD)')}:</Typography>
            <Typography variant="h6">{moneyFormatter.format(filteredIvoices.reduce((acc, current) => (acc + current.issuedAmount), 0))}</Typography>
          </Stack>
          <Stack direction="row" justifyContent="space-between">
            <Typography variant="h6">{t('Total Received Amount (HKD)')}:</Typography>
            <Typography variant="h6">{moneyFormatter.format(filteredIvoices.reduce((acc, current) => (acc + current.receivedAmount), 0))}</Typography>
          </Stack>
          </CardContent>
        </Card>
        <Divider sx={{ paddingBlockEnd: 2 }} />
        </>
      }
      
      
      {
        !loading &&
        // tabIndex == 0 &&
        <ThemeProvider theme={SMALL_ROW_THEME}>
          <SearchResults<invoiceList>
            items={filteredIvoices}
            columns={combinedColumns}
            autoRedirectToFirstPage={autoRedirectToFirstPage}
          />
        </ThemeProvider>
      }
    <Dialog open={dialogOpen} onClose={handleCloseDialog} maxWidth="lg" fullWidth>
      <DialogTitle>{t("Edit Invoice")}</DialogTitle>
      <DialogContent>
        <DialogContentText>
          {t("You can edit the invoice details here.")}
        </DialogContentText>
        <StyledDataGrid
        apiRef={apiRef}
        autoHeight
        sx={{
          "--DataGrid-overlayHeight": "100px",
          ".MuiDataGrid-row .MuiDataGrid-cell.hasError": {
            border: "1px solid",
            borderColor: "error.main",
          },
          ".MuiDataGrid-row .MuiDataGrid-cell.hasWarning": {
            border: "1px solid",
            borderColor: "warning.main",
          },
          '& .MuiDataGrid-columnHeaderTitle': {
            whiteSpace: 'normal',
            textWrap: 'pretty',
            textAlign: 'center',
          },
          '.MuiDataGrid-row:not(.MuiDataGrid-row--dynamicHeight)>.MuiDataGrid-cell': {
            overflow: 'auto',
            whiteSpace: 'nowrap',
            textWrap: 'pretty',
          },
          width: "100%", // Make the DataGrid wider
        }}
        disableColumnMenu
        editMode="row"
        rows={selectedRow}
        rowModesModel={rowModesModel}
        onRowModesModelChange={setRowModesModel}
        onRowEditStop={handleEditStop}
        columns={editCombinedColumns}
        getCellClassName={(params: GridCellParams<invoiceListRow>) => {
          let classname = "";
          if (params.row._error?.[params.field as keyof invoiceList]) {
            classname = "hasError";
          } 
          return classname;
        }}
      />
      </DialogContent>
      <DialogActions>
      <Button onClick={handleDeleteInvoice} color="error">
          {t("Delete")}
        </Button>
        <Button onClick={handleCloseDialog} color="primary">
          {t("Cancel")}
        </Button>
        <Button 
          onClick={handleSaveDialog} 
          color="primary"
          disabled={
            Object.values(rowModesModel).some((mode) => mode.mode === GridRowModes.Edit) || 
            selectedRow.some((row) => row._error)
          }
        >
          {t("Save")}
        </Button>
      </DialogActions>
    </Dialog>
    <CreateInvoiceModal
      isOpen={modelOpen}
      onClose={handleModalClose}
      projects={projects}
      invoices={invoices}
    />
    </>
  );
};

InvoiceSearch.Loading = InvoiceSearchLoading;

export default InvoiceSearch;
