import {MultiSelect, MultiSelectProps} from "@mantine/core";
import {useEffect, useMemo, useState} from "react";
import {groupBy} from "lodash";

interface ObjectWithIdAndName {
    id: number,
    name: string
}

export type MultiselectObjectType<T> = T;

type ControlledValueProps<T> = {
    value: MultiselectObjectType<T>[],
    onChange: (value: MultiselectObjectType<T>[]) => void;
}

type CustomLabelAndKeyProps<T> = {
    getLabelFn: (item: MultiselectObjectType<T>) => string;
    getValueFn: (item: MultiselectObjectType<T>) => string;
}
type RestProps<T> = {
    defaultValue?: MultiselectObjectType<T>[]
    onChange: (value: MultiselectObjectType<T>[]) => void;
    data: MultiselectObjectType<T>[];
    groupBy?: (item: MultiselectObjectType<T>) => string;
    filterBy?: (item: MultiselectObjectType<T>) => boolean;
}
const defaultGetLabelFn = <T extends { name: string }>(item: T): string => {
    return item.name;
}

const defaultGetValueFn = <T extends { id: number }>(item: T): string => {
    return item.id.toString();
}
export type ObjectMultiSelectProps<T> =
    (RestProps<T> & Partial<ControlledValueProps<T>>)
    & Omit<MultiSelectProps, 'value' | 'onChange' | 'data'>
    & (T extends ObjectWithIdAndName ? Partial<CustomLabelAndKeyProps<T>> : CustomLabelAndKeyProps<T>);

const ObjectMultiSelect = <T, >(props: ObjectMultiSelectProps<T>) => {

    const isControlled = 'value' in props;
    const haveGetValueFn = 'getValueFn' in props;
    const haveGetLabelFn = 'getLabelFn' in props;

    const [internalValue, setInternalValue] = useState<MultiselectObjectType<T>[] | undefined>(props.defaultValue);

    const value = (isControlled ? props.value : internalValue) || [];

    const getValueFn = (haveGetValueFn && props.getValueFn) || defaultGetValueFn;
    const getLabelFn = (haveGetLabelFn && props.getLabelFn) || defaultGetLabelFn;

    const data = useMemo(() => {
        const propsData = props.filterBy ? props.data.filter(props.filterBy) : props.data;
        if (props.groupBy) {

            const groupedData = groupBy(propsData, props.groupBy);
            return Object.entries(groupedData).map(([group, items]) => ({
                group,
                items: items.map(item => ({
                    label: getLabelFn(item),
                    value: getValueFn(item)
                }))
            }));
        } else {
            return propsData.map(item => ({
                label: getLabelFn(item),
                value: getValueFn(item)
            }));
        }
    }, [props.data, props.groupBy]);

    const handleOnChange = (event: string[]) => {
        const objects = props.data.filter(item => event.includes(getValueFn(item)));
        if (isControlled) {
            props.onChange(objects);
            return;
        }
        setInternalValue(objects);
    }
    useEffect(() => {
        if (isControlled || !internalValue) return;
        props.onChange(internalValue);
    }, [internalValue]);

    return (
        <MultiSelect
            {...props}
            value={value.map(item => getValueFn(item))}
            onChange={handleOnChange}
            data={data}
        />
    )
}

export default ObjectMultiSelect;