import { Task } from "@/app/api/tasks";
import {
  Box,
  Typography,
  Grid,
  Paper,
  List,
  ListItemButton,
  ListItemText,
  TextField,
} from "@mui/material";
import { useState, useCallback, useEffect, useMemo } from "react";
import { useTranslation } from "react-i18next";
import { Props as ResourceMilestoneProps } from "./ResourceMilestone";
import StyledDataGrid from "../StyledDataGrid";
import { useForm, useFormContext } from "react-hook-form";
import { GridColDef, GridRowModel, useGridApiRef } from "@mui/x-data-grid";
import {
  CreateProjectInputs,
  ManhourAllocation,
} from "@/app/api/projects/actions";
import isEmpty from "lodash/isEmpty";
import _reduce from "lodash/reduce";

const mockGrades = [1, 2, 3, 4, 5];

interface Props {
  tasks: Task[];
  manhourBreakdownByGrade: ResourceMilestoneProps["defaultManhourBreakdownByGrade"];
}

type Row = ManhourAllocation & { id: "manhourAllocation" };

const parseValidManhours = (value: number | string): number => {
  const inputNumber = Number(value);
  return isNaN(inputNumber) || inputNumber < 0 ? 0 : inputNumber;
};

const ResourceSection: React.FC<Props> = ({
  tasks,
  manhourBreakdownByGrade = mockGrades.reduce<
    NonNullable<Props["manhourBreakdownByGrade"]>
  >((acc, grade) => {
    return { ...acc, [grade]: 1 };
  }, {}),
}) => {
  const { t } = useTranslation();
  const [selectedTaskId, setSelectedTaskId] = useState(tasks[0].id);
  const makeOnTaskSelect = useCallback(
    (taskId: Task["id"]): React.MouseEventHandler =>
      () => {
        return setSelectedTaskId(taskId);
      },
    [],
  );

  useEffect(() => {
    setSelectedTaskId(tasks[0].id);
  }, [tasks]);

  const { getValues, setValue } = useFormContext<CreateProjectInputs>();

  const updateTaskAllocations = useCallback(
    (newAllocations: ManhourAllocation) => {
      setValue("tasks", {
        ...getValues("tasks"),
        [selectedTaskId]: {
          manhourAllocation: newAllocations,
        },
      });
    },
    [getValues, selectedTaskId, setValue],
  );

  const gridApiRef = useGridApiRef();
  const columns = useMemo(() => {
    return mockGrades.map<GridColDef>((grade) => ({
      field: grade.toString(),
      editable: true,
      sortable: false,
      width: 120,
      headerName: t("Grade {{grade}}", { grade }),
      type: "number",
      valueParser: parseValidManhours,
      valueFormatter(params) {
        return Number(params.value).toFixed(2);
      },
    }));
  }, [t]);

  const rows = useMemo<Row[]>(() => {
    const initialAllocation =
      getValues("tasks")[selectedTaskId].manhourAllocation;
    if (!isEmpty(initialAllocation)) {
      return [{ ...initialAllocation, id: "manhourAllocation" }];
    }
    return [
      mockGrades.reduce(
        (acc, grade) => {
          return { ...acc, [grade]: 0 };
        },
        { id: "manhourAllocation" },
      ),
    ];
  }, [getValues, selectedTaskId]);

  const initialManhours = useMemo(() => {
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const { id, ...allocations } = rows[0];
    return Object.values(allocations).reduce((acc, hours) => acc + hours, 0);
  }, [rows]);

  const {
    register,
    reset,
    getValues: getManhourFormValues,
    setValue: setManhourFormValue,
  } = useForm<{ manhour: number }>({
    defaultValues: {
      manhour: initialManhours,
    },
  });

  // Reset man hour input when task changes
  useEffect(() => {
    reset({ manhour: initialManhours });
  }, [initialManhours, reset, selectedTaskId]);

  const updateAllocation = useCallback(() => {
    const inputHour = getManhourFormValues("manhour");
    const ratioSum = Object.values(manhourBreakdownByGrade).reduce(
      (acc, ratio) => acc + ratio,
      0,
    );
    const newAllocations = _reduce(
      manhourBreakdownByGrade,
      (acc, value, key) => {
        return { ...acc, [key]: (inputHour / ratioSum) * value };
      },
      {},
    );
    gridApiRef.current.updateRows([
      { id: "manhourAllocation", ...newAllocations },
    ]);
    updateTaskAllocations(newAllocations);
  }, [
    getManhourFormValues,
    gridApiRef,
    manhourBreakdownByGrade,
    updateTaskAllocations,
  ]);

  const processRowUpdate = useCallback(
    (newRow: GridRowModel) => {
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      const { id: rowId, ...newAllocations } = newRow;
      const totalHours = Object.values(
        newAllocations as ManhourAllocation,
      ).reduce<number>((acc, hour) => acc + hour, 0);
      setManhourFormValue("manhour", totalHours);
      updateTaskAllocations(newAllocations);
      return newRow;
    },
    [setManhourFormValue, updateTaskAllocations],
  );

  return (
    <Box marginBlock={4}>
      <Typography variant="overline" display="block" marginBlockEnd={1}>
        {t("Task Breakdown")}
      </Typography>
      <Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
        <Grid item xs={6}>
          <Paper elevation={2}>
            <List dense sx={{ maxHeight: 300, overflow: "auto" }}>
              {tasks.map((task, index) => {
                return (
                  <ListItemButton
                    selected={selectedTaskId === task.id}
                    key={`${task.id}-${index}`}
                    onClick={makeOnTaskSelect(task.id)}
                  >
                    <ListItemText primary={task.name} />
                  </ListItemButton>
                );
              })}
            </List>
          </Paper>
        </Grid>
        <Grid item xs={6}>
          <TextField
            label={t("Mahours Allocated to Task")}
            fullWidth
            type="number"
            {...register("manhour", {
              valueAsNumber: true,
              onBlur: updateAllocation,
            })}
          />
          <Paper
            elevation={2}
            sx={{
              marginBlockStart: 2,
              ".MuiDataGrid-root .MuiDataGrid-columnHeader:focus-within": {
                outlineOffset: -2,
              },
            }}
          >
            <StyledDataGrid
              apiRef={gridApiRef}
              disableColumnMenu
              hideFooter
              disableRowSelectionOnClick
              rows={rows}
              columns={columns}
              processRowUpdate={processRowUpdate}
              sx={{ paddingBlockEnd: 2 }}
            />
          </Paper>
        </Grid>
      </Grid>
    </Box>
  );
};

export default ResourceSection;
