import type { TableFilterCellProps } from '@servicetitan/design-system';
import { Checkbox } from '@servicetitan/design-system';
import { Component, Fragment, FormEvent } from 'react';

import { observer } from 'mobx-react';
import { observable, computed, runInAction, action, makeObservable } from 'mobx';

import { Input, InputChangeEvent } from '@progress/kendo-react-inputs';

import { DataSource, IdType } from '@servicetitan/data-query';
import { TableState } from '../table-state';
import { renderCustomColumnMenuFilter } from './column-menu-filters';

export function operator<T>(item: T, value: T[]) {
    return value.includes(item);
}

export function fieldValuesColumnMenuFilter<T, TId extends IdType = never>(
    tableState: TableState<T, TId>,
    renderItem: (value: T[keyof T]) => string = defaultRenderItem
) {
    interface Data {
        value: T[keyof T];
        text: string;
    }
    @observer
    class FieldValuesFilterCell extends Component<TableFilterCellProps> {
        @observable data: Data[] = [];

        @observable searchQuery = '';

        @computed get processedData() {
            const searchQuery = this.searchQuery.toLowerCase();

            const filteredData = !searchQuery
                ? this.data
                : this.data.filter(v => v.text.toLowerCase().includes(searchQuery));

            return filteredData.slice().sort((a, b) => {
                if (a.text === b.text) {
                    return 0;
                }

                return a.text < b.text ? -1 : 1;
            });
        }

        get field() {
            const field = this.props.field;

            if (field === undefined) {
                throw 'missing field';
            }

            return field as keyof T;
        }

        get value(): T[keyof T][] {
            return this.props.value || [];
        }

        constructor(props: TableFilterCellProps) {
            super(props);
            makeObservable(this);
        }

        @action
        handleSearchQueryChange = (ev: InputChangeEvent) => {
            if (typeof ev.target.value === 'string') {
                this.searchQuery = ev.target.value;
            }
        };

        componentDidMount() {
            this.fetchData(tableState.dataSource);
        }

        handleChange = (value: Data, checked: boolean, ev: FormEvent<HTMLInputElement>) => {
            const newFilterValue = checked
                ? this.value.concat(value.value)
                : this.value.filter((v: T[keyof T]) => v !== value.value);

            const hasValue = newFilterValue.length > 0;

            this.props.onChange({
                value: hasValue ? newFilterValue : '',
                operator: hasValue ? operator : '',
                syntheticEvent: ev,
            });
        };

        render() {
            return (
                <Fragment>
                    <Input
                        value={this.searchQuery}
                        onChange={this.handleSearchQueryChange}
                        placeholder="Search"
                        className="m-b-1"
                    />

                    {this.processedData.map(item => (
                        <span key={JSON.stringify(item)} className="k-widget d-b m-b-1">
                            <Checkbox
                                label={item.text}
                                checked={this.value.includes(item.value)}
                                onChange={this.handleChange}
                                value={item}
                            />
                        </span>
                    ))}
                </Fragment>
            );
        }

        private async fetchData(dataSource: DataSource<T, TId> | null) {
            if (!dataSource) {
                return;
            }

            const items = (await dataSource.getData({})).data as T[];

            const values = items.map(i => i[this.field]);

            const distinctValues = values.filter((v, idx) => values.indexOf(v) === idx);

            runInAction(() => {
                this.data = distinctValues.map(value => ({ value, text: renderItem(value) }));
            });
        }
    }

    return renderCustomColumnMenuFilter(FieldValuesFilterCell);
}

function defaultRenderItem(v: any): string {
    if (v == null) {
        return '';
    }

    // suppose v has toString();
    return v.toString();
}
