import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
import classNames from 'clsx';
import dayjs from 'dayjs';
import durationPlugin from 'dayjs/plugin/duration';
import timezonePlugin from 'dayjs/plugin/timezone';
import utcPlugin from 'dayjs/plugin/utc';
import { useRouter } from 'next/router';
import { AddRounded, CheckRounded, CloseRounded, DeleteRounded, EditRounded } from '@mui/icons-material';

import { EVENT_STATUS_ARCHIVED, EXTERNAL_ENDPOINT } from '../../constants';

import timezones from '../../helpers/timezones';
import { getMinStartDate, getTimeWithoutS, validateSchedules } from '../../helpers/validation';
import {
  deliverySelector,
  eventStatusSelector,
  isTimezoneDisabledSelector,
  schedulesSelector,
  timezoneSelector,
} from '../../redux/selectors';

import { FormRow, useFormSettings } from '@/modules/@common/Form';
import useIsAllowedToCreateOrEdit from '../../hooks/useIsAllowedToCreateOrEdit';
import Recurring from './Recurring';
import {
  Button,
  DateTimePicker,
  IconButton,
  MenuItem,
  Select,
  Stack,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  TableRow,
  TableScroll,
  TextField,
  Typography,
} from '@/mui/components';

import styles from './EventSchedule.module.scss';

dayjs.extend(durationPlugin);
dayjs.extend(utcPlugin);
dayjs.extend(timezonePlugin);

function EventSchedule(props) {
  const { size } = useFormSettings();
  const router = useRouter();
  const isAllowedToCreateOrEdit = useIsAllowedToCreateOrEdit();
  const timezone = useSelector(timezoneSelector);
  const schedules = useSelector(schedulesSelector);
  const eventStatus = useSelector(eventStatusSelector);
  const isTimezoneDisabled = useSelector(isTimezoneDisabledSelector);
  const delivery = useSelector(deliverySelector);

  const { id: existedEventId } = router.query;
  const isArchived = eventStatus === EVENT_STATUS_ARCHIVED;

  const getEmptySchedule = () => ({
    title: '',
    startTime: getMinStartDate(),
    duration: 1,
    durationIn: 'HOURS',
    endTime: getMinStartDate() + 60 * 60 * 1000,
    isEditDisabled: false,
    isValid: true,
    errors: {},
  });

  const [editingSchedules, setEditingSchedules] = useState([getEmptySchedule()]);
  const [editingSchedule, setEditingSchedule] = useState(getEmptySchedule());
  const [editMode, setEditMode] = useState({ 0: false });
  const [minStartDate, setMinStartDate] = useState(getMinStartDate());

  useEffect(() => {
    setEditMode({
      0: !existedEventId && !schedules.length,
    });
  }, []);

  useEffect(() => {
    if (schedules.length) {
      setEditingSchedules([
        ...schedules
          .map((schedule) => {
            const modifiedSchedule = { ...schedule };
            modifiedSchedule.duration = dayjs
              .duration((schedule?.endTime ?? 0) - (schedule?.startTime ?? 0))
              .as(schedule?.durationIn);

            return { ...modifiedSchedule };
          })
          .sort((a, b) => a.startTime < b.startTime),
      ]);
    }
  }, [schedules]);

  const timezoneChange = (e) => {
    if (schedules.length) {
      const shiftedSchedules = [
        ...schedules.map((schedule$) => {
          const offsetBefore = dayjs.tz(schedule$.startTime, timezone).utcOffset();
          const offsetAfter = dayjs.tz(schedule$.startTime, e.target.value).utcOffset();
          let offsetShift = 0;

          if (offsetBefore === 0) {
            offsetShift += offsetAfter;
          } else {
            offsetShift = offsetAfter - offsetBefore;
          }

          return {
            ...schedule$,
            startTime: schedule$.startTime - offsetShift * 60 * 1000,
            endTime: schedule$.endTime - offsetShift * 60 * 1000,
          };
        }),
      ];

      props.handleOnChange('schedules', [...validateSchedules(shiftedSchedules)]);
    } else {
      const offsetBefore = dayjs.tz(editingSchedules[0].startTime.startTime, timezone).utcOffset();
      const offsetAfter = dayjs.tz(editingSchedules[0].startTime.startTime, e.target.value).utcOffset();
      let offsetShift = 0;

      if (offsetBefore === 0) {
        offsetShift += offsetAfter;
      } else {
        offsetShift = offsetAfter - offsetBefore;
      }

      setEditingSchedules([
        {
          ...editingSchedules[0],
          startTime: editingSchedules[0].startTime - offsetShift * 60 * 1000,
          endTime: editingSchedules[0].endTime - offsetShift * 60 * 1000,
        },
      ]);
    }
    props.handleOnChange('timezone', e.target.value);
  };

  const addSchedule = () => {
    const schedule = {
      ...getEmptySchedule(),
    };

    const schedules$ = [...editingSchedules];

    schedules$.push({
      ...schedule,
    });

    setEditingSchedule({ ...schedule });

    setEditMode({
      [schedules$.length - 1]: true,
    });

    setEditingSchedules([...schedules$]);

    setMinStartDate(getMinStartDate());
  };

  const deleteSchedule = (index) => {
    const schedules$ = [...editingSchedules];
    schedules$.splice(index, 1);

    const validatedSchedules = validateSchedules(schedules$.sort((a, b) => a.startTime - b.startTime));

    props.handleOnChange('schedules', [...validatedSchedules]);
  };

  const editSchedule = (index) => {
    if (schedules[index]) {
      setEditingSchedule({
        ...schedules[index],
      });
    }

    setEditMode({
      [index]: true,
    });
    setMinStartDate(getMinStartDate());
  };

  const cancelEditSchedule = (index) => {
    const schedules$ = [...editingSchedules];
    setEditMode({
      [index]: false,
    });

    if (editingSchedule.startTime !== null) {
      schedules$[index] = { ...editingSchedule };
    } else {
      schedules$.splice(index, 1);
    }
    setEditingSchedules([...schedules$]);
  };

  const endTimeChange = (value, index) => {
    const schedules$ = [...editingSchedules];
    const { startTime } = editingSchedules[index];

    if (startTime) {
      const delta = schedules$[index].durationIn === 'HOURS' ? value * 60 * 60 * 1000 : value * 60 * 1000;
      schedules$[index].endTime = startTime + delta;
    }

    return setEditingSchedules([...schedules$]);
  };

  const durationTypeChange = (durationType, index) => {
    const schedules$ = [...editingSchedules];
    const { duration, startTime } = schedules$[index];
    const resDuration = durationType === 'MINUTES' ? duration * 60 : Math.floor(duration / 60);
    schedules$[index].duration = resDuration;
    schedules$[index].durationIn = durationType;

    if (startTime) {
      const delta = schedules$[index].durationIn === 'HOURS' ? resDuration * 60 * 60 * 1000 : resDuration * 60 * 1000;
      schedules$[index].endTime = startTime + delta;
    }

    return setEditingSchedules([...schedules$]);
  };

  const startTimeChange = (value, index) => {
    const schedules$ = [...editingSchedules];
    const { duration } = schedules$[index];
    schedules$[index].startTime = getTimeWithoutS(value);
    const delta = schedules$[index].durationIn === 'HOURS' ? duration * 60 * 60 * 1000 : duration * 60 * 1000;
    schedules$[index].endTime = getTimeWithoutS(value + delta);

    return setEditingSchedules([...schedules$]);
  };

  const titleChange = (index, value) => {
    const schedules$ = [...editingSchedules];
    schedules$[index].title = value;

    return setEditingSchedules([...schedules$]);
  };

  const saveSchedule = (index) => {
    setEditMode({
      [index]: false,
    });

    const validatedSchedules = validateSchedules(editingSchedules.sort((a, b) => a.startTime - b.startTime));

    props.handleOnChange('schedules', [...validatedSchedules]);
  };

  return (
    <>
      <FormRow label="Time Zone">
        <Select
          size={size}
          disabled={!isAllowedToCreateOrEdit || isArchived || isTimezoneDisabled}
          placeholder="Select Time Zone"
          value={timezone}
          onChange={timezoneChange}
        >
          {timezones.map(({ label, value }) => (
            <MenuItem key={value} value={value}>
              {label}
            </MenuItem>
          ))}
        </Select>
      </FormRow>
      <FormRow
        fullWidth
        label="Schedule"
        tooltip="Add one or more sessions for this live event."
        justifyContent="flex-end"
      >
        <Button
          variant="outlined"
          onClick={addSchedule}
          color="neutral"
          size={size}
          disabled={!isAllowedToCreateOrEdit || editMode[[editingSchedules.length - 1]] || isArchived}
          startIcon={<AddRounded />}
        >
          Add Session
        </Button>
      </FormRow>
      <FormRow fullWidth>
        <TableContainer>
          <TableScroll>
            <Table size="small">
              <TableHead>
                <TableRow>
                  <TableCell align="left">#</TableCell>
                  <TableCell align="left">Title</TableCell>
                  <TableCell align="left">Start</TableCell>
                  <TableCell align="left">Duration</TableCell>
                  <TableCell align="left">End</TableCell>
                  <TableCell align="left" />
                </TableRow>
              </TableHead>
              <TableBody>
                {editingSchedules.map((schedule, index) => {
                  const isViewMode = !editMode[index];
                  const key = index;

                  return (
                    <TableRow key={key}>
                      <TableCell
                        component="th"
                        scope="row"
                        className={classNames(styles.Cell, {
                          [styles.Cell___error]: !schedule.isValid,
                        })}
                      >
                        {index + 1}
                      </TableCell>
                      <TableCell
                        padding="none"
                        className={classNames(styles.Cell, {
                          [styles.Cell___error]: !schedule.isValid && !schedule.errors.title,
                        })}
                      >
                        <TextField
                          placeholder="Enter the title"
                          value={schedule.title}
                          size={size}
                          shape="square"
                          disabled={!isAllowedToCreateOrEdit}
                          InputProps={{
                            readOnly: isViewMode,
                            classes: {
                              root: classNames(styles.InputRoot, {
                                [styles.InputRoot___viewMode]: isViewMode,
                              }),
                              notchedOutline: classNames(styles.NotchedOutline, {
                                [styles.NotchedOutline___viewMode]: isViewMode,
                              }),
                            },
                          }}
                          FormHelperTextProps={{
                            classes: {
                              root: styles.HelperText,
                            },
                          }}
                          className={styles.ScheduleTitle}
                          onChange={(e) => titleChange(index, e.target.value)}
                          error={!schedule.isValid && !!schedule.errors.title}
                          helperText={!schedule.isValid && schedule.errors.title}
                        />
                      </TableCell>
                      <TableCell
                        padding="none"
                        className={classNames(styles.Cell, {
                          [styles.Cell___error]: !schedule.isValid && !schedule.errors.startTime,
                        })}
                      >
                        <div className={styles.DatePicker}>
                          <DateTimePicker
                            readOnly={isViewMode}
                            // todo: deprecated flow, should be replace with controlled value
                            defaultValue={dayjs.tz(schedule.startTime, timezone)}
                            onAccept={(date) => startTimeChange(date.toDate().getTime(), index)}
                            minDate={dayjs.tz(minStartDate, timezone)}
                            format="DD/MM/YYYY hh:mm A"
                            slotProps={{
                              textField: {
                                onBlur: ({ target: { value } }) => {
                                  if (!isViewMode) {
                                    const [day, month, year, hour, minute, period] = value.split(/[\s/:]+/);

                                    // Adjust hour based on AM/PM
                                    let adjustedHour =
                                      period === 'PM' && hour !== '12' ? parseInt(hour, 10) + 12 : hour;
                                    adjustedHour = period === 'AM' && hour === '12' ? '00' : adjustedHour;

                                    const date = new Date(year, month - 1, day, adjustedHour, minute);
                                    const timestamp = date.getTime();

                                    startTimeChange(timestamp, index);
                                  }
                                },
                                size: 'small',
                                shape: 'square',
                                InputProps: {
                                  classes: {
                                    root: classNames(styles.InputRoot, {
                                      [styles.InputRoot___viewMode]: isViewMode,
                                    }),
                                    notchedOutline: classNames(styles.NotchedOutline, {
                                      [styles.NotchedOutline___viewMode]: isViewMode,
                                    }),
                                  },
                                },
                                FormHelperTextProps: {
                                  classes: {
                                    root: styles.HelperText,
                                  },
                                },
                                error: !schedule.isValid && !!schedule.errors.startTime,
                                helperText: !schedule.isValid && schedule.errors.startTime,
                              },
                            }}
                          />
                        </div>
                      </TableCell>
                      <TableCell
                        padding="none"
                        className={classNames(styles.Cell, {
                          [styles.Cell___error]: !schedule.isValid && !schedule.errors.duration,
                        })}
                      >
                        <Stack fullWidth spacing={1} direction="row" alignItems="center">
                          <TextField
                            value={schedule.duration}
                            type="number"
                            size={size}
                            shape="square"
                            textAlign="center"
                            margin="none"
                            className={styles.ScheduleDuration}
                            disabled={!isAllowedToCreateOrEdit}
                            InputProps={{
                              readOnly: isViewMode,
                              classes: {
                                root: classNames(styles.InputRoot, {
                                  [styles.InputRoot___viewMode]: isViewMode,
                                }),
                                notchedOutline: classNames(styles.NotchedOutline, {
                                  [styles.NotchedOutline___viewMode]: isViewMode,
                                }),
                              },
                            }}
                            onChange={(e) => {
                              editingSchedules[index].duration = e.target.value;
                              return setEditingSchedules([...editingSchedules]);
                            }}
                            onBlur={(e) => endTimeChange(e.target.value, index)}
                            error={!schedule.isValid && !!schedule.errors.duration}
                          />
                          <Select
                            size={size}
                            shape="square"
                            value={schedule.durationIn}
                            className={classNames(styles.ScheduleDurationIn, styles.InputRoot, {
                              [styles.InputRoot___viewMode]: isViewMode,
                            })}
                            readOnly={isViewMode}
                            disabled={!isAllowedToCreateOrEdit}
                            onChange={(e) => durationTypeChange(e.target.value, index)}
                          >
                            <MenuItem key="minutes" value="MINUTES">
                              Minutes
                            </MenuItem>
                            <MenuItem key="hours" value="HOURS">
                              Hours
                            </MenuItem>
                          </Select>
                        </Stack>
                        {!schedule.isValid && (
                          <Typography variant="caption" color="error.main" component="div">
                            {schedule.errors.duration}
                          </Typography>
                        )}
                      </TableCell>
                      <TableCell
                        padding="none"
                        className={classNames(styles.Cell, {
                          [styles.Cell___error]: !schedule.isValid && !schedule.errors.endTime,
                        })}
                      >
                        <TextField
                          size={size}
                          value={
                            schedule.endTime ? dayjs.tz(schedule.endTime, timezone).format('DD/MM/YYYY hh:mm A') : ''
                          }
                          className={styles.ScheduleEndDate}
                          shape="square"
                          disabled={!isAllowedToCreateOrEdit}
                          InputProps={{
                            readOnly: true,
                            classes: {
                              root: classNames(styles.InputRoot, {
                                [styles.InputRoot___viewMode]: isViewMode,
                              }),
                              notchedOutline: classNames(styles.NotchedOutline, {
                                [styles.NotchedOutline___viewMode]: isViewMode,
                              }),
                            },
                          }}
                          FormHelperTextProps={{
                            classes: {
                              root: styles.HelperText,
                            },
                          }}
                          error={!schedule.isValid && !!schedule.errors.endTime}
                          helperText={!schedule.isValid && schedule.errors.endTime}
                        />
                      </TableCell>
                      <TableCell
                        className={classNames(styles.Cell, {
                          [styles.Cell___error]: !schedule.isValid,
                        })}
                      >
                        <Stack spacing={1} direction="row" alignItems="center">
                          <IconButton
                            disabled={!isAllowedToCreateOrEdit || schedule?.isEditDisabled || isArchived}
                            size={size}
                            onClick={editMode[index] ? () => saveSchedule(index) : () => editSchedule(index)}
                          >
                            {editMode[index] ? <CheckRounded /> : <EditRounded />}
                          </IconButton>
                          <IconButton
                            size={size}
                            onClick={editMode[index] ? () => cancelEditSchedule(index) : () => deleteSchedule(index)}
                            disabled={
                              !isAllowedToCreateOrEdit ||
                              editingSchedules.length <= 1 ||
                              schedule?.isEditDisabled ||
                              isArchived
                            }
                          >
                            {editMode[index] ? <CloseRounded /> : <DeleteRounded />}
                          </IconButton>
                        </Stack>
                      </TableCell>
                    </TableRow>
                  );
                })}
              </TableBody>
            </Table>
          </TableScroll>
        </TableContainer>
      </FormRow>
      <FormRow label="Recurring event" tooltip="Enter the event's recurring timeframe" />
      <Recurring
        eventStartDate={schedules[0]?.startTime}
        eventEndDate={schedules[schedules.length - 1]?.endTime}
        handleOnChange={props.handleOnChange}
        disabled={
          !isAllowedToCreateOrEdit ||
          !schedules.length ||
          delivery === EXTERNAL_ENDPOINT ||
          schedules[schedules.length - 1]?.endTime <= Date.now()
        }
      />
    </>
  );
}

EventSchedule.propTypes = {
  handleOnChange: PropTypes.func.isRequired,
};

export default EventSchedule;
