import React, { useEffect, useMemo, useRef, useState } from 'react';
import {
  Calendar,
  EventProps,
  SlotInfo,
  Views,
  momentLocalizer,
} from 'react-big-calendar';
import moment from 'moment';
import Link from 'next/link';
import 'react-big-calendar/lib/css/react-big-calendar.css';
import BaseButton from './BaseButton';
import ListActionsPopover from './ListActionsPopover';
import LoadingSpinner from './LoadingSpinner';
import CardBoxComponentEmpty from './CardBoxComponentEmpty';
import { useAppSelector } from '../stores/hooks';
import { hasPermission } from '../helpers/userPermissions';

const localizer = momentLocalizer(moment);

type TEvent = {
  id: string;
  title: string;
  start: Date;
  end: Date;
  [key: string]: any;
};

type Props = {
  events: any[];
  handleDeleteAction: (id: string) => void;
  handleCreateEventAction: (slotInfo: SlotInfo) => void;
  onDateRangeChange: (range: { start: string; end: string }) => void;
  entityName: string;
  showField: string;
  pathEdit?: string;
  pathView?: string;
  isLoading?: boolean;
  emptyTitle?: string;
  emptyDescription?: string;
  'start-data-key': string;
  'end-data-key': string;
};

const formatEventWindow = (event: TEvent) => {
  const sameDay = moment(event.start).isSame(event.end, 'day');
  if (sameDay) {
    return `${moment(event.start).format('MMM D')} · ${moment(event.start).format('h:mm A')} - ${moment(event.end).format('h:mm A')}`;
  }

  return `${moment(event.start).format('MMM D')} - ${moment(event.end).format('MMM D')}`;
};

const CalendarToolbar = ({ label, onNavigate }: any) => {
  return (
    <div className='mb-4 flex flex-col gap-3 border-b border-gray-200 pb-4 md:flex-row md:items-center md:justify-between dark:border-dark-700'>
      <div>
        <p className='text-xs font-semibold uppercase tracking-[0.18em] text-gray-400 dark:text-gray-500'>
          Schedule
        </p>
        <p className='mt-1 text-lg font-semibold text-gray-900 dark:text-white'>{label}</p>
      </div>
      <div className='flex items-center gap-2'>
        <BaseButton small outline color='info' label='Prev' onClick={() => onNavigate('PREV')} />
        <BaseButton small outline color='info' label='Today' onClick={() => onNavigate('TODAY')} />
        <BaseButton small outline color='info' label='Next' onClick={() => onNavigate('NEXT')} />
      </div>
    </div>
  );
};

const BigCalendar = ({
  events,
  handleDeleteAction,
  handleCreateEventAction,
  onDateRangeChange,
  entityName,
  showField,
  pathEdit,
  pathView,
  isLoading = false,
  emptyTitle = 'Nothing is scheduled in this window',
  emptyDescription = 'Adjust the date range or create a new item to start filling this calendar.',
  'start-data-key': startDataKey,
  'end-data-key': endDataKey,
}: Props) => {
  const [myEvents, setMyEvents] = useState<TEvent[]>([]);
  const prevRange = useRef<{ start: string; end: string } | null>(null);

  const currentUser = useAppSelector((state) => state.auth.currentUser);
  const hasUpdatePermission =
    currentUser && hasPermission(currentUser, `UPDATE_${entityName.toUpperCase()}`);
  const hasCreatePermission =
    currentUser && hasPermission(currentUser, `CREATE_${entityName.toUpperCase()}`);

  const { defaultDate, scrollToTime } = useMemo(
    () => ({
      defaultDate: new Date(),
      scrollToTime: new Date(1970, 1, 1, 8),
    }),
    [],
  );

  useEffect(() => {
    if (!Array.isArray(events) || !events.length) {
      setMyEvents([]);
      return;
    }

    const formattedEvents = events.map((event) => ({
      ...event,
      start: new Date(event[startDataKey]),
      end: new Date(event[endDataKey]),
      title: event[showField],
    }));

    setMyEvents(formattedEvents);
  }, [endDataKey, events, showField, startDataKey]);

  const onRangeChange = (range: Date[] | { start: Date; end: Date }) => {
    const newRange = { start: '', end: '' };
    const format = 'YYYY-MM-DDTHH:mm';

    if (Array.isArray(range)) {
      newRange.start = moment(range[0]).format(format);
      newRange.end = moment(range[range.length - 1]).format(format);
    } else {
      newRange.start = moment(range.start).format(format);
      newRange.end = moment(range.end).format(format);
    }

    if (newRange.start === newRange.end) {
      newRange.end = moment(newRange.end).add(1, 'days').format(format);
    }

    if (
      prevRange.current &&
      prevRange.current.start <= newRange.start &&
      prevRange.current.end >= newRange.end
    ) {
      return;
    }

    prevRange.current = { start: newRange.start, end: newRange.end };
    onDateRangeChange(newRange);
  };

  return (
    <div className='rounded-2xl border border-gray-200/80 bg-white p-4 shadow-sm dark:border-dark-700 dark:bg-dark-800/80'>
      {isLoading ? (
        <LoadingSpinner
          compact
          label='Loading calendar view'
          detail='Syncing the current date range and live reservations.'
        />
      ) : null}

      {!isLoading && myEvents.length === 0 ? (
        <CardBoxComponentEmpty
          compact
          title={emptyTitle}
          description={emptyDescription}
        />
      ) : null}

      <div className={isLoading ? 'pointer-events-none opacity-40' : ''}>
        <Calendar
          className='min-h-[680px]'
          defaultDate={defaultDate}
          defaultView={Views.MONTH}
          events={myEvents}
          localizer={localizer}
          selectable={hasCreatePermission}
          onSelectSlot={handleCreateEventAction}
          onRangeChange={onRangeChange}
          scrollToTime={scrollToTime}
          dayPropGetter={(date) => ({
            style: {
              backgroundColor: [0, 6].includes(date.getDay()) ? 'rgba(148, 163, 184, 0.06)' : 'transparent',
            },
          })}
          eventPropGetter={() => ({
            style: {
              backgroundColor: '#eff6ff',
              border: '1px solid #bfdbfe',
              borderRadius: '14px',
              color: '#1e3a8a',
              padding: '2px 4px',
              boxShadow: 'none',
            },
          })}
          components={{
            toolbar: (toolbarProps: any) => <CalendarToolbar {...toolbarProps} />,
            event: (props) => (
              <MyCustomEvent
                {...props}
                onDelete={handleDeleteAction}
                hasUpdatePermission={!!hasUpdatePermission}
                pathEdit={pathEdit}
                pathView={pathView}
              />
            ),
          }}
        />
      </div>
    </div>
  );
};

const MyCustomEvent = (
  props: {
    onDelete: (id: string) => void;
    hasUpdatePermission: boolean;
    pathEdit?: string;
    pathView?: string;
  } & EventProps<TEvent>,
) => {
  const { onDelete, hasUpdatePermission, title, event, pathEdit, pathView } = props;

  return (
    <div className='flex items-start justify-between gap-2 overflow-hidden rounded-xl'>
      <div className='min-w-0'>
        <Link
          href={`${pathView}${event.id}`}
          className='block truncate text-xs font-semibold text-blue-900'
        >
          {title}
        </Link>
        <p className='truncate text-[11px] text-blue-700/80'>{formatEventWindow(event)}</p>
      </div>
      <ListActionsPopover
        className='h-5 w-5 text-blue-700'
        iconClassName='w-4 text-blue-700'
        itemId={event.id}
        onDelete={onDelete}
        pathEdit={`${pathEdit}${event.id}`}
        pathView={`${pathView}${event.id}`}
        hasUpdatePermission={hasUpdatePermission}
      />
    </div>
  );
};

export default BigCalendar;
