import React, {FC, useCallback, useEffect, useLayoutEffect, useRef} from "react";
import {MultiSelect} from "../fields/MultiSelect";
import {pad, range} from "lodash";
import {__, CouponsPlus} from "../../globals";
import {MultiSelectSearch} from "../fields/MultiSelectSearch";
import {TimeField} from "../fields/TimeField";
import {EmulatedButton} from "../EmulatedButton";
import {useOpenSelectMenuPopupWindow} from "../useOpenSelectMenuPopupWindow";
import {Tabs} from "../fields/tabs";
import {CustomAttributeOption} from "./AttributeFields";
import {SingleFieldRenderer} from "../../Fields";
import classNames from "classnames";

export type TimeSlot = {
    days: number[]; // [0 is sunday, 1 is monday, ..., 6 is saturday]
    time: {
        type: 'hours' | 'range';
        value: string[] | [string, string]; // e.g., ['00:00', '10:00'] for range or [0, 10, 18] for hours
        // this isn't imported or exported and is for use on the ui only because switching types expect a different value format, so we need to keep both values in the state to avoid losing or corrupting data when switching between types.
        __local: {
            value_hours: string[];
            value_range: [string, string];
            open_days_popup_on_mount?: boolean;
        }
    };
}

export type TimeSlotProps = {
    timeSlot: TimeSlot,
    onAddDay: (day: number) => void,
    onRemoveDay: (day: number) => void,
    onTimeTypeChange: (type: 'hours' | 'range') => void,
    onTimeValueChange: (value: [string, string] | string[]) => void,
    onDaysChange?: (days: number[]) => void,
    autoOpenDaysPopupOnMount?: boolean,
    onAutoOpenDaysPopupHandled?: () => void,
}

export function getCarbonWeekdayLabel(day: number): string {
    const sundayFirstDaysList = CouponsPlus?.time?.daysListSundayFirst;
    if (Array.isArray(sundayFirstDaysList) && sundayFirstDaysList.length === 7) {
        return sundayFirstDaysList[day] ?? '';
    }

    return CouponsPlus?.time?.daysList?.[(day + 6) % 7] ?? '';
}

function getDayOptions(days: number[] = range(7)): { id: string, label: string; value: string }[] {
    return days.map(day => ({
        id: day.toString(),
        label: getCarbonWeekdayLabel(day),
        value: day.toString()
    }))
}

function getTimeTypeOptions(): { id: string, label: string; value: string }[] {
    return [
        {id: 'range', label: 'Range', value: 'range'},
        {id: 'hours', label: 'Hours', value: 'hours'},
    ]
}

function timeForRange(value: undefined | any[] | [string, string]): [string, string] {
    if (!Array.isArray(value) || value.length < 2) {
        return ['00:00', '23:59']; // Default to full day if no value is provided
    }

    return value as [string, string]
}

export function getHour12Format(hour: number): { hour: number; label: string } {
    const hour12 = (hour % 12) || 12;
    const label = hour < 12 ? 'AM' : 'PM';
    return { hour: hour12, label };
}

function getHourOptions(): { id: string, label: string; value: string }[] {
    return range(24).map(hour => {
        const { hour: hour12, label } = getHour12Format(hour);
        return {
            id: hour.toString(),
            // show am or pm label in 12-hour format
            label: `${hour12} ${label}`,
            value: hour.toString()
        }
    });
}

export const TimeSlotField: FC<TimeSlotProps> = ({
                                                     timeSlot: {days, time},
                                                     onAddDay,
                                                     onRemoveDay,
                                                     onTimeTypeChange,
                                                     onTimeValueChange,
                                                     onDaysChange,
                                                     autoOpenDaysPopupOnMount,
                                                     onAutoOpenDaysPopupHandled
                                                 }) => {
    const openSelectMenu = useOpenSelectMenuPopupWindow()
    const shouldAttemptAutoOpenOnMount = useRef<boolean>(true)

    const openDaysPopup = useCallback(() => {
        openSelectMenu({
            context: {
                id: 'time-slot-days',
                data: {
                    options: getDayOptions(),
                    selectedValues: days.map(String),
                    window: {
                        title: __('Select the days that this time slot will be active for'),
                        description: __('You can select multiple days, and combine this with other time slots to create more specific schedules. For example, you can create one time slot for weekdays, and another for weekends, and set different time ranges for each.')
                    }
                }
            },
            onClose: ({status, values}) => {
                if (status === 'success') {
                    const selectedDays = values.map(value => parseInt(value));
                    onDaysChange?.(selectedDays);
                }
            }
        })
    }, [days, onDaysChange, openSelectMenu])

    useLayoutEffect(() => {
        if (!shouldAttemptAutoOpenOnMount.current) {
            return
        }

        shouldAttemptAutoOpenOnMount.current = false
        if (!autoOpenDaysPopupOnMount) {
            return
        }

        openDaysPopup()
        onAutoOpenDaysPopupHandled?.()
    }, [autoOpenDaysPopupOnMount, onAutoOpenDaysPopupHandled, openDaysPopup])

    const openHoursPopup = () => {
        openSelectMenu({
            context: {
                id: 'time-slot-hours',
                data: {
                    options: getHourOptions(),
                    selectedValues: time.value.map(String),
                    window: {
                        title: __('Select the hours that this time slot will be active for'),
                        description: __('This is useful for creating gaps in hours and discontinuous ranges. Each hour represents a full hour, e.g., 2 PM means from 2:00 until 2:59.')
                    }
                }
            },
            onClose: ({status, values}) => {
                if (status === 'success') {
                    const selectedHours = values.map(value => parseInt(value));
                    onTimeValueChange?.(selectedHours);
                }
            }
        })
    }
    return <div className="grid grid-cols-3 items-stretch --gap-4 w-full">
        <EmulatedButton className={'col-span-1 flex flex-col items-start text-left gap-y-2 --rounded-1 rounded-l-1 bg-gray-150 bg-opacity-40 hover:bg-gray-150 p-4'} onClick={openDaysPopup}>
            <h2 className={'text-smaller-1 uppercase text-gray-450'}>{__('Days')}</h2>
            {!(!!days.length) &&
                <div className={'text-base text-gray-400'}>{__('Select available days for this slot')}</div>}
            {!!days.length && <ul className="flex flex-col gap-[2px] text-base text-gray-650">
                {days.sort((day, nextDay) => day - nextDay ).map(day => <li className="mb-0">
                    {/* lets render the days by name from 0 index */}
                    {getCarbonWeekdayLabel(day) || 'day not found'}
                </li>)}
            </ul>}
        </EmulatedButton>
        <div className="col-span-2 flex flex-col p-3">
            {/*here the tabs for hours or range*/}
            <Tabs size={'extra-small'}
                  direction={'horizontal'}
                  tabs={getTimeTypeOptions()}
                  width={'full'}
                  dimUnselected
                  onTabSelect={(id) => {
                        onTimeTypeChange(id as 'hours' | 'range')
                  }}
                  selectedTabId={time.type}
                  connectedToThe={'bottom'}
            />
            {/*here the state/clickable button that changes hours or ranges.*/}
            <div className="border-px --border-t-0 border-gray-150 rounded-1 rounded-t-0 px-4 py-2">
                <div className={classNames({
                    "grid grid-cols-2 items-center gap-2": time.type === 'range',
                    'flex flex-col items-stretch': time.type === 'hours'
                })} style={{gridTemplateColumns: 'max-content minmax(0, 1fr)'}}>
                    {time.type === 'range' && ([0, 1].map(index => <>
                            <p className="text-smaller-1 text-gray-450 justify-self-end">{index === 0? __('From') : __('To')}</p>
                            <TimeField source={timeForRange(time.value)[index]}
                                       onChange={(newTime) => {
                                           const otherTime = timeForRange(time.value)[index === 0 ? 1 : 0];

                                           const firstTime = index === 0 ? newTime : otherTime;
                                           const secondTime = index === 0 ? otherTime : newTime;

                                           onTimeValueChange([
                                               // DO NOT CHANGE THE ORDER OF THESE VALUES
                                               firstTime,
                                               secondTime
                                           ])
                                       }}
                                       narrow={true}
                            />
                        </>)
                    )}
                    {time.type === 'hours' && <EmulatedButton onClick={openHoursPopup} className={' rounded-1 p-4 hover:bg-gray-150 bg-opacity-60 active:scale-[.98] transition-all'}>
                        {time.value.length === 0 || time.value.filter(value => typeof value !== 'number').length ? <div className="flex items-center text-gray-550 gap-2">
                            <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="min-w-4 min-h-4 max-w-4 max-h-4">
                                <path strokeLinecap="round" strokeLinejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
                            </svg>
                            <span>{__('Select hours for this slot')}</span>
                        </div> : <ul className="flex flex-col gap-[2px] text-base text-gray-650">
                            {time.value.sort((a, b) => (typeof a === 'number' && typeof b === 'number') ? a - b : 0).map(hour => {
                                if (typeof hour !== 'number') {
                                    return null;
                                }
                                return <li className="mb-0 flex items-center gap-2 text-gray-550" key={hour}>
                                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className="min-w-4 min-h-4 max-w-4 max-h-4 text-gray-450">
                                        <path fillRule="evenodd" d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25ZM12.75 6a.75.75 0 0 0-1.5 0v6c0 .414.336.75.75.75h4.5a.75.75 0 0 0 0-1.5h-3.75V6Z" clipRule="evenodd" />
                                    </svg>
                                    <span className="font-medium">{getHour12Format(hour).hour}</span>
                                    <span className="opacity-70">
                                        {getHour12Format(hour).label}
                                    </span>
                                </li>
                            })}
                        </ul>}
                    </EmulatedButton>}
                </div>
            </div>
        </div>
    </div>

    return <div className={'flex items-start gap-x-4'}>
        <div className={'flex flex-col gap-y-2'}>
            <h2 className={'text-smaller-1 uppercase text-gray-450'}>{__('Days')}</h2>
            <MultiSelect value={days.map(String)}
                         onSelectedValue={({id}) => onAddDay(parseInt(id))}
                         options={getDayOptions()}
                         onRemovedValue={({id}) => onRemoveDay(parseInt(id))}

            />
        </div>
        <div className={'flex flex-col gap-y-2'}>
            <h2 className={'text-smaller-1 uppercase text-gray-450'}>{__('Time')}</h2>
            <MultiSelectSearch options={getTimeTypeOptions()}
                               onSelectedValue={({id}) => onTimeTypeChange(id as 'hours' | 'range')}
                               selectedValue={time.type}
                               style={'gray'}
                               size={'small'}
                               width={'auto'}
            />
            {time.type === 'range' && (
                [0, 1].map(index => <TimeField source={timeForRange(time.value)[index]}
                                               onChange={(newTime) => {
                                                   const otherTime = timeForRange(time.value)[index === 0 ? 1 : 0];

                                                   const firstTime = index === 0 ? newTime : otherTime;
                                                   const secondTime = index === 0 ? otherTime : newTime;

                                                   onTimeValueChange([
                                                       // DO NOT CHANGE THE ORDER OF THESE VALUES
                                                       firstTime,
                                                       secondTime
                                                   ])
                                               }}
                                               narrow={true}
                />)
            )}
            {time.type === 'hours' && <MultiSelect
                options={getHourOptions()}
                value={time.value.map(String)}
                onSelectedValue={({id}) => onTimeValueChange([
                    ...time.value,
                    id
                ])}
                onRemovedValue={({id}) => {
                    onTimeValueChange(time.value.filter(hour => hour !== id))
                }}
            />
            }
        </div>
    </div>;
}
