import {
    forwardRef,
    ForwardedRef,
    useRef,
    useState,
    useMemo,
    useEffect,
} from 'react';
import dayjs, { Dayjs } from 'dayjs';

import { styled } from '../../stitches.config';

import { InputOptions } from '../../utility';
import { useValue } from '../../hooks';

import { BaseCalendar, BaseCalendarProps, BaseCalendarView } from '../internal';

const StyledCalendar = styled(BaseCalendar, {
    '&[data-disabled]': {
        backgroundColor: `$grey-5`,
        borderColor: `$grey-4`,
        color: `$grey-3`,
        cursor: 'default',
        pointerEvents: 'none',
    },
    '&:focus': {
        outline: '1px solid $primary-1',
        borderColor: '$primary-1',
    },
    '&[data-focused]': {
        outline: '1px solid $primary-1',
        borderColor: '$primary-1',
    },
    variants: {
        valid: {
            true: {
                borderColor: `$grey-4`,
                '&:hover': {
                    borderColor: '$primary-1',
                },
            },
            false: {
                borderColor: `$error-1`,
            },
        },
    },
    defaultVariants: {
        valid: true,
    },
});

export interface CalendarProps
    extends InputOptions<string>,
        BaseCalendarProps {}

/**
 * Calendar component
 */
const _Calendar = (
    props: CalendarProps,
    ref: ForwardedRef<HTMLDivElement>,
): JSX.Element => {
    const {
        id,
        value,
        defaultValue,
        onChange = () => null,
        disabled = false,
        valid = true,
        format = 'MM/DD/YYYY',
        onFocus = () => null,
        onBlur = () => null,
        ...otherProps
    } = props;

    // refs
    const calendarRef = useRef<HTMLDivElement | null>(null);

    // manage the internal value
    const [internalValue, setInternalValue] = useValue({
        initialValue: '',
        value: value,
        defaultValue: defaultValue,
        onChange: onChange,
    });

    // state
    const [focused, setFocused] = useState<boolean>(false);
    const [view, setView] = useState<BaseCalendarView>('day');
    const [highlightedDay, setHighlightedDay] = useState<Dayjs>(dayjs());

    // get the selected day
    const selectedDay: Dayjs = useMemo(() => {
        return dayjs(internalValue, format);
    }, [internalValue, format]);

    useEffect(() => {
        if (
            document.hasFocus() &&
            calendarRef.current &&
            calendarRef.current.contains(document.activeElement)
        ) {
            setFocused(true);
        }
    }, []);

    return (
        <StyledCalendar
            ref={(node) => {
                if (typeof ref === 'function') {
                    ref(node);
                } else if (ref) {
                    ref.current = node;
                }

                calendarRef.current = node;
            }}
            id={id}
            valid={valid}
            disabled={disabled}
            format={format}
            view={view}
            selectedDay={selectedDay}
            highlightedDay={highlightedDay}
            onView={(v) => setView(v)}
            onHighlightedDay={(d) => setHighlightedDay(d)}
            onSelectedDay={(d) => {
                // parse the value
                const v = dayjs(d).format(format);

                // propagate it
                setInternalValue(v);
            }}
            onFocus={(event) => {
                onFocus(event);

                // show the focus when it enters this or a child
                if (!event.currentTarget.contains(event.relatedTarget)) {
                    // show the focus
                    setFocused(true);
                }
            }}
            onBlur={(event) => {
                onBlur(event);

                // hide the focus when it leaves this or a child
                if (!event.currentTarget.contains(event.relatedTarget)) {
                    // hide the focus
                    setFocused(false);
                }

                // reset when it leaves the calendar
                if (!calendarRef.current?.contains(event.relatedTarget)) {
                    // update the view
                    setView('day');

                    // update the highlight based on validity
                    if (selectedDay.isValid()) {
                        setHighlightedDay(selectedDay);
                    } else {
                        setHighlightedDay(dayjs());
                    }
                }
            }}
            data-focused={focused || undefined}
            {...otherProps}
        ></StyledCalendar>
    );
};

export const Calendar = forwardRef(_Calendar) as (
    props: CalendarProps & {
        ref?: ForwardedRef<HTMLDivElement>;
    },
) => ReturnType<typeof _Calendar>;
