import React, { useCallback, useMemo, useState, useEffect, SyntheticEvent } from "react";
import { useTranslation } from "react-i18next";
import { Button, TextField, CardContent, Typography, Divider, Card, Box, Autocomplete, MenuItem, AutocompleteChangeReason, AutocompleteChangeDetails } from "@mui/material";
import { Add, Check, Close, ControlPointSharp, Delete} from "@mui/icons-material";
import { invoiceList } from "@/app/api/invoices";
import { 
  GridCellParams, 
  GridColDef, 
  GridEventListener, 
  GridRowId, 
  GridRowModel, 
  GridRowModes, 
  GridRowModesModel,
  GridRenderEditCellParams,
  GridRenderCellParams,
  useGridApiContext,
  GridActionsCellItem,
  GridRowIdGetter, 
} from "@mui/x-data-grid";
import { useGridApiRef } from "@mui/x-data-grid";
import StyledDataGrid from "../StyledDataGrid";
import { GridToolbarContainer } from "@mui/x-data-grid";
import { FooterPropsOverrides } from "@mui/x-data-grid";
import { useFormContext } from "react-hook-form";
import { ProjectResult } from "@/app/api/projects";

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

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

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

class ProcessRowUpdateError extends Error {
  public readonly row: invoiceListRow;
  public readonly errors: InvoiceListError | undefined;
  constructor(
    row: invoiceListRow,
    message?: string,
    errors?: InvoiceListError,
  ) {
    super(message);
    this.row = row;
    this.errors = errors;

    Object.setPrototypeOf(this, ProcessRowUpdateError.prototype);
  }
}
type project = {
  label: string;
  value: number;
}
const InvoiceTable: React.FC<Props> = ({ projects, invoices }) => {
    // if change this to code - name,
    // also change the submit function
    const projectCombos = projects.map(item => item.code)
    const { t } = useTranslation()
    const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({});
    const [selectedRow, setSelectedRow] = useState<invoiceListRow[] | []>([]);
    const { getValues, setValue, clearErrors, setError } = useFormContext<any>();
    const apiRef = useGridApiRef();
    // const [projectCode, setProjectCode] = useState<project>({label: "", value: 0})
    const getRowId = useCallback<GridRowIdGetter<invoiceListRow>>(
      (row) => row.id != undefined ? row.id : 0,
      [],
    );
    const validateInvoiceEntry = (
        entry: Partial<invoiceList>,
    ): InvoiceListError | undefined => {
        // Test for errors
        const error: any = {};

        if (!entry.issuedAmount) {
            error.issuedAmount = true
            error.message = t("Please input issued amount ")
        } else if (!entry.issuedAmount) {
            error.issuedAmount = true
            error.message = t("Please input received amount")
        } else if (!entry.issuedDate) {
            error.issuedDate = true
            error.message = t("Please input issue date")
        } else if (!entry.projectCode){
          error.projectCode = true
          error.message = t("Please select project code")
        } else if(!entry.invoiceNo){
          error.invoiceNo = true
          error.message = t("Please input invoice No")
        }else if(isInvoiceExists(entry.invoiceNo)){
          error.invoiceNo = true
          error.message = t("Duplicate Invoice No")
        }

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

  const isInvoiceExists = (invoiceNo: string): boolean => {
    const result = invoices.some(i => i.invoiceNo === invoiceNo)
    return result
  }


    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;
        },
        [],
    );

    const handleEditStop = useCallback<GridEventListener<"rowEditStop">>(
        (params, event) => {
            const row = apiRef.current.getRowWithUpdatedValues(
              params.id,
              "",
            )
            // console.log(params)
            // console.log(apiRef.current)
            // console.log(validateRow(params.id) !== undefined)
            // console.log(!validateRow(params.id))
            if (validateRow(params.id) !== undefined && !validateRow(params.id)) {
            
            setRowModesModel((model) => ({
                ...model,
                [params.id]: { mode: GridRowModes.View},
            }));
            
            setSelectedRow((row) => [...row] as any[])
            event.defaultMuiPrevented = true;
            } else {
              // console.log(row)
              const error = validateRow(params.id)
              setSelectedRow((row) => {
                const updatedRow = row.map(r => r.id === params.id ? { ...r, _error: error } : r);
                return updatedRow;
              })
            }
            // console.log(row)
        },
        [validateRow],
    );

    const addRow = useCallback(() => {
        const newEntry = { id: Date.now(), _isNew: true };
        setSelectedRow((e) => [...e, newEntry]);
        setRowModesModel((model) => ({
            ...model,
            [getRowId(newEntry)]: { mode: GridRowModes.Edit },
        }));
    }, [getRowId]);

    const handleSave = useCallback(
      (id: GridRowId) => () => {
        setRowModesModel((model) => ({
          ...model,
          [id]: { mode: GridRowModes.View },
        }));
      },
      [],
    );

    const handleCancel = useCallback(
      (id: GridRowId) => () => {
        setRowModesModel((model) => ({
          ...model,
          [id]: { mode: GridRowModes.View, ignoreModifications: true },
        }));
        const editRow = selectedRow.find(row => getRowId(row) === id)
        if (editRow?._isNew) {
          setSelectedRow((row => row.filter(r => getRowId(r) !== id)))
        }else{
          setSelectedRow((row) => 
            row.map((r) => 
              getRowId(r) === id
              ? { ...r, _error: undefined}
              : r
            )
          )
        }
      },
      [selectedRow, getRowId],
    );

    const handleDelete = useCallback(
      (id: GridRowId) => () => {
        setSelectedRow((row => row.filter(r => getRowId(r) !== id)))
      },
      [getRowId],
    )

    const processRowUpdate = useCallback(
        (
          newRow: GridRowModel<invoiceListRow>, 
          originalRow: GridRowModel<invoiceListRow>,
        ) => {
          // console.log(newRow)
          // console.log(originalRow)
          const errors = validateRow(newRow.id!!);
          if (errors) {
            // console.log(errors)
            // throw new error for error checking
            throw new ProcessRowUpdateError(
              originalRow, 
              "validation error",
              errors,
            )
          }
          // eslint-disable-next-line @typescript-eslint/no-unused-vars
          const { _isNew, _error, ...updatedRow } = newRow;
    
          const rowToSave = {
            ...updatedRow,
          } satisfies invoiceListRow;

          console.log(newRow)

          setSelectedRow((es) =>
            es.map((e) => (e.id === originalRow.id ? rowToSave : e))
          );
          console.log(rowToSave)
          return rowToSave;
        },
        [validateRow],
      );

    const hasRowErrors = selectedRow.some(row => row._error !== undefined)

    /**
     * Add callback to check error
     */
    const onProcessRowUpdateError = useCallback(
      (updateError: ProcessRowUpdateError) => {
        const errors = updateError.errors;
        const oldRow = updateError.row;
        // console.log(errors)
        apiRef.current.updateRows([{ ...oldRow, _error: errors }]);
      },
      [apiRef]
    )

    useEffect(() => {
      console.log(selectedRow)
      setValue("data", selectedRow)
    }, [selectedRow, setValue]);

  function renderAutocomplete(params: GridRenderCellParams<any, number>) {
    console.log(params)
    return(
      <Box sx={{ display: 'flex', alignItems: 'center', pr: 2, }}>
        <Autocomplete
         readOnly 
         sx={{ width: 300 }}
         value={params.row.projectCode}
         options={projectCombos}
         renderInput={(params) => <TextField {...params} />}
         />
      </Box>
      ) 
  }
  
  function AutocompleteInput(props: GridRenderCellParams<any, number>) {
    const { id, value, field, hasFocus } = props;
    const apiRef = useGridApiContext();
    
    console.log(value)

    const handleValueChange =  useCallback((newValue: any) => {
      // console.log(id, field, newValue)
      apiRef.current.setEditCellValue({ id, field, value: newValue })
    }, []);

    // console.log(projectCombos.find(o => o?.toString() === value?.toString()))

    return (
      <Box sx={{ display: 'flex', alignItems: 'center', pr: 2 }}>
        <Autocomplete
          disablePortal
          value={props.row.projectCode}
          options={projectCombos}
          sx={{ width: 300 }}
          onChange={(event: React.SyntheticEvent<Element, Event>, value: string | null, ) => handleValueChange(value)}
          renderInput={(params) => <TextField {...params} />}
        />
      </Box>
    );
  }

  const renderAutocompleteInput: GridColDef['renderCell'] = (params) => {
    return <AutocompleteInput {...params} />;
  };

const editCombinedColumns = useMemo<GridColDef[]>(
    () => [
        { field: "invoiceNo", headerName: t("Invoice No"), editable: true, flex: 1 },
        { field: "projectCode", 
          headerName: t("Project Code"), 
          editable: true, 
          flex: 1,
          renderCell: renderAutocomplete,
          renderEditCell: renderAutocompleteInput
        },
        { field: "issuedDate", 
        headerName: t("Issue Date"), 
        editable: true, 
        flex: 0.5,  
        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.7,
        type: 'number'
        },
        { 
        field: "receiptDate", 
        headerName: t("Settle Date"), 
        editable: true,
        flex: 0.5,
        type: 'date', 
        },
        { 
        field: "receivedAmount", 
        headerName: t("Actual Received Amount (HKD)"), 
        editable: true, 
        flex: 0.7,
        type: 'number'
        },
        {
          type: "actions",
          field: "actions",
          headerName: t("Actions"),
          getActions: ({ id }) => {
            console.log(rowModesModel[id]?.mode)
            if (rowModesModel[id]?.mode === GridRowModes.Edit) {
              return [
                <GridActionsCellItem
                  key="accpet-action"
                  icon={<Check />}
                  label={t("Save")}
                  onClick={handleSave(id)}
                />,
                <GridActionsCellItem
                  key="cancel-action"
                  icon={<Close />}
                  label={t("Cancel")}
                  onClick={handleCancel(id)}
                />,
              ];
            }
  
            return [
              <GridActionsCellItem
                key="delete-action"
                icon={<Delete />}
                label={t("Remove")}
                onClick={handleDelete(id)}
              />,
            ];
          },
        },
    ],
    [t, rowModesModel, handleSave, handleCancel, handleDelete]
    )

const footer = (
  <Box display="flex" gap={2} alignItems="center">
    <Button
      disableRipple
      variant="outlined"
      startIcon={<Add />}
      onClick={addRow}
      size="small"
    >
      {t("Create Invoice")}
    </Button>
    {
      hasRowErrors &&
      <Typography color="warning.main" variant="body2">
          {t("There are errors!")} {selectedRow.find(row => row._error !== null)?._error?.message}
      </Typography>
    }
  </Box>
);

    return (
      <>
<StyledDataGrid
        apiRef={apiRef}
        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",
            },
            height: 400, width: '95%'
          }}
        disableColumnMenu
        editMode="row"
        rows={selectedRow}
        rowModesModel={rowModesModel}
        onRowModesModelChange={setRowModesModel}
        onRowEditStop={handleEditStop}
        columns={editCombinedColumns}
        processRowUpdate={processRowUpdate}
        onProcessRowUpdateError={onProcessRowUpdateError}
        getCellClassName={(params: GridCellParams<invoiceListRow>) => {
          let classname = "";
          if (params.row._error?.[params.field as keyof invoiceList]) {
            classname = "hasError";
          } 
          return classname;
        }}
        slots={{
            footer: FooterToolbar,
            noRowsOverlay: NoRowsOverlay,
        }}
        slotProps={{
            footer: { child: footer },
        }}
      />
      </>
    )   
}

export default InvoiceTable

const NoRowsOverlay: React.FC = () => {
    const { t } = useTranslation("home");
    return (
      <Box
        display="flex"
        justifyContent="center"
        alignItems="center"
        height="100%"
      >
        <Typography variant="caption">{t("Add some time entries!")}</Typography>
      </Box>
    );
};
  
const FooterToolbar: React.FC<FooterPropsOverrides> = ({ child }) => {
    return <GridToolbarContainer sx={{ p: 2 }}>{child}</GridToolbarContainer>;
};