import { MouseEvent, memo, useMemo } from 'react';

import { startOfWeek, isSameDay } from 'date-fns';

import { addDaysToDate, isSameDate } from '../../utils/helperUtil';

import { MousePos, YearCalendarData, YearsCalendarConfig } from './YearConfigs';
import styled from '@emotion/styled';

const StyledCalendarBox = styled('div')((props: any) => ({
  display: 'flex',
  padding: '10px',

  minHeight: 120,

  '& .year-box': {
    div: {
      fontWeight: 'bold',
      fontSize: 'x-large',
      transform: `translateY(${props.cellSize * 4}px) rotate(-90deg)`,
    },
  },

  '& .calendars-box': {
    flex: 1,
    display: 'flex',
    minWidth: 0,

    'div.weeks': {
      '.item': {
        minWidth: `${props.cellSize}px !important`,
        minHeight: `${props.cellSize}px !important`,
        border: `1px solid ${props.borderColor}`,
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        fontSize: '12px',
        userSelect: 'none',
        '&.week': {
          fontWeight: 'bold',
          border: `1px solid transparent`,
        },
      },
    },

    'div.calendars': {
      minWidth: 0,
      overflowX: 'auto',
      overflowY: 'hidden',
      paddingBottom: 5,
      'div.calendar-months': {
        flex: 1,
        display: 'flex',
        flexDirection: 'column',
        cursor: 'pointer',
        width: 'fit-content',

        'div.month-names': {
          display: 'grid',
          justifyItems: 'center',
          gridTemplateColumns: 'repeat(12, 1fr)',
          gridTemplateRows: '1fr',

          minHeight: `${props.cellSize}px !important`,

          div: { flex: 1, textAlign: 'center' },
        },

        'div.month-calendars': {
          flex: 1,
          display: 'grid',
          justifyItems: 'center',
          gridTemplateColumns: 'repeat(53, 1fr)',
          gridTemplateRows: 'repeat(7, 1fr)',
          gridAutoFlow: 'column',
          '.item': {
            minWidth: `${props.cellSize}px !important`,
            minHeight: `${props.cellSize}px !important`,
            width: '100%',
            height: '100%',
            border: `1px solid ${props.borderColor}`,
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            fontSize: '12px',
            userSelect: 'none',
            position: 'relative',

            span: {
              position: 'absolute',
              left: '50%',
              top: '50%',
              transform: 'translate(-50%, -50%)',
            },
            '.items': {
              flex: 1,
              display: 'flex',
              flexWrap: 'wrap',
            },
            '&.week': {
              fontWeight: 'bold',
              border: `1px solid transparent`,
              '&:hover': {
                backgroundColor: 'inherit',
                color: 'inherit',
              },
            },
            '&.odd-month': {
              backgroundColor: props.defaultBgColor,
              color: props.textColor,
            },
            '&.even-month': {
              backgroundColor: props.alternateBgColor,
              color: props.textColor,
            },
            '&.today': {
              backgroundColor: props.todayBgColor,
              color: props.todayColor,
            },
            '&:hover': {
              backgroundColor: props.hoverBgColor,
              color: props.hoverColor,
            },
          },
        },
      },
    },
  },
}));

type DayData = {
  date: Date;
  data?: YearCalendarData[];
  otherYear?: boolean;
};

const getDayClassName = (d: DayData) => {
  const cls = [];
  if (d.otherYear) {
    cls.push('week');
  } else {
    if (isSameDate(new Date(), d.date)) {
      cls.push('today');
    } else {
      if (d.date.getMonth() % 2 === 0) {
        cls.push('odd-month');
      } else {
        cls.push('even-month');
      }
    }
  }
  return cls.reduce((p, c) => `${p} ${c}`, 'item');
};

const getDayStyle = (d: YearCalendarData) => {
  return { backgroundColor: d.bgColor, width: '4px', height: '4px' };
};

const WeeksDisplay = () => (
  <div className='weeks'>
    {[' ', 'S', 'M', 'T', 'W', 'T', 'F', 'S'].map((a, i) => (
      <div key={i} className='item week'>
        {a}
      </div>
    ))}
  </div>
);

const MonthsDisplay = () => (
  <div className='month-names'>
    {[
      'Jan',
      'Feb',
      'Mar',
      'Apr',
      'May',
      'Jun',
      'Jul',
      'Aug',
      'Sep',
      'Oct',
      'Nov',
      'Dec',
    ].map((a, i) => (
      <div key={i}>{a}</div>
    ))}
  </div>
);

const CalendarsDisplay = memo(
  ({
    year,
    items,
    onClick,
    onHover,
    config,
  }: {
    year: number;
    items: Array<YearCalendarData>;
    config: YearsCalendarConfig;
    onClick?: (item: YearCalendarData) => void;
    onHover?: (item: YearCalendarData, pos: MousePos) => void;
  }) => {
    const days = useMemo(() => {
      let theDate = startOfWeek(new Date(year, 0, 1));

      const daysArray: Array<DayData> = [];
      const theYear = parseInt(`${year}`);
      while (theDate.getFullYear() <= theYear) {
        const x = theDate;
        const data = items.filter((y) => isSameDay(y.date, x));
        daysArray.push({
          date: theDate,
          otherYear: theDate.getFullYear() !== theYear,
          data,
        });
        theDate = addDaysToDate(theDate, 1);
      }
      return daysArray;
    }, [year, items]);

    return (
      <div className='month-calendars'>
        {days.map((d, i) => (
          <div key={i} className={getDayClassName(d)}>
            <div className='items'>
              {d.data?.map((d1, j) => (
                <div
                  key={j}
                  style={getDayStyle(d1)}
                  onClick={() => onClick?.(d1)}
                  onMouseEnter={(e: MouseEvent<HTMLDivElement>) => {
                    onHover?.(d1, { x: e.clientX, y: e.clientY });
                  }}
                ></div>
              ))}
            </div>
            <span>{d.otherYear ? '' : d.date.getDate()}</span>
          </div>
        ))}
      </div>
    );
  }
);

export function YearBox(props: {
  year: number;
  items: Array<YearCalendarData>;
  config: YearsCalendarConfig;
  onClick?: (item: YearCalendarData) => void;
  onHover?: (item: YearCalendarData, pos: MousePos) => void;
}) {
  const { year, config } = props;

  return (
    <StyledCalendarBox {...config}>
      <div className='year-box'>
        <div>{year}</div>
      </div>
      <div className='calendars-box'>
        <WeeksDisplay />
        <div className='calendars'>
          <div className='calendar-months'>
            <MonthsDisplay />
            <CalendarsDisplay {...props} />
          </div>
        </div>
      </div>
    </StyledCalendarBox>
  );
}
