import { Add, Check, Close, Delete } from "@mui/icons-material";
import { Box, Button, Tooltip, Typography } from "@mui/material";
import {
  FooterPropsOverrides,
  GridActionsCellItem,
  GridCellParams,
  GridColDef,
  GridEditInputCell,
  GridEditSingleSelectCell,
  GridEventListener,
  GridRenderEditCellParams,
  GridRowId,
  GridRowModel,
  GridRowModes,
  GridRowModesModel,
  GridToolbarContainer,
  useGridApiRef,
} from "@mui/x-data-grid";
import { useTranslation } from "react-i18next";
import StyledDataGrid from "../StyledDataGrid";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { useFormContext } from "react-hook-form";
import { RecordLeaveInput, LeaveEntry } from "@/app/api/timesheets/actions";
import { manhourFormatter } from "@/app/utils/formatUtil";
import dayjs from "dayjs";
import isBetween from "dayjs/plugin/isBetween";
import { LeaveType } from "@/app/api/timesheets";
import {
  DAILY_NORMAL_MAX_HOURS,
  LeaveEntryError,
  validateLeaveEntry,
} from "@/app/api/timesheets/utils";
import { roundToNearestQuarter } from "@/app/utils/manhourUtils";

dayjs.extend(isBetween);

interface Props {
  day: string;
  isHoliday: boolean;
  leaveTypes: LeaveType[];
}

type LeaveEntryRow = Partial<
  LeaveEntry & {
    _isNew: boolean;
    _error: LeaveEntryError;
  }
>;

const EntryInputTable: React.FC<Props> = ({ day, leaveTypes, isHoliday }) => {
  const { t } = useTranslation("home");

  const { getValues, setValue, clearErrors } =
    useFormContext<RecordLeaveInput>();
  const currentEntries = getValues(day);

  const [entries, setEntries] = useState<LeaveEntryRow[]>(currentEntries || []);

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

  const apiRef = useGridApiRef();
  const addRow = useCallback(() => {
    const id = Date.now();
    setEntries((e) => [...e, { id, _isNew: true }]);
    setRowModesModel((model) => ({
      ...model,
      [id]: { mode: GridRowModes.Edit, fieldToFocus: "leaveTypeId" },
    }));
  }, []);

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

      const error = validateLeaveEntry(row, isHoliday);

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

  const handleCancel = useCallback(
    (id: GridRowId) => () => {
      setRowModesModel((model) => ({
        ...model,
        [id]: { mode: GridRowModes.View, ignoreModifications: true },
      }));
      const editedRow = entries.find((entry) => entry.id === id);
      if (editedRow?._isNew) {
        setEntries((es) => es.filter((e) => e.id !== id));
      }
    },
    [entries],
  );

  const handleDelete = useCallback(
    (id: GridRowId) => () => {
      setEntries((es) => es.filter((e) => e.id !== id));
    },
    [],
  );

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

  const handleEditStop = useCallback<GridEventListener<"rowEditStop">>(
    (params, event) => {
      if (!validateRow(params.id)) {
        event.defaultMuiPrevented = true;
      }
    },
    [validateRow],
  );

  const processRowUpdate = useCallback((newRow: GridRowModel) => {
    const updatedRow = { ...newRow, _isNew: false };
    setEntries((es) => es.map((e) => (e.id === newRow.id ? updatedRow : e)));
    return updatedRow;
  }, []);

  const columns = useMemo<GridColDef[]>(
    () => [
      {
        type: "actions",
        field: "actions",
        headerName: t("Actions"),
        getActions: ({ id }) => {
          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)}
            />,
          ];
        },
      },
      {
        field: "leaveTypeId",
        headerName: t("Leave Type"),
        width: 200,
        editable: true,
        type: "singleSelect",
        renderEditCell(params: GridRenderEditCellParams<LeaveEntryRow>) {
          const errorMessage =
            params.row._error?.[params.field as keyof LeaveEntry];
          const content = (
            <GridEditSingleSelectCell variant="outlined" {...params} />
          );
          return errorMessage ? (
            <Tooltip title={t(errorMessage)} placement="top">
              <Box width="100%">{content}</Box>
            </Tooltip>
          ) : (
            content
          );
        },
        valueOptions() {
          return leaveTypes.map((p) => ({ value: p.id, label: p.name }));
        },
        valueGetter({ value }) {
          return value ?? "";
        },
      },
      {
        field: "inputHours",
        headerName: t("Leave Hours"),
        width: 150,
        editable: true,
        type: "number",
        renderEditCell(params: GridRenderEditCellParams<LeaveEntryRow>) {
          const errorMessage =
            params.row._error?.[params.field as keyof LeaveEntry];
          const content = <GridEditInputCell {...params} />;
          return errorMessage ? (
            <Tooltip title={t(errorMessage, { DAILY_NORMAL_MAX_HOURS })}>
              <Box width="100%">{content}</Box>
            </Tooltip>
          ) : (
            content
          );
        },
        valueParser(value) {
          return value ? roundToNearestQuarter(value) : value;
        },
        valueFormatter(params) {
          return manhourFormatter.format(params.value);
        },
      },
      {
        field: "remark",
        headerName: t("Remark"),
        sortable: false,
        flex: 1,
        editable: true,
      },
    ],
    [t, rowModesModel, handleDelete, handleSave, handleCancel, leaveTypes],
  );

  useEffect(() => {
    setValue(day, [
      ...entries
        .filter(
          (e) =>
            !e._isNew && !e._error && e.inputHours && e.leaveTypeId && e.id,
        )
        .map((e) => ({
          id: e.id!,
          inputHours: e.inputHours!,
          leaveTypeId: e.leaveTypeId!,
          remark: e.remark,
        })),
    ]);
    clearErrors(day);
  }, [getValues, entries, setValue, day, clearErrors]);

  const footer = (
    <Box display="flex" gap={2} alignItems="center">
      <Button
        disableRipple
        variant="outlined"
        startIcon={<Add />}
        onClick={addRow}
        size="small"
      >
        {t("Record leave")}
      </Button>
    </Box>
  );

  return (
    <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",
        },
      }}
      disableColumnMenu
      editMode="row"
      rows={entries}
      rowModesModel={rowModesModel}
      onRowModesModelChange={setRowModesModel}
      onRowEditStop={handleEditStop}
      processRowUpdate={processRowUpdate}
      columns={columns}
      getCellClassName={(params: GridCellParams<LeaveEntryRow>) => {
        let classname = "";
        if (params.row._error?.[params.field as keyof LeaveEntry]) {
          classname = "hasError";
        }
        return classname;
      }}
      slots={{
        footer: FooterToolbar,
        noRowsOverlay: NoRowsOverlay,
      }}
      slotProps={{
        footer: { child: footer },
      }}
    />
  );
};

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

const FooterToolbar: React.FC<FooterPropsOverrides> = ({ child }) => {
  return <GridToolbarContainer sx={{ p: 2 }}>{child}</GridToolbarContainer>;
};

export default EntryInputTable;
