import { TimeEntry } from "@/app/api/timesheets/actions";
import { Check, ExpandMore } from "@mui/icons-material";
import {
  Accordion,
  AccordionDetails,
  AccordionSummary,
  Alert,
  Box,
  Button,
  FormControl,
  FormHelperText,
  InputLabel,
  Modal,
  ModalProps,
  Paper,
  SxProps,
  TextField,
  Typography,
} from "@mui/material";
import React, { useCallback, useMemo } from "react";
import { Controller, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import ProjectSelect from "./ProjectSelect";
import { AssignedProject, ProjectWithTasks } from "@/app/api/projects";
import {
  distributeQuarters,
  roundToNearestQuarter,
} from "@/app/utils/manhourUtils";
import { manhourFormatter, shortDateFormatter } from "@/app/utils/formatUtil";
import { DAILY_NORMAL_MAX_HOURS } from "@/app/api/timesheets/utils";
import zip from "lodash/zip";
import intersectionBy from "lodash/intersectionBy";
import dayjs from "dayjs";

export interface FastTimeEntryForm {
  projectIds: TimeEntry["projectId"][];
  inputHours: TimeEntry["inputHours"];
  otHours: TimeEntry["otHours"];
  remark: TimeEntry["remark"];
}

export interface Props extends Omit<ModalProps, "children"> {
  onSave: (timeEntries: TimeEntry[], recordDate?: string) => Promise<void>;
  onDelete?: () => void;
  allProjects: ProjectWithTasks[];
  assignedProjects: AssignedProject[];
  modalSx?: SxProps;
  recordDate?: string;
  isHoliday?: boolean;
}

const modalSx: SxProps = {
  position: "absolute",
  top: "50%",
  left: "50%",
  transform: "translate(-50%, -50%)",
  width: "90%",
  maxWidth: "sm",
  maxHeight: "90%",
  padding: 3,
  display: "flex",
  flexDirection: "column",
  gap: 2,
};

let idOffset = Date.now();
const getID = () => {
  return ++idOffset;
};

const MISC_TASK_GROUP_ID = 5;
const FAST_ENTRY_TASK_ID = 42;

const FastTimeEntryModal: React.FC<Props> = ({
  onSave,
  open,
  onClose,
  allProjects,
  assignedProjects,
  modalSx: mSx,
  recordDate,
  isHoliday,
}) => {
  const {
    t,
    i18n: { language },
  } = useTranslation("home");

  const allProjectsWithFastEntry = useMemo(() => {
    return allProjects.filter((p) =>
      p.tasks.find((t) => t.id === FAST_ENTRY_TASK_ID),
    );
  }, [allProjects]);

  const allAssignedProjectsWithFastEntry = useMemo(() => {
    return intersectionBy(assignedProjects, allProjectsWithFastEntry, "id");
  }, [allProjectsWithFastEntry, assignedProjects]);

  const { register, control, reset, trigger, formState, watch } =
    useForm<FastTimeEntryForm>({
      defaultValues: {
        projectIds: [],
      },
    });

  const projectIds = watch("projectIds");
  const inputHours = watch("inputHours");
  const otHours = watch("otHours");
  const remark = watch("remark");

  const selectedProjects = useMemo(() => {
    return projectIds.map((id) =>
      allProjectsWithFastEntry.find((p) => p.id === id),
    );
  }, [allProjectsWithFastEntry, projectIds]);

  const normalHoursArray = distributeQuarters(
    inputHours || 0,
    selectedProjects.length,
  );
  const otHoursArray = distributeQuarters(
    otHours || 0,
    selectedProjects.length,
  );

  const projectsWithHours = zip(
    selectedProjects,
    normalHoursArray,
    otHoursArray,
  );

  const saveHandler = useCallback(async () => {
    const valid = await trigger();
    if (valid) {
      onSave(
        projectsWithHours.map(([project, hour, othour]) => {
          const projectId = project?.id;

          return {
            id: getID(),
            projectId,
            inputHours: hour,
            otHours: othour,
            taskGroupId: MISC_TASK_GROUP_ID,
            taskId: FAST_ENTRY_TASK_ID,
            remark,
          };
        }),
        recordDate,
      );
      reset();
    }
  }, [projectsWithHours, trigger, onSave, recordDate, reset, remark]);

  const closeHandler = useCallback<NonNullable<ModalProps["onClose"]>>(
    (...args) => {
      onClose?.(...args);
      reset();
    },
    [onClose, reset],
  );

  return (
    <Modal open={open} onClose={closeHandler}>
      <Paper sx={{ ...modalSx, ...mSx }}>
        {recordDate && (
          <Typography variant="h6" marginBlockEnd={2}>
            {shortDateFormatter(language).format(new Date(recordDate))}
          </Typography>
        )}
        <FormControl fullWidth error={Boolean(formState.errors.projectIds)}>
          <InputLabel shrink>{t("Project Code and Name")}</InputLabel>
          <Controller
            control={control}
            name="projectIds"
            render={({ field }) => (
              <ProjectSelect
                referenceDay={dayjs(recordDate)}
                includeLeaves={false}
                error={Boolean(formState.errors.projectIds)}
                multiple
                allProjects={allProjectsWithFastEntry}
                assignedProjects={allAssignedProjectsWithFastEntry}
                value={field.value}
                onProjectSelect={(newIds) => {
                  field.onChange(
                    newIds.map((id) => (id === "" ? undefined : id)),
                  );
                }}
              />
            )}
            rules={{
              validate: (value) =>
                value.length > 0 || t("Please choose at least 1 project."),
            }}
          />
          <FormHelperText>
            {formState.errors.projectIds?.message ||
              t(
                'The inputted time will be evenly distributed among the selected projects. Only projects with the Management Timesheet Allocation task can use the fast entry.',
              )}
          </FormHelperText>
        </FormControl>
        <TextField
          type="number"
          label={t("Hours")}
          fullWidth
          {...register("inputHours", {
            setValueAs: (value) => roundToNearestQuarter(parseFloat(value)),
            validate: (value) => {
              if (value) {
                if (isHoliday) {
                  return t("Cannot input normal hours on holidays");
                }

                return (
                  (0 < value && value <= DAILY_NORMAL_MAX_HOURS) ||
                  t(
                    "Input hours should be between 0 and {{DAILY_NORMAL_MAX_HOURS}}",
                    { DAILY_NORMAL_MAX_HOURS },
                  )
                );
              } else {
                return Boolean(value || otHours) || t("Required");
              }
            },
          })}
          error={Boolean(formState.errors.inputHours)}
          helperText={formState.errors.inputHours?.message}
        />
        <TextField
          type="number"
          label={t("Other Hours")}
          fullWidth
          {...register("otHours", {
            setValueAs: (value) => roundToNearestQuarter(parseFloat(value)),
            validate: (value) => (value ? value > 0 : true),
          })}
          error={Boolean(formState.errors.otHours)}
        />
        <TextField
          label={t("Remarks")}
          fullWidth
          multiline
          rows={2}
          error={Boolean(formState.errors.remark)}
          {...register("remark", {
            validate: (value) =>
              projectIds.every((id) => id) ||
              Boolean(value) ||
              t("Required for non-billable tasks"),
          })}
          helperText={
            formState.errors.remark?.message ||
            t("The remark will be added to all selected projects")
          }
        />
        <Accordion variant="outlined" sx={{ overflowY: "scroll" }}>
          <AccordionSummary expandIcon={<ExpandMore />}>
            <Typography variant="subtitle2">
              {t("Hour distribution preview")}
            </Typography>
          </AccordionSummary>
          <AccordionDetails>
            {projectIds.length > 0 ? (
              <ProjectHourSummary projectsWithHours={projectsWithHours} />
            ) : (
              <Alert severity="warning">
                {t("Please select some projects.")}
              </Alert>
            )}
          </AccordionDetails>
        </Accordion>
        <Box display="flex" justifyContent="flex-end">
          <Button
            variant="contained"
            startIcon={<Check />}
            onClick={saveHandler}
          >
            {t("Save")}
          </Button>
        </Box>
      </Paper>
    </Modal>
  );
};

const ProjectHourSummary: React.FC<{
  projectsWithHours: [ProjectWithTasks?, number?, number?][];
}> = ({ projectsWithHours }) => {
  const { t } = useTranslation("home");

  return (
    <Box
      sx={{
        display: "flex",
        flexDirection: "column",
        gap: 2,
      }}
    >
      {projectsWithHours.map(([project, manhour, otManhour], index) => {
        return (
          <Box key={`${index}-${project?.id || "none"}`}>
            <Typography variant="body2" component="div" fontWeight="bold">
              {project
                ? `${project.code} - ${project.name}`
                : t("Non-billable Task")}
            </Typography>
            <Box display="flex" gap={2}>
              <Box>
                <Typography variant="body2" component="div" fontWeight="bold">
                  {t("Hours")}
                </Typography>
                <Typography component="p">
                  {manhourFormatter.format(manhour || 0)}
                </Typography>
              </Box>
              <Box>
                <Typography variant="body2" component="div" fontWeight="bold">
                  {t("Other Hours")}
                </Typography>
                <Typography component="p">
                  {manhourFormatter.format(otManhour || 0)}
                </Typography>
              </Box>
            </Box>
          </Box>
        );
      })}
    </Box>
  );
};

export default FastTimeEntryModal;
