import React, { Component } from 'react';
import { Select, Spin, Empty } from 'antd'
import { SelectProps } from 'antd/lib/select/index'
import * as Tools from 'jad-tool'
import { throttle, loading, funToPromise_callbackType } from 'jad-tool'

type resultProps = {
    code: number,
    data: any[],
    totalSize: number,
    pageSize: number,
    currentPage: number
}

interface ScrollSelectProps {
    loading?: boolean,
    fetchParams?: Object, // 自定义的请求参数
    paramsPage?: string, // 默认为page
    paramsPageSize?: string, // 默认为size
    paramsSearch?: string, // 搜索关键字 默认为search
    onFetchOptions: Function,
    onGetSearchFilterValue: (value: any) => string | number, // 配合内部searchFilterMap过滤用
    fetchOptionsApi: (params: any) => Promise<{}>, // 接口
    noInitFetch?: boolean, // constructor 不触发 fetchOptions；选填，默认为 false
}

export class ScrollSelectV2 extends Component<ScrollSelectProps & SelectProps>{
    private setAsyncState = funToPromise_callbackType(this.setState.bind(this))
    constructor(props) {
        super(props);
        // 无论是页面新建还是页面编辑状态下调用该ScrollSelect组件，先发起一次只带分页信息的请求
        // 后续页面编辑状态下再调用activeSearchOptions方法，恢复已选择的选项，isNoReset
        if(!props.noInitFetch)
            this.fetchOptions(false, false)
    }
    searchFilterMap = [];
    state = {
        spinning: false,
        serachValue: "",
        pagination: {
            current: 1,
            total: 0,
        },
        options: []
    };
    @loading('spinning')
    async fetchOptions(isScroll: any, serachParams?: any) {
        const { fetchOptionsApi } = this.props;
        const { total } = this.state.pagination
        if (total < 0) return Promise.reject()
        const params = this.createFetchParams(isScroll, serachParams)
        return await fetchOptionsApi(params).then((res: resultProps) => {
            if (res.code !== 2000) return res
            this.handleFetchResult(res, isScroll, serachParams)
            return res
        })
    }
    createFetchParams(isScroll: any, serachParams?: any) {
        const { fetchParams, paramsPage = 'page', paramsPageSize = 'size', paramsSearch = 'search' } = this.props;
        const { pagination, serachValue } = this.state
        const { current } = pagination
        let params: any = {
            [paramsPage]: serachParams ? 1 : current,
            [paramsPageSize]: 10
        }
        //select的输入查询
        if (serachValue) {
			params[paramsSearch] = Tools.isString(serachValue) ? serachValue.replace(/\s\n\t\r/g, '') : serachValue;
        }
        //搜索参数
        if (serachParams && Tools.isPlainObject(serachParams)) {
            Tools.map(serachParams, (item, key) => {
                params[key] = item
            })
        }
        //搜索参数可能是字符串
        if (serachParams && (Tools.isString(serachParams) || Tools.isNumber(serachParams))) {
            params[paramsSearch] = Tools.isString(serachParams) ? serachParams.replace(/\s\n\t\r/g, '') : serachParams
        }
        // add other params
        if (Tools.isObject(fetchParams)) {
            Tools.map(fetchParams, (value, key) => {
                params[key] = fetchParams[key]
            })
        }
        //isScroll
        if (isScroll && !serachParams) {
            params[paramsPage] += 1;
        }
        if(serachValue !== params[paramsSearch]) { // 如果 Select 输入查询条件与实际查询条件不一致，则更新输入查询条件展示
            this.setState({serachValue: params[paramsSearch]})
        }
        return params
    }
    filterSearchOptions(newOptions: any) {
        const { onGetSearchFilterValue } = this.props
        if (Tools.isEmptyArray(this.searchFilterMap)) return newOptions
        return newOptions.filter(item => {
            return this.searchFilterMap.indexOf(onGetSearchFilterValue(item)) === -1
        })
    }
    handleFetchResult(result: any, isScroll?: any, serachParams?: any) {
        const { data, totalSize, currentPage, pageSize } = result
        const { total } = this.state.pagination
        const { onFetchOptions, onGetSearchFilterValue } = this.props
        let { options } = this.state
        if (serachParams) { // 这表示页面编辑状态下再调用activeSearchOptions方法传的参数
            this.searchFilterMap = data.map(e => onGetSearchFilterValue(e))
        }
        const newOptions = serachParams
            ? data.concat(this.filterSearchOptions(options)) //页面编辑状态下，恢复已选择的选项+之前第一次请求获取的options
            : (isScroll ? options.concat(this.filterSearchOptions(data)) : data) // 滚动场景需要拼接下拉数据
        this.setState({
            newOptions,
            pagination: {
                current: currentPage,
                total: totalSize === total ? -1 : (pageSize * currentPage > totalSize ? totalSize : pageSize * currentPage),
            },
        })
        if (onFetchOptions) { // 传递options到外部，供外部使用
            onFetchOptions(newOptions)
        }
    }
    resetInit() {
        this.searchFilterMap = []
        return this.setAsyncState({
            pagination: {
                current: 1,
                total: 0,
            },
            options: [],
        })
    }
    // 此方法供外部编辑状态下恢复已选择的选项用，isNoReset为true，表示拼接初始化组件时请求获取的选项，false表示不拼接
    activeSearchOptions(serachParams: any, isNoReset?: boolean) {
        if (isNoReset) return this.fetchOptions(false, serachParams);
        return this.resetInit().then(() => this.fetchOptions(false, serachParams))
    }
    // 滚动
    onHandleScroll(el) {
        const { target } = el;
        const { onPopupScroll } = this.props
        let { spinning } = this.state
        // WARN: Math.ceil 是因为 windows 浏览器 带小数点 target.scrollTop + target.offsetHeight，导致无法进入 if 循环
        if (Math.ceil(target.scrollTop + target.offsetHeight) === target.scrollHeight && !spinning) {
            const result = this.fetchOptions.bind(this)(el)
            if (result instanceof Promise || (result && result.then && Tools.isFunction(result.then))) {
                this.setState({
                    spinning: true,
                })
                result.then(() => this.setState({
                    spinning: false,
                }), () => this.setState({
                    spinning: false,
                }))
            }
        }

        if (Tools.isFunction(onPopupScroll)) {
            onPopupScroll(el)
        }
    }
    onHandleSearchOptions() {
        return this.resetInit().then(() => this.fetchOptions(false))
    }
    // 搜索关键字，需要存储搜索关键字、重置分页信息和重置之前存储的选项
    @throttle(300)
    onHandleSearch(value) {
        const { onSearch } = this.props
        this.setState({
            serachValue: value,
        }, this.onHandleSearchOptions)

        if (Tools.isFunction(onSearch)) {
            onSearch(value)
        }
    }
    // 清空全部，需要重置分页信息和重置之前存储的选项
    onHandleChange(value, options) {
        const { onChange } = this.props
        //clear all
        if(!value || (value && Tools.isEmptyArray(value))){
            this.onHandleSearchOptions()
        }
        //notice
        if (Tools.isFunction(onChange)) {
            onChange(value, options)
        }
    }
    // 失去焦点，输入框的关键字会自动被系统清空，这时需要重置搜索关键字，不然再次看到的选项是搜索关键字对应的选项
    onHandleBlur(selected) {
        const { onBlur } = this.props
        const { serachValue } = this.state
        if(serachValue) {
            this.onHandleSearch('')
        }
            
        if (Tools.isFunction(onBlur)) {
            onBlur(selected)
        }
    }
    render() {
        const { spinning } = this.state
        return (
            <Select  {...this.props}
                loading={this.props.loading || spinning}
                notFoundContent={spinning ? <Spin spinning/> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />} 
                onPopupScroll={this.onHandleScroll.bind(this)}
                onSearch={this.onHandleSearch.bind(this)}
                onChange={this.onHandleChange.bind(this)}
                onBlur={this.onHandleBlur.bind(this)}
            >
                {this.props.children}
            </Select>
        )
    }
}
