import {
    useImperativeHandle,
    useLayoutEffect,
    forwardRef,
    useEffect,
    useState,
    type Ref,
    useRef
} from "react";
import {
    TouchableOpacity,
    type ViewStyle,
    View
} from "react-native";
import type {
    IDateSelectorRef,
    CalendarDay
} from "./type";
import type IDateSelectorProps from "./type";
import stylesheet, {
    useStyles
} from "./stylesheet";
import {
    dateSelect
} from "./util";
import {
    NCoreUIKitLocalize,
    NCoreUIKitTheme,
    NCoreUIKitToast
} from "../../core/hooks";
import type {
    Mutable
} from "../../types";
import {
    ChevronRight as ChevronRightIcon,
    ChevronLeft as ChevronLeftIcon
} from "lucide-react-native";
import moment from "moment";
import Text from "../text";

const DateSelector = ({
    multipleSelectMinimumRequiredDayCount,
    isShowTodayIndicator = true,
    localeBasedFirstDayOfWeek,
    dayOfWeekLength = "long",
    multipleSelectDayLimit,
    setIsSheetContentReady,
    viewDate: viewDateProp,
    selectMultipleObject,
    selectObject,
    dateRange,
    maxDate,
    minDate,
    variant,
    date
}: IDateSelectorProps, ref: Ref<IDateSelectorRef>) => {
    const {
        radiuses,
        borders,
        spaces,
        colors
    } = NCoreUIKitTheme.useContext();

    const {
        localize
    } = NCoreUIKitLocalize.useContext();

    const selectDate = dateSelect({
        dateRange,
        variant,
        date
    });

    const [
        viewDate,
        setViewDate
    ] = useState(viewDateProp ? viewDateProp : selectDate);

    const [
        monthDays,
        setMonthDays
    ] = useState<Array<CalendarDay>>([]);

    const allSelectedDays = useRef<Array<Date>>([]);

    const {
        nextPrevToolChevronButton: nextPrevToolChevronButtonDynamicStyle,
        nextPrevToolContainer: nextPrevToolContainerDynamicStyle,
        todayIndicator: todayIndicatorDynamicStyle,
        dayOfWeek: dayOfWeekDynamicStyle,
        day: dayDynamicStyle
    } = useStyles({
        radiuses,
        borders,
        colors,
        spaces
    });

    useEffect(() => {
        const allDays = getDaysOfViewMonth({
            tDate: viewDate
        });

        setMonthDays(allDays);
    }, [
        dateRange,
        viewDate,
        date
    ]);

    useLayoutEffect(() => {
        if(monthDays) setIsSheetContentReady(true);
    }, [
        monthDays
    ]);

    useEffect(() => {
        if(viewDateProp) {
            setViewDate(viewDateProp);
        }
    }, [
        viewDateProp
    ]);

    useImperativeHandle(
        ref,
        () => ({
            changeCurrentMonth: (date: Date) => {
                setViewDate(date);
            }
        }),
        []
    );

    const getDaysOfViewMonth = ({
        tDate
    }: {
        tDate: Date
    }): Array<CalendarDay> => {
        allSelectedDays.current = [];

        const targetDate = moment(new Date(tDate));

        const startOfCalendar = targetDate.clone().startOf("month").startOf("week");
        const endOfCalendar = targetDate.clone().endOf("month").endOf("week");

        const totalDays = endOfCalendar.diff(startOfCalendar, "days") + 1;

        const allDays = Array.from({
            length: totalDays
        }).map((_, index) => {
            const currentDay = startOfCalendar.clone().add(index, "days");

            let isSelected = false;

            if(variant === "single" && date) {
                allSelectedDays.current = [
                    date
                ];

                if(moment(currentDay).isSame(date, "day")) isSelected = true;
            } else if(variant === "range" && dateRange && dateRange.start) {
                if(dateRange.end) {
                    allSelectedDays.current = Array.from({
                        length: moment(dateRange.end).diff(dateRange.start, "days") + 1
                    }, (_, i) =>
                        moment(dateRange.start).clone().add(i, "days").toDate()
                    );

                    if(moment(currentDay).isBetween(dateRange.start, dateRange.end, "day", "[]")) isSelected = true;
                } else if(moment(currentDay).isSame(dateRange.start)) {
                    allSelectedDays.current = [
                        dateRange.start
                    ];

                    isSelected = true;
                }
            }

            let isDisabled = false;

            if(minDate && moment(currentDay).isBefore(minDate)) {
                isDisabled = true;
            }

            if(maxDate && moment(currentDay).isAfter(maxDate)) {
                isDisabled = true;
            }

            return {
                originalIndex: allSelectedDays.current.findIndex((day) => moment(currentDay).isSame(day, "day")),
                isCurrentMonth: currentDay.isSame(targetDate, "month"),
                isToday: currentDay.isSame(moment(), "day"),
                dayOfWeek: currentDay.weekday(),
                dayNumber: currentDay.date(),
                date: currentDay,
                isDisabled,
                isSelected
            };
        });

        return allDays;
    };

    const renderDay = ({
        weekIndex,
        dayIndex,
        dayItem
    }: {
        dayItem: CalendarDay;
        weekIndex: number;
        dayIndex: number;
    }) => {
        const isLastItemSelected = dayItem.isSelected && dayItem.originalIndex === allSelectedDays.current.length - 1;
        const isFirstItemSelected = dayItem.isSelected && dayItem.originalIndex === 0;

        const selectionStyle: Array<Mutable<ViewStyle> | null> = [
            isFirstItemSelected && allSelectedDays.current.length > 1 ? {
                borderBottomRightRadius: 0,
                borderTopRightRadius: 0
            } : null,
            isLastItemSelected && allSelectedDays.current.length > 1 ? {
                borderBottomLeftRadius: 0,
                borderTopLeftRadius: 0
            } : null
        ];

        let dayTitleColor: keyof NCoreUIKit.TextContentColors = "mid";

        if(dayItem.isSelected && !isLastItemSelected && !isFirstItemSelected) {
            dayTitleColor = "emphasized";

            selectionStyle.push({
                borderBottomRightRadius: 0,
                borderBottomLeftRadius: 0,
                borderTopRightRadius: 0,
                borderTopLeftRadius: 0
            });
        } else if(dayItem.isSelected) {
            dayTitleColor = "onPrimary";
        }

        if(!dayItem.isCurrentMonth || dayItem.isDisabled) {
            dayTitleColor = "disabled";
        }

        return <TouchableOpacity
            onPress={() => {
                if(!dayItem.isCurrentMonth) {
                    return;
                }

                if(variant === "single") {
                    selectObject(dayItem.date.toDate());
                } else if(variant === "range") {
                    if(!dateRange || !dateRange.start) {
                        selectMultipleObject({
                            start: new Date(dayItem.date.toDate().setHours(0, 0, 0)),
                            end: undefined
                        });
                        return;
                    }

                    if(dateRange && moment(dateRange.start).isSame(dayItem.date, "day")) {
                        selectMultipleObject({
                            start: undefined,
                            end: undefined
                        });
                        return;
                    }

                    if(dateRange && !dateRange.end) {
                        if(multipleSelectDayLimit && moment(dayItem.date).diff(dateRange.start, "days") >= multipleSelectDayLimit) {
                            NCoreUIKitToast.open({
                                title: localize("maximum-selection-number-of-days-limit-exceeds")
                            });
                            return;
                        }

                        if(multipleSelectMinimumRequiredDayCount && moment(dayItem.date).diff(dateRange.start, "days") + 1 < multipleSelectMinimumRequiredDayCount) {
                            NCoreUIKitToast.open({
                                title: localize("minimum-selection-number-of-days-required-not-provided")
                            });
                            return;
                        }

                        if(moment(dayItem.date).isBefore(dateRange.start)) {
                            selectMultipleObject({
                                start: new Date(dayItem.date.toDate().setHours(0, 0, 0)),
                                end: undefined
                            });
                        } else {
                            selectMultipleObject({
                                end: new Date(dayItem.date.toDate().setHours(23, 59, 59)),
                                start: dateRange?.start
                            });
                        }
                        return;
                    }

                    selectMultipleObject({
                        start: dayItem.date.toDate(),
                        end: undefined
                    });
                }
            }}
            style={[
                stylesheet.day,
                dayDynamicStyle,
                dayItem.isSelected ? {
                    backgroundColor: colors.content.container.primary,
                    borderColor: colors.content.container.primary
                } : null,
                dayItem.isSelected && !isLastItemSelected && !isFirstItemSelected ? {
                    backgroundColor: colors.content.container.emphasized,
                    borderColor: colors.content.container.emphasized
                } : null,
                dayItem.isSelected && (dayItem.isDisabled || !dayItem.isCurrentMonth) ? {
                    opacity: 0.33
                } : null,
                ...selectionStyle
            ]}
            disabled={!dayItem.isCurrentMonth || dayItem.isDisabled}
            key={`day-${weekIndex}-${dayIndex}`}
        >
            <Text
                variant="labelLargeSize"
                color={dayTitleColor}
            >
                {dayItem.dayNumber}
            </Text>
            {
                isShowTodayIndicator && dayItem.isToday ?
                    <View
                        style={[
                            stylesheet.todayIndicator,
                            selectionStyle,
                            todayIndicatorDynamicStyle,
                    dayItem.isSelected ? {
                        borderColor: colors.content.border.subtle
                    } : null
                        ]}
                    />
                :
                    null
            }
        </TouchableOpacity>;
    };

    const renderNextPrevTool = () => {
        return <View
            style={[
                stylesheet.nextPrevToolContainer,
                nextPrevToolContainerDynamicStyle
            ]}
        >
            <TouchableOpacity
                onPress={() => {
                    const prevViewDate = moment(viewDate).clone().subtract(1, "months");
                    setViewDate(prevViewDate.toDate());
                }}
                style={[
                    nextPrevToolChevronButtonDynamicStyle
                ]}
            >
                <ChevronLeftIcon
                    color={colors.content.icon.default}
                    size={26}
                />
            </TouchableOpacity>
            <Text
                variant="labelLargeSize"
                color="high"
            >
                {moment(viewDate).format("MMMM YYYY")}
            </Text>
            <TouchableOpacity
                onPress={() => {
                    const nextViewDate = moment(viewDate).clone().add(1, "months");
                    setViewDate(nextViewDate.toDate());
                }}
                style={[
                    nextPrevToolChevronButtonDynamicStyle
                ]}
            >
                <ChevronRightIcon
                    color={colors.content.icon.default}
                    size={26}
                />
            </TouchableOpacity>
        </View>;
    };

    let weekDays = moment.weekdays(localeBasedFirstDayOfWeek ? true : false);

    if(dayOfWeekLength === "short") {
        weekDays = moment.weekdaysShort(localeBasedFirstDayOfWeek ? true : false);
    } else if(dayOfWeekLength === "very-short") {
        weekDays = moment.weekdaysMin(localeBasedFirstDayOfWeek ? true : false);
    }

    return <View
        style={[
            stylesheet.container
        ]}
    >
        <View>
            {renderNextPrevTool()}
        </View>
        <View
            style={[
                stylesheet.contentContainer
            ]}
        >
            {weekDays.map((weekItem, weekIndex) => {
                const days = monthDays.filter((dayItem) => dayItem.dayOfWeek === weekIndex);

                return <View
                    key={`day-column-${weekIndex}`}
                    style={[
                        stylesheet.columnContainer
                    ]}
                >
                    <Text
                        style={{
                            ...dayOfWeekDynamicStyle
                        }}
                        variant="labelMediumSize"
                        color="low"
                    >
                        {weekItem}
                    </Text>
                    {days.map((dayItem, dayIndex) => {
                        return renderDay({
                            weekIndex,
                            dayIndex,
                            dayItem
                        });
                    })}
                </View>;
            })}
        </View>
    </View>;
};
export default forwardRef(DateSelector);
