import "./Experience1To1Modal.scss";
import * as yup from "yup";
import React, { useRef, useState, useCallback, useMemo } from "react";
import Modal from "components/Modal";
import Button from "antd/lib/button";
import { MediaUpload } from "redux/Common/types";
import CoverUpload from "./components/CoverUpload/CoverUpload";
import { Form, Formik, FormikProps } from "formik";
import { useEffect } from "react";
import isEmpty from "lodash/isEmpty";
import pick from "lodash/pick";
import notification from "utils/notification";
import { useTypedSelector } from "redux/rootReducer";
import { selectExchangeRates } from "redux/User/selector";
import {
  Experience,
  EXPERIENCE_STATUS,
  EXPERIENCE_TYPE,
} from "redux/Experience/types";
import { getPriceWithExchangeRate } from "utils/currency";
import useMiniumPrice from "hooks/useMinimumPrice";
import Experience1To1Form from "./components/Experience1To1Form/Experience1To1Form";
import AvailabilityCalendarForm from "./components/AvailabilityCalendarForm/AvailabilityCalendarForm";
import {
  Experience1to1FormValues,
  formSteps,
  initialExperienceValues,
} from "./components/FormModel/initial-values";
import {
  FieldTimeRange,
  FormTimeRangeValues,
} from "components/Form/FormListTimeRangeField";
import * as Moment from "moment";
import { extendMoment } from "moment-range";
const moment = extendMoment(Moment);
import momentTZ from "moment-timezone";
import {
  availablePeriodsToMap,
  availablePeriodsToUTC,
  DATE_FORTMAT,
  getFirstAvailablePeriods,
  isOverlapsTimeRange,
} from "utils/available-periods";
import { JSONService } from "utils/json-patch";
import { ExperienceItem } from "models/talent/talent-profile-module.model";

interface Experience1to1ModalProps {
  show: boolean;
  isEdit: boolean;
  item?: ExperienceItem;
  onCancel?: () => void;
  toggleModal: (value: boolean) => void;
  onConfirmCallback: (item: ExperienceItem) => void;
}

const Experience1To1Modal: React.FC<Experience1to1ModalProps> = ({
  show,
  isEdit,
  item,
  onCancel,
  toggleModal,
  onConfirmCallback,
}) => {
  const formRef = useRef<FormikProps<Experience1to1FormValues>>(null);
  const exchangeRate = useTypedSelector(selectExchangeRates);

  // we have to state to check edit because the case create new one then go to step 2.
  // In time the state have to switch edit
  const [minimumPrice, minimumPriceFormat] = useMiniumPrice();
  const [photo, setPhoto] = useState<MediaUpload>({});
  const [timezone, setTimezone] = useState<string>(momentTZ.tz.guess());
  const [initialValues, setInitialValues] = useState<
    Experience & FormTimeRangeValues
  >(initialExperienceValues);

  const [activeStep, setActiveStep] = useState(0);

  const [availablePeriods, setAvailablePeriods] = useState<
    Map<string, FieldTimeRange[]>
  >(new Map<string, FieldTimeRange[]>());
  const [bookingDates, setBookingDates] = useState<string[]>([]);

  const step1ValidationSchema = yup.object().shape({
    // Validate form field
    name: yup
      .string()
      .min(10, "Minimum 10 characters.")
      .max(90, "Maximum 90 characters.")
      .required("Enter your experience name"),
    description: yup.string().required("Enter your experience description"),
    duration: yup
      .number()
      .min(15, "The minimum duration is 15 seconds")
      .max(30 * 60, "The maximum duration is 30 minutes"),
    experiencePrice: yup
      .number()
      .typeError("Please enter the experience price")
      .required("Please enter the experience price")
      .test(
        "minimum",
        `The experience price must be at least ${minimumPriceFormat}.`,
        (val: any) => Number(val) >= minimumPrice || Number(val) === 0
      ),
  });

  const step2ValidationSchema = yup.object().shape({
    periods: yup.array().of(
      yup.object().shape({
        startTime: yup
          .date()
          .required("Start time is required")
          .test(
            "duplicate-range",
            "Times overlap with another set of times",
            function () {
              const ranges = formRef.current?.values.periods.map((field: any) =>
                moment.range(field.startTime, field.endTime)
              );
              // @ts-ignore
              ranges?.splice(this.options.index, 1);
              const rangeCheck = moment.range(
                this.parent.startTime,
                this.parent.endTime
              );
              return !isOverlapsTimeRange(ranges, rangeCheck);
            }
          ),
        endTime: yup
          .date()
          .required("End time is required")
          .test(
            "valid-range-time",
            "Choose an end time later than the start time",
            function (value: any) {
              return moment(value).isAfter(moment(this.parent.startTime));
            }
          )
          .test(
            "duplicate-range",
            "Times overlap with another set of times",
            function () {
              const ranges = formRef.current?.values.periods.map((field: any) =>
                moment.range(field.startTime, field.endTime)
              );
              // @ts-ignore
              ranges?.splice(this.options.index, 1);
              const rangeCheck = moment.range(
                this.parent.startTime,
                this.parent.endTime
              );
              return !isOverlapsTimeRange(ranges, rangeCheck);
            }
          ),
      })
    ),
  });

  const currentValidationSchema =
    activeStep === 0 ? step1ValidationSchema : step2ValidationSchema;

  const isBooked = useMemo(() => {
    if (item?.bookings?.length) return true;
    return false;
  }, [item]);

  useEffect(() => {
    if (!show) return;
    if (isEdit && item) {
      if (
        !!exchangeRate &&
        item?.experienceCreationCurrency?.toLowerCase() !==
          exchangeRate?.localCurrency.toLowerCase()
      ) {
        item.experiencePrice = getPriceWithExchangeRate(
          item.gbpExperiencePrice || 0,
          exchangeRate
        );
      }
      const availablePeriodTimezone =
        item.availablePeriodTimezone || momentTZ.tz.guess();

      const availablePeriods = availablePeriodsToMap(
        item.availablePeriods || [],
        availablePeriodTimezone
      );
      setInitialValues({
        ...pick(item, [
          "id",
          "name",
          "description",
          "experiencePrice",
          "duration",
          "order",
          "visible",
        ]),
        periods: [],
        selectedDate: [],
        // fix bug item.availablePeriodTimezone === null
        availablePeriodTimezone: availablePeriodTimezone,
      });
      setPhoto({ url: item.covers?.[0] });
      setActiveStep(0);
      setTimezone(availablePeriodTimezone);
      setAvailablePeriods(availablePeriods);
      setBookingDates(item.bookings?.map((b) => b.date) as string[]);
    } else {
      setInitialValues({
        ...initialExperienceValues,
        periods: [],
        selectedDate: [],
      });
      setPhoto({});
      setActiveStep(0);
      setTimezone(momentTZ.tz.guess());
      setAvailablePeriods(new Map<string, FieldTimeRange[]>());
      setBookingDates([]);
    }
  }, [show, item, isEdit]);

  useEffect(() => {
    const { value } = availablePeriods.values().next();
    const key = availablePeriods.keys().next().value;
    const selectedDate = formRef.current?.values.selectedDate?.[0];
    if (Array.isArray(value) && value.length > 0 && key) {
      if (selectedDate) {
        formRef.current?.setFieldValue(
          "periods",
          availablePeriods.get(selectedDate.format(DATE_FORTMAT))?.length
            ? availablePeriods.get(selectedDate.format(DATE_FORTMAT))
            : []
        );
        formRef.current?.setFieldValue("selectedDate", [selectedDate]);
      } else {
        formRef.current?.setFieldValue("periods", value);
        formRef.current?.setFieldValue("selectedDate", [momentTZ(key)]);
      }
    } else {
      formRef.current?.setFieldValue("periods", []);
      formRef.current?.setFieldValue("selectedDate", []);
    }
  }, [availablePeriods]);

  useEffect(() => {
    formRef.current?.setFieldValue("availablePeriodTimezone", timezone);
  }, [timezone]);

  const uploadPhotoSuccess = (file: MediaUpload) => {
    setPhoto(file);
  };

  const removePhoto = () => {
    setPhoto({});
  };

  const handleSubmit = useCallback(
    async (form: Experience1to1FormValues) => {
      switch (activeStep) {
        case 0:
          if (!photo.url) {
            notification.warn({ message: "Please add the Cover Photo" });
            return;
          }
          setActiveStep((step) => step + 1);
          formRef.current?.setTouched({});
          formRef.current?.setSubmitting(false);

          // set first day has avaibility
          const firstDate = getFirstAvailablePeriods(availablePeriods);
          if (firstDate) {
            formRef.current?.setFieldValue("periods", firstDate.periods);
            formRef.current?.setFieldValue("selectedDate", [
              momentTZ(firstDate.date),
            ]);
          } else {
            formRef.current?.setFieldValue("periods", []);
            formRef.current?.setFieldValue("selectedDate", []);
          }
          break;
        case 1:
          if (!availablePeriods || !availablePeriods.size) {
            notification.error({ message: "Please select a availability day" });
            return;
          }

          const itemUpdate = {
            ...item,
            ...form,
            covers: [photo.url as string],
            experienceCreationCurrency:
              item?.experienceCreationCurrency || exchangeRate?.localCurrency,
            availablePeriods: availablePeriodsToUTC(availablePeriods, timezone),
            availablePeriodTimezone: timezone,
            type: EXPERIENCE_TYPE.ONE_TO_ONE,
            status: EXPERIENCE_STATUS.PUBLISHED,
            visible: item ? item.visible : true,
          };

          const patch = JSONService.getPatch(itemUpdate, item, {
            actions: ["add", "remove", "replace"], // actions
            ignoreFields: ["periods", "selectedDate"], // ignoreFieldss
          });

          onConfirmCallback({
            ...itemUpdate,
            isUpdate: item?.isUpdate ? item.isUpdate : !isEmpty(patch),
          });

          toggleModal(false);
          break;
        default:
          break;
      }
    },
    [
      activeStep,
      isEdit,
      photo,
      formRef,
      availablePeriods,
      timezone,
      initialValues,
      item,
    ]
  );

  const handleGoBack = () => {
    switch (activeStep) {
      case 0:
        onCancel && onCancel();
        break;
      case 1:
        setActiveStep((step) => step - 1);
        break;
      default:
        break;
    }
  };

  const handleCancel = () => {
    toggleModal(false);
  };

  return (
    <React.Fragment>
      <Modal
        width={664}
        closable
        centered
        className="experience-one-to-one-modal"
        title={formSteps[activeStep]}
        visible={show}
        onCancel={handleCancel}
        destroyOnClose
        maskClosable={false}
      >
        <div className="experience-one-to-one__content">
          <Formik
            innerRef={formRef}
            enableReinitialize={isEdit}
            initialValues={initialValues}
            onSubmit={handleSubmit}
            validationSchema={currentValidationSchema}
          >
            {(props) => (
              <React.Fragment>
                <Form {...props} className="experience-one-to-one-form">
                  {activeStep === 0 && (
                    <React.Fragment>
                      <div className="m__b--32">
                        <CoverUpload
                          photo={photo}
                          onUploadSuccess={uploadPhotoSuccess}
                          onRemove={removePhoto}
                        />
                      </div>
                      <Experience1To1Form
                        {...props}
                        exchangeRate={exchangeRate}
                      />
                    </React.Fragment>
                  )}
                  {activeStep === 1 && (
                    <AvailabilityCalendarForm
                      {...props}
                      isBooked={isBooked}
                      timezone={timezone}
                      setTimezone={setTimezone}
                      availablePeriods={availablePeriods}
                      bookingDates={bookingDates}
                      setAvailablePeriods={setAvailablePeriods}
                    />
                  )}
                </Form>
              </React.Fragment>
            )}
          </Formik>
          <div className="experience-one-to-one__actions m__t--16">
            <Button
              className="ant-btn-xl ant-btn-uppercase"
              // if state is editing we will close modal
              onClick={isEdit && activeStep === 0 ? handleCancel : handleGoBack}
            >
              {isEdit && activeStep === 0 ? "Cancel" : "Go Back"}
            </Button>
            <Button
              className="ant-btn-xl ant-btn-uppercase"
              type="primary"
              onClick={() => formRef.current?.handleSubmit()}
            >
              {activeStep === 0 ? "Next" : "Done"}
            </Button>
          </div>
        </div>
      </Modal>
    </React.Fragment>
  );
};

export default Experience1To1Modal;
