import { useState, useEffect, Component, Fragment, ComponentType, FC } from 'react';
import { DatePicker, TimePicker } from '@progress/kendo-react-dateinputs';
import { TableFilterCellProps } from '@servicetitan/design-system';
import { NumericTextBox, NumericTextBoxProps } from '@progress/kendo-react-inputs';
import moment from 'moment';

import { renderCustomColumnMenuFilter } from '../column-menu-filters';

// TODO: remove when Kendo fix the propType bug of DatePicker
type ComponentClassWithBrokenPropTypes<P> = ComponentType<P> & { defaultProps?: any };

interface Range<T> {
    min: T;
    max: T;
}

interface BaseControlProps<T> {
    value?: T | null;
    width?: number | string;
    onChange?: (ev: any) => void;
}

type RangeControlProps<T, TProps> = BaseControlProps<T> & TProps;

type RangeControl<T, TProps> = Component<RangeControlProps<T, TProps>> & { value: T | null };

const NoSSR: FC<{ children: JSX.Element }> = ({ children }) => {
    const [isMounted, setIsMounted] = useState(false);

    useEffect(() => {
        setIsMounted(true);
    }, []);

    return isMounted ? children : null;
};

class RangeFilterCellBase<T, TProps = void> extends Component<TableFilterCellProps> {
    private minValueBox: RangeControl<T, TProps> | null = null;
    private maxValueBox: RangeControl<T, TProps> | null = null;

    constructor(
        props: TableFilterCellProps,
        private controlClass: ComponentClassWithBrokenPropTypes<RangeControlProps<T, TProps>>,
        private controlProps: TProps = {} as any
    ) {
        super(props);
    }

    /** override this if additional type support needed */
    inRange(current: T, values: Range<T>) {
        return (
            (values.min === null || values.min === undefined || current >= values.min) &&
            (values.max === null || values.max === undefined || current <= values.max)
        );
    }

    onChange = (ev: any) => {
        if (!this.minValueBox || !this.maxValueBox) {
            return;
        }
        this.props.onChange({
            value: { min: this.minValueBox.value, max: this.maxValueBox.value },
            operator: this.inRange,
            syntheticEvent: ev.syntheticEvent,
        });
    };

    render() {
        const filterValue = this.props.value;
        const Control = this.controlClass;
        return (
            <Fragment>
                <span className="k-widget m-b-1 d-b">From:</span>
                <span>
                    {/* KendoNumericTextBox uses useLayoutEffect which is incompatible with SSR */}
                    <NoSSR>
                        <Control
                            {...this.controlProps}
                            value={filterValue?.min}
                            ref={this.setMinValueRef}
                            onChange={this.onChange}
                        />
                    </NoSSR>
                </span>
                <span className="k-widget m-t-1 m-b-1 d-b">To:</span>
                <span>
                    {/* KendoNumericTextBox uses useLayoutEffect which is incompatible with SSR */}
                    <NoSSR>
                        <Control
                            {...this.controlProps}
                            value={filterValue?.max}
                            ref={this.setMaxValueRef}
                            onChange={this.onChange}
                        />
                    </NoSSR>
                </span>
            </Fragment>
        );
    }

    protected setMinValueRef = (el: RangeControl<T, TProps> | null) => {
        this.minValueBox = el;
    };

    protected setMaxValueRef = (el: RangeControl<T, TProps> | null) => {
        this.maxValueBox = el;
    };
}

class NumericRangeFilterCell extends RangeFilterCellBase<number> {
    constructor(props: TableFilterCellProps) {
        super(props, NumericTextBox);
    }
}

class CurrencyRangeFilterCell extends RangeFilterCellBase<number, NumericTextBoxProps> {
    constructor(props: TableFilterCellProps) {
        super(props, NumericTextBox, {
            format: 'c',
        });
    }
}

class DateRangeFilterCell extends RangeFilterCellBase<Date> {
    constructor(props: TableFilterCellProps) {
        super(props, DatePicker);
    }
}

function getTimeString(date: Date) {
    return `${date.getHours()}:${date.getMinutes()}`;
}

class TimeRangeFilterCell extends RangeFilterCellBase<Date> {
    constructor(props: TableFilterCellProps) {
        super(props, TimePicker);
    }

    inRange(current: Date, values: Range<Date>) {
        return moment(getTimeString(current), 'h:mma').isBetween(
            moment(getTimeString(values.min || current), 'h:mma'),
            moment(getTimeString(values.max || current), 'h:mma'),
            'minutes',
            '[]'
        );
    }
}

export const NumericRangeColumnMenuFilter = renderCustomColumnMenuFilter(NumericRangeFilterCell);
export const CurrencyRangeColumnMenuFilter = renderCustomColumnMenuFilter(CurrencyRangeFilterCell);
export const DateRangeColumnMenuFilter = renderCustomColumnMenuFilter(DateRangeFilterCell);
export const TimeRangeColumnMenuFilter = renderCustomColumnMenuFilter(TimeRangeFilterCell);
