import React, { useCallback, useEffect, useMemo, useState } from "react";

import { HolidaysResult } from "@/app/api/holidays";
import { TeamTimeSheets } from "@/app/api/timesheets";
import dayGridPlugin from "@fullcalendar/daygrid";
import interactionPlugin from "@fullcalendar/interaction";
import { Autocomplete, Stack, TextField, useTheme } from "@mui/material";
import { useTranslation } from "react-i18next";
import transform from "lodash/transform";
import { getPublicHolidaysForNYears } from "@/app/utils/holidayUtils";
import {
  INPUT_DATE_FORMAT,
  convertDateArrayToString,
} from "@/app/utils/formatUtil";
import StyledFullCalendar from "../StyledFullCalendar";
import { ProjectWithTasks } from "@/app/api/projects";
import { TimeEntry, saveMemberEntry } from "@/app/api/timesheets/actions";
import TimesheetEditModal, {
  Props as TimesheetEditModalProps,
} from "../TimesheetTable/TimesheetEditModal";

export interface Props {
  teamTimesheets: TeamTimeSheets;
  companyHolidays: HolidaysResult[];
  allProjects: ProjectWithTasks[];
}

type MemberOption = TeamTimeSheets[0] & { id: string };

interface EventClickArg {
  event: {
    start: Date | null;
    startStr: string;
    extendedProps: {
      calendar?: string;
      entry?: TimeEntry;
      memberId?: string;
    };
  };
}

const TimesheetAmendment: React.FC<Props> = ({
  teamTimesheets,
  companyHolidays,
  allProjects,
}) => {
  const { t } = useTranslation(["home", "common"]);

  const theme = useTheme();

  const projectMap = useMemo(() => {
    return allProjects.reduce<{
      [id: ProjectWithTasks["id"]]: ProjectWithTasks;
    }>((acc, project) => {
      return { ...acc, [project.id]: project };
    }, {});
  }, [allProjects]);

  // Use a local state to manage updates after a mutation
  const [localTeamTimesheets, setLocalTeamTimesheets] =
    useState(teamTimesheets);

  // member select
  const allMembers = useMemo(() => {
    return transform<TeamTimeSheets[0], MemberOption[]>(
      localTeamTimesheets,
      (acc, memberTimesheet, id) => {
        return acc.push({
          ...memberTimesheet,
          id,
        });
      },
      [],
    );
  }, [localTeamTimesheets]);
  const [selectedStaff, setSelectedStaff] = useState<MemberOption>(
    allMembers[0],
  );
  useEffect(() => {
    setSelectedStaff(
      (currentStaff) =>
        allMembers.find((member) => member.id === currentStaff.id) ||
        allMembers[0],
    );
  }, [allMembers]);

  // edit modal related
  const [editModalProps, setEditModalProps] = useState<
    Partial<TimesheetEditModalProps>
  >({});
  const [editModalOpen, setEditModalOpen] = useState(false);

  const openEditModal = useCallback(
    (defaultValues?: TimeEntry, recordDate?: string) => {
      setEditModalProps({
        defaultValues,
        recordDate,
      });
      setEditModalOpen(true);
    },
    [],
  );

  const closeEditModal = useCallback(() => {
    setEditModalOpen(false);
  }, []);

  // calendar related
  const holidays = useMemo(() => {
    return [
      ...getPublicHolidaysForNYears(2),
      ...companyHolidays.map((h) => ({
        title: h.name,
        date: convertDateArrayToString(h.date, INPUT_DATE_FORMAT),
        extendedProps: {
          calender: "holiday",
        },
      })),
    ].map((e) => ({
      ...e,
      backgroundColor: theme.palette.error.main,
      borderColor: theme.palette.error.main,
    }));
  }, [companyHolidays, theme.palette.error.main]);

  const timeEntries = useMemo(
    () =>
      Object.keys(selectedStaff.timeEntries).flatMap((date, index) => {
        return selectedStaff.timeEntries[date].map((entry) => ({
          id: `${date}-${index}-entry-${entry.id}`,
          date,
          title: `${t("{{count}} hour", {
            ns: "common",
            count: (entry.inputHours || 0) + (entry.otHours || 0),
          })} (${
            entry.projectId
              ? projectMap[entry.projectId].code
              : t("Non-billable task")
          })`,
          backgroundColor: theme.palette.info.main,
          borderColor: theme.palette.info.main,
          extendedProps: {
            calendar: "timeEntry",
            entry,
            memberId: selectedStaff.id,
          },
        }));
      }),
    [projectMap, selectedStaff, t, theme],
  );

  const handleEventClick = useCallback(
    ({ event }: EventClickArg) => {
      if (
        event.extendedProps.calendar === "timeEntry" &&
        event.extendedProps.entry
      ) {
        openEditModal(event.extendedProps.entry, event.startStr);
      }
    },
    [openEditModal],
  );

  const handleDateClick = useCallback(
    (e: { dateStr: string }) => {
      openEditModal(undefined, e.dateStr);
    },
    [openEditModal],
  );

  const handleSave = useCallback(
    async (timeEntry: TimeEntry, recordDate?: string) => {
      // TODO: should be fine, but can handle parse error
      const intStaffId = parseInt(selectedStaff.id);
      const newMemberTimesheets = await saveMemberEntry({
        staffId: intStaffId,
        entry: timeEntry,
        recordDate,
      });
      setLocalTeamTimesheets((timesheets) => ({
        ...timesheets,
        [intStaffId]: {
          ...timesheets[intStaffId],
          timeEntries: newMemberTimesheets,
        },
      }));
      setEditModalOpen(false);
    },
    [selectedStaff.id],
  );

  return (
    <Stack spacing={2}>
      <Autocomplete
        sx={{ maxWidth: 400 }}
        noOptionsText={t("No team members")}
        value={selectedStaff}
        onChange={(_, value) => {
          if (value) setSelectedStaff(value);
        }}
        options={allMembers}
        isOptionEqualToValue={(option, value) => option.id === value.id}
        getOptionLabel={(option) => `${option.staffId} - ${option.name}`}
        renderInput={(params) => <TextField {...params} />}
      />
      <StyledFullCalendar
        plugins={[dayGridPlugin, interactionPlugin]}
        initialView="dayGridMonth"
        buttonText={{ today: t("Today") }}
        events={[...holidays, ...timeEntries]}
        eventClick={handleEventClick}
        dateClick={handleDateClick}
      />
      <TimesheetEditModal
        modalSx={{ maxWidth: 400 }}
        allProjects={allProjects}
        assignedProjects={[]}
        open={editModalOpen}
        onClose={closeEditModal}
        onSave={handleSave}
        {...editModalProps}
      />
    </Stack>
  );
};

export default TimesheetAmendment;
