import { Check, ExpandMore } from "@mui/icons-material";
import {
  Accordion,
  AccordionDetails,
  AccordionSummary,
  Alert,
  Box,
  Button,
  Divider,
  FormControl,
  FormHelperText,
  InputLabel,
  MenuItem,
  Modal,
  ModalProps,
  Paper,
  Select,
  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 {
  INPUT_DATE_FORMAT,
  moneyFormatter,
  OUTPUT_DATE_FORMAT,
  truncateMoney,
} from "@/app/utils/formatUtil";
import { PaymentInputs } from "@/app/api/projects/actions";
import dayjs from "dayjs";
import { DatePicker } from "@mui/x-date-pickers";

export interface BulkAddPaymentForm {
  numberOfEntries: number;
  amountToDivide: number;
  dateType: "monthly" | "weekly" | "fixed";
  dateReference?: dayjs.Dayjs;
  description: string;
}

export interface Props extends Omit<ModalProps, "children"> {
  onSave: (payments: PaymentInputs[]) => Promise<void>;
  modalSx?: SxProps;
}

type DatetypeOption = {
  value: string;
  label: string;
  unit: 'week' | 'month' | 'year';
  step: number;
};

const datetypeOptions: DatetypeOption[] = [
  { value: "weekly",     label: "Weekly",           unit: "week",  step: 1 },
  { value: "biweekly",   label: "Bi-Weekly",  unit: "week",  step: 2 },
  { value: "monthly",    label: "Monthly",          unit: "month", step: 1 },
  // { value: "bimonthly",  label: "Every two months from", unit: "month", step: 2 },
  { value: "quarterly",  label: "Quarterly",    unit: "month", step: 3 },
  { value: "half-year",  label: "Half Year",  unit: "month", step: 6 },
  // { value: "yearly",     label: "Yearly from",           unit: "year",  step: 1 },
  { value: "fixed",      label: "Fixed",                 unit: "month", step: 0 }, // unit/step not used for fixed
];

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 BulkAddPaymentModal: React.FC<Props> = ({
  onSave,
  open,
  onClose,
  modalSx: mSx,
}) => {
  const { t } = useTranslation();

  const { register, reset, trigger, formState, watch, control } =
    useForm<BulkAddPaymentForm>({
      mode: "onTouched",
      defaultValues: { dateType: "monthly", dateReference: dayjs() },
    });

  const formValues = watch();

  const newPayments = useMemo<PaymentInputs[]>(() => {
    const {
      numberOfEntries,
      amountToDivide,
      dateType,
      dateReference = dayjs(),
      description,
    } = formValues;

    if (
      Number.isInteger(numberOfEntries) &&
      numberOfEntries > 0 &&
      amountToDivide &&
      description
    ) {
      const dividedAmount = truncateMoney(amountToDivide / numberOfEntries)!;
      const amountForLastItem = truncateMoney(
        amountToDivide - dividedAmount * (numberOfEntries - 1),
      )!;

      const datetypeOption = datetypeOptions.find(r => r.value === dateType);

      return Array(numberOfEntries)
        .fill(undefined)
        .map((_, index) => {
          const date =
            dateType === "fixed"
              ? dateReference
              : dateReference.add(
                  index * (datetypeOption?.step ?? 0),
                  datetypeOption?.unit ?? "month",
                );

          return {
            id: getID(),
            amount:
              index === numberOfEntries - 1 ? amountForLastItem : dividedAmount,
            description: replaceTemplateString(description, {
              "{index}": (index + 1).toString(),
              "{date}": date.format(OUTPUT_DATE_FORMAT),
            }),
            date: date.format(INPUT_DATE_FORMAT),
          };
        });
    } else {
      return [];
    }
  }, [formValues]);

  const saveHandler = useCallback(async () => {
    const valid = await trigger();
    if (valid) {
      onSave(newPayments);
      reset();
    }
  }, [trigger, onSave, reset, newPayments]);

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

  return (
    <Modal open={open} onClose={closeHandler}>
      <Paper sx={{ ...modalSx, ...mSx }}>
        <TextField
          type="number"
          label={t("Total amount to divide")}
          fullWidth
          {...register("amountToDivide", {
            valueAsNumber: true,
            required: t("Required"),
          })}
          error={Boolean(formState.errors.amountToDivide)}
          helperText={
            formState.errors.amountToDivide?.message ||
            t(
              "The inputted amount will be evenly divided by the inputted number of payments",
            )
          }
        />
        <TextField
          type="number"
          label={t("Number of payments")}
          fullWidth
          {...register("numberOfEntries", {
            valueAsNumber: true,
            required: t("Required"),
            validate: (value) => {
              if (!value) {
                return t("Required");
              } else if (value < 0) {
                return t("Number must be positive");
              } else if (!Number.isInteger(value)) {
                return t("Number must be an integer");
              } else {
                return true;
              }
            },
          })}
          error={Boolean(formState.errors.numberOfEntries)}
          helperText={formState.errors.numberOfEntries?.message}
        />
        <FormControl fullWidth error={Boolean(formState.errors.dateType)}>
          <InputLabel shrink>{t("Default Date Type")}</InputLabel>
          <Controller
            control={control}
            name="dateType"
            render={({ field }) => (
              <Select
                label={t("Date type")}
                {...field}
                error={Boolean(formState.errors.dateType)}
              >
                {datetypeOptions.map(option => (
                  <MenuItem key={option.value} value={option.value}>
                    {t(option.label)}
                  </MenuItem>
                ))}
                {/* <MenuItem value="monthly">{t("Monthly from")}</MenuItem>
                <MenuItem value="weekly">{t("Weekly from")}</MenuItem>
                <MenuItem value="fixed">{t("Fixed")}</MenuItem> */}
              </Select>
            )}
            rules={{
              required: t("Required"),
            }}
          />
          <FormHelperText>
            {"The type of date to use for each payment entry."}
          </FormHelperText>
        </FormControl>
        <FormControl fullWidth error={Boolean(formState.errors.dateReference)}>
          <Controller
            control={control}
            name="dateReference"
            render={({ field }) => (
              <DatePicker
                label={t("Date reference")}
                format={OUTPUT_DATE_FORMAT}
                {...field}
              />
            )}
            rules={{
              required: t("Required"),
            }}
          />
          <FormHelperText>
            {
              "The reference date for calculating the date for each payment entry."
            }
          </FormHelperText>
        </FormControl>
        <TextField
          label={t("Payment description")}
          fullWidth
          multiline
          rows={2}
          error={Boolean(formState.errors.description)}
          {...register("description", {
            required: t("Required"),
          })}
          helperText={
            formState.errors.description?.message ||
            t(
              'The description to be added to each payment entry. You can use {date} to reference the generated date and {index} to reference the payment entry index. For example, "Payment {index} ({date})" will create entries with "Payment 1 (YYYY/MM/DD)", "Payment 2 (YYYY/MM/DD)"..., and so on.',
            )
          }
        />
        <Accordion variant="outlined" sx={{ overflowY: "scroll" }}>
          <AccordionSummary expandIcon={<ExpandMore />}>
            <Typography variant="subtitle2">
              {t("Milestone payments preview")}
            </Typography>
          </AccordionSummary>
          <AccordionDetails>
            {newPayments.length > 0 ? (
              <PaymentSummary paymentInputs={newPayments} />
            ) : (
              <Alert severity="warning">
                {t(
                  "Please input the amount to divde, the number of payments, and the description.",
                )}
              </Alert>
            )}
          </AccordionDetails>
        </Accordion>
        <Box display="flex" justifyContent="flex-end">
          <Button
            variant="contained"
            startIcon={<Check />}
            onClick={saveHandler}
          >
            {t("Save")}
          </Button>
        </Box>
      </Paper>
    </Modal>
  );
};

const PaymentSummary: React.FC<{ paymentInputs: PaymentInputs[] }> = ({
  paymentInputs,
}) => {
  const { t } = useTranslation();

  return (
    <Box
      sx={{
        display: "flex",
        flexDirection: "column",
        gap: 2,
      }}
    >
      {paymentInputs.map(({ id, date, description, amount }, index) => {
        return (
          <Box key={`${index}-${id}`}>
            <Box marginBlockEnd={1}>
              <Typography variant="body2" component="div" fontWeight="bold">
                {t("Description")}
              </Typography>
              <Typography component="p">{description}</Typography>
            </Box>
            <Box display="flex" gap={2} justifyContent="space-between">
              <Box>
                <Typography variant="body2" component="div" fontWeight="bold">
                  {t("Date")}
                </Typography>
                <Typography component="p">{date}</Typography>
              </Box>
              <Box>
                <Typography variant="body2" component="div" fontWeight="bold">
                  {t("Amount")}
                </Typography>
                <Typography component="p">
                  {moneyFormatter.format(amount)}
                </Typography>
              </Box>
            </Box>
            {index !== paymentInputs.length - 1 && (
              <Divider sx={{ marginBlockStart: 2 }} />
            )}
          </Box>
        );
      })}
    </Box>
  );
};

const replaceTemplateString = (
  template: string,
  replacements: { [key: string]: string },
): string => {
  let returnString = template;
  Object.entries(replacements).forEach(([key, replacement]) => {
    returnString = returnString.replaceAll(key, replacement);
  });

  return returnString;
};

export default BulkAddPaymentModal;
