import React, { useCallback, useMemo, useState, useEffect } 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 } from "@/app/utils/formatUtil"
import { Button, ButtonGroup, Stack, Tab, Tabs, TabsProps, Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, TextField, CardContent, Typography, Divider, Card, Box, Autocomplete, MenuItem } from "@mui/material";
import FileUploadIcon from '@mui/icons-material/FileUpload';
import { Add, Check, Close, Delete } from "@mui/icons-material";
import { invoiceList, issuedInvoiceList, issuedInvoiceSearchForm, receivedInvoiceList, receivedInvoiceSearchForm } from "@/app/api/invoices";
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
import { 
  GridCellParams, 
  GridColDef, 
  GridEventListener, 
  GridRowId, 
  GridRowModel, 
  GridRowModes, 
  GridRowModesModel,
  GridRenderEditCellParams,
  useGridApiContext, 
} from "@mui/x-data-grid";
import { useGridApiRef } from "@mui/x-data-grid";
import StyledDataGrid from "../StyledDataGrid";

import { uniq } from "lodash";
import CreateInvoiceModal from "./CreateExpenseModal";
import { GridToolbarContainer } from "@mui/x-data-grid";
import { FooterPropsOverrides } from "@mui/x-data-grid";
import { th } from "@faker-js/faker";
import { GridRowIdGetter } from "@mui/x-data-grid";
import { useFormContext } from "react-hook-form";
import { ProjectResult } from "@/app/api/projects";
import { ProjectExpensesResultFormatted } from "@/app/api/projectExpenses";
import { GridRenderCellParams } from "@mui/x-data-grid";

type ExpenseListError = {
    [field in keyof ProjectExpensesResultFormatted]?: string;
}& {
  message?: string;
};

type ExpenseListRow = Partial<
  ProjectExpensesResultFormatted & {
    _isNew: boolean;
    _error: ExpenseListError;
  }
>;

interface Props {
  projects: ProjectResult[];
}

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

    Object.setPrototypeOf(this, ProcessRowUpdateError.prototype);
  }
}
type project = {
  label: string;
  value: number;
}
const ExpenseTable: React.FC<Props> = ({ projects }) => {
    console.log(projects)
    const projectCombos = projects.map(item => item.code)
    const { t } = useTranslation()
    const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({});
    const [selectedRow, setSelectedRow] = useState<ExpenseListRow[] | []>([]);
    const { getValues, setValue, clearErrors, setError } =
    useFormContext<any>();
    const apiRef = useGridApiRef();
    const validateExpenseEntry = (
        entry: Partial<ProjectExpensesResultFormatted>,
    ): ExpenseListError | undefined => {
        // Test for errors
        const error: ExpenseListError = {};
        if (!entry.issueDate) {
          error.issueDate = "Please input issued date";
          error.message = "Please input issued date";
        }
        if (!entry.amount) {
          error.amount = "Please input amount";
          error.message = "Please input amount"
        }
        if (!entry.projectCode) {
          error.projectCode = "Please input project code";
          error.message = "Please input project code";
        }

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

    const validateRow = useCallback(
        (id: GridRowId) => {
          const row = apiRef.current.getRowWithUpdatedValues(
            id,
            "",
          )
    
          const error = validateExpenseEntry(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(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},
            }));
            
            console.log(row)
            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 id = Date.now();
        setSelectedRow((e) => [...e, { id, _isNew: true }]);
        setRowModesModel((model) => ({
            ...model,
            [id]: { mode: GridRowModes.Edit },
        }));
    }, []);

    const processRowUpdate = useCallback(
        (
          newRow: GridRowModel<ExpenseListRow>, 
          originalRow: GridRowModel<ExpenseListRow>,
        ) => {
          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 ExpenseListRow;

          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>) {
    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();
    const ref = React.useRef<HTMLElement>(null);
    
    const handleValueChange = useCallback((newValue: any) => {
      console.log(newValue)
      apiRef.current.setEditCellValue({ id, field, value: newValue })
    }, []);

    return (
      <Box sx={{ display: 'flex', alignItems: 'center', pr: 2 }}>
        <Autocomplete
          disablePortal
          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: "expenseNo", 
          headerName: t("Expense No"), 
          editable: true, 
          flex: 0.5 
        },
        { 
          field: "projectCode", 
          headerName: t("Project Code"), 
          editable: true, 
          flex: 0.3,
          renderCell: renderAutocomplete,
          renderEditCell: renderAutocompleteInput
        },
        { 
          field: "issueDate", 
          headerName: t("Issue Date"), 
          editable: true, 
          flex: 0.4,  
          type: 'date', 
        },
        { 
          field: "amount", 
          headerName: t("Amount (HKD)"), 
          editable: true, 
          flex: 0.5,
          type: 'number'
        },
        // { 
        //   field: "receiptDate", 
        //   headerName: t("Settle Date"), 
        //   editable: true,
        //   flex: 0.4,
        //   type: 'date', 
        // },
        { 
          field: "remarks", 
          headerName: t("Remarks"), 
          editable: true,
          flex: 1,
        },
    ],
    [t]
    )

const footer = (
  <Box display="flex" gap={2} alignItems="center">
    <Button
      disableRipple
      variant="outlined"
      startIcon={<Add />}
      onClick={addRow}
      size="small"
    >
      {t("Create Expense")}
    </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<ExpenseListRow>) => {
          let classname = "";
          if (params.row._error?.[params.field as keyof ProjectExpensesResultFormatted]) {
            classname = "hasError";
          } 
          return classname;
        }}
        slots={{
            footer: FooterToolbar,
            noRowsOverlay: NoRowsOverlay,
        }}
        slotProps={{
            footer: { child: footer },
        }}
      />
      </>
    )   
}

export default ExpenseTable

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>;
};