import { CreateProjectInputs, PaymentInputs } from "@/app/api/projects/actions";
import { TaskGroup } from "@/app/api/tasks";
import { Add, Check, Close, Delete } from "@mui/icons-material";
import {
  Stack,
  Typography,
  Grid,
  FormControl,
  Box,
  Button,
} from "@mui/material";
import {
  GridColDef,
  GridActionsCellItem,
  GridToolbarContainer,
  GridRowModesModel,
  GridRowModes,
  FooterPropsOverrides,
  GridRowId,
  GridEventListener,
  useGridApiRef,
  GridRowModel,
} from "@mui/x-data-grid";
import { LocalizationProvider, DatePicker } from "@mui/x-date-pickers";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import dayjs from "dayjs";
import "dayjs/locale/zh-hk";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
import StyledDataGrid from "../StyledDataGrid";
import { moneyFormatter } from "@/app/utils/formatUtil";
import isDate from "lodash/isDate";

interface Props {
  taskGroupId: TaskGroup["id"];
}

declare module "@mui/x-data-grid" {
  interface FooterPropsOverrides {
    onAdd: () => void;
    child?: React.ReactNode;
  }
}

type PaymentRow = Partial<PaymentInputs & { _isNew: boolean; _error: string }>;

const MilestoneSection: React.FC<Props> = ({ taskGroupId }) => {
  const { t } = useTranslation();
  const { getValues, setValue } = useFormContext<CreateProjectInputs>();
  const [payments, setPayments] = useState<PaymentRow[]>(
    getValues("milestones")[taskGroupId]?.payments || [],
  );
  const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({});
  // Change the payments table depending on the TaskGroupId
  useEffect(() => {
    setPayments(getValues("milestones")[taskGroupId]?.payments || []);
    setRowModesModel({});
  }, [getValues, taskGroupId]);

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

  const validateRow = useCallback(
    (id: GridRowId) => {
      const row = apiRef.current.getRowWithUpdatedValues(id, "") as PaymentRow;
      let error: keyof PaymentInputs | "" = "";
      if (!row.description) {
        error = "description";
      } else if (!(isDate(row.date) && row.date.getTime())) {
        error = "date";
      } else if (!(row.amount && row.amount >= 0)) {
        error = "amount";
      }

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

  const handleCancel = useCallback(
    (id: GridRowId) => () => {
      setRowModesModel((model) => ({
        ...model,
        [id]: { mode: GridRowModes.View, ignoreModifications: true },
      }));
      const editedRow = payments.find((payment) => payment.id === id);
      if (editedRow?._isNew) {
        setPayments((ps) => ps.filter((p) => p.id !== id));
      }
    },
    [payments],
  );

  const handleDelete = useCallback(
    (id: GridRowId) => () => {
      setPayments((ps) => ps.filter((p) => p.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 };
    setPayments((ps) => ps.map((p) => (p.id === newRow.id ? updatedRow : p)));
    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: "description",
        headerName: t("Payment Milestone Description"),
        width: 300,
        editable: true,
      },
      {
        field: "date",
        headerName: t("Payment Milestone Date"),
        width: 200,
        type: "date",
        editable: true,
        valueGetter(params) {
          return new Date(params.value);
        },
      },
      {
        field: "amount",
        headerName: t("Payment Milestone Amount"),
        width: 300,
        editable: true,
        type: "number",
        valueFormatter(params) {
          return moneyFormatter.format(params.value);
        },
      },
    ],
    [handleCancel, handleDelete, handleSave, rowModesModel, t],
  );

  useEffect(() => {
    const milestones = getValues("milestones");
    setValue("milestones", {
      ...milestones,
      [taskGroupId]: {
        ...milestones[taskGroupId],
        payments: payments
          .filter((p) => !p._isNew && !p._error)
          .map((p) => ({
            description: p.description!,
            id: p.id!,
            amount: p.amount!,
            date: dayjs(p.date!).toISOString(),
          })),
      },
    });
  }, [getValues, payments, setValue, taskGroupId]);

  return (
    <Stack gap={1}>
      <Typography variant="overline" display="block" marginBlockEnd={1}>
        {t("Task Stage Milestones")}
      </Typography>
      <LocalizationProvider dateAdapter={AdapterDayjs} adapterLocale="zh-hk">
        <Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
          <Grid item xs>
            <FormControl fullWidth>
              <DatePicker
                label={t("Stage Start Date")}
                value={dayjs(getValues("milestones")[taskGroupId]?.startDate)}
                onChange={(date) => {
                  if (!date) return;
                  const milestones = getValues("milestones");
                  setValue("milestones", {
                    ...milestones,
                    [taskGroupId]: {
                      ...milestones[taskGroupId],
                      startDate: date.toISOString(),
                    },
                  });
                }}
              />
            </FormControl>
          </Grid>
          <Grid item xs>
            <FormControl fullWidth>
              <DatePicker
                label={t("Stage End Date")}
                value={dayjs(getValues("milestones")[taskGroupId]?.endDate)}
                onChange={(date) => {
                  if (!date) return;
                  const milestones = getValues("milestones");
                  setValue("milestones", {
                    ...milestones,
                    [taskGroupId]: {
                      ...milestones[taskGroupId],
                      endDate: date.toISOString(),
                    },
                  });
                }}
              />
            </FormControl>
          </Grid>
        </Grid>
      </LocalizationProvider>
      <Box
        sx={(theme) => ({
          marginBlockStart: 1,
          marginInline: -3,
          borderBottom: `1px solid ${theme.palette.divider}`,
        })}
      >
        <StyledDataGrid
          apiRef={apiRef}
          autoHeight
          sx={{
            "--DataGrid-overlayHeight": "100px",
            ".MuiDataGrid-row .MuiDataGrid-cell.hasError": {
              border: "1px solid",
              borderColor: "error.main",
            },
          }}
          disableColumnMenu
          editMode="row"
          rows={payments}
          rowModesModel={rowModesModel}
          onRowModesModelChange={setRowModesModel}
          onRowEditStop={handleEditStop}
          processRowUpdate={processRowUpdate}
          columns={columns}
          getCellClassName={(params) => {
            return params.row._error === params.field ? "hasError" : "";
          }}
          slots={{
            footer: FooterToolbar,
            noRowsOverlay: NoRowsOverlay,
          }}
          slotProps={{
            footer: { onAdd: addRow },
          }}
        />
      </Box>
    </Stack>
  );
};

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

const FooterToolbar: React.FC<FooterPropsOverrides> = ({ onAdd }) => {
  const { t } = useTranslation();
  return (
    <GridToolbarContainer sx={{ p: 2 }}>
      <Button
        variant="outlined"
        startIcon={<Add />}
        onClick={onAdd}
        size="small"
      >
        {t("Add Payment Milestone")}
      </Button>
    </GridToolbarContainer>
  );
};

export default MilestoneSection;
