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
}
type params = {
    page:number,
    size:number,
    search?:string
}
interface ScrollSelectProps {
    loading?:boolean,
    fetchParams?:any,
    paramsPage?:string,
    paramsPageSize?:string,
    paramsSearch?:string,
    disabled?:boolean,
    onFetchOptions?: Function,
    onPopupScroll?: Function,
    onGetSearchFilterValue?:(value:any)=>string|number, // 获取去重关键值函数，一般用于组件外触发 activeSearchOptions 且 serachParams 存在 isNoReset为 true 的场景
    onHandleFetchResult?:(data:any)=>{data:any,totalSize:number,currentPage:number,pageSize:number}
    fetchOptionsApi: (params:any) => Promise<{}>, // 下拉选项的 api；必填
    noInitFetch?: boolean, // constructor 不触发 fetchOptions；选填，默认为 false
} 



export class ScrollSelect extends  React.Component<ScrollSelectProps & SelectProps>{

    private setAsyncState = funToPromise_callbackType(this.setState.bind(this))

    constructor(props) {
        super(props);
        const { fetchOptionsApi, noInitFetch = false } = this.props;
        if(fetchOptionsApi && !noInitFetch){ // 当 fetchOptionsApi 存在且允许初始化自动请求时
            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(!fetchOptionsApi || 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
        var 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
    }
    handleFetchResult(result:any, isScroll?: any, serachParams?:any){
        const { data,totalSize,currentPage,pageSize } = result
        const { total } = this.state.pagination
        const { onFetchOptions,onGetSearchFilterValue } = this.props
        var { options } = this.state
        if(onGetSearchFilterValue && serachParams){
            this.searchFilterMap = data.map(e=>onGetSearchFilterValue(e))
        }
        const newOptions = !serachParams ? (isScroll ? options.concat(this.filterSearchOptions(data)) : data) : // 滚动场景需要拼接下拉数据
            data.concat(onGetSearchFilterValue ? this.filterSearchOptions(options) : []) // 当 serachParams 存在时特殊场景拼接数据，具体应用场景「王凯」
        this.setState({
            options: newOptions,
            pagination:{
                current: currentPage,
                total: totalSize===total? -1 :(pageSize*currentPage>totalSize? totalSize :pageSize*currentPage),
            },
        })
        if(onFetchOptions){
            onFetchOptions(newOptions)
        }
    }  
    resetInit(){
        this.searchFilterMap  = []
        return this.setAsyncState({
            pagination:{
                current: 1,
                total: 0,
            },
            options:[],
        })
    }
    activeSearchOptions(serachParams:any,isNoReset?:boolean){
        if(isNoReset) return this.fetchOptions(false,serachParams);
        return this.resetInit().then(()=>this.fetchOptions(false,serachParams))
    }
    filterSearchOptions(newOptions:any){
        const { onGetSearchFilterValue } = this.props
        if(!onGetSearchFilterValue || Tools.isEmptyArray(this.searchFilterMap)) return newOptions
        return newOptions.filter(item=>{
            return this.searchFilterMap.indexOf(onGetSearchFilterValue(item)) === -1
        })
    }
    


    @throttle(300)
    onHandleSearchOptions(){
       return this.resetInit().then(()=>this.fetchOptions(false))
    }
    onHandleScroll(el){
        const { target } = el;
        let { onPopupScroll=false } = this.props as any;
        let { spinning } = this.state
        onPopupScroll = onPopupScroll || this.fetchOptions.bind(this)
        // WARN: Math.ceil 是因为 windows 浏览器 带小数点 target.scrollTop + target.offsetHeight，导致无法进入 if 循环
        if (Math.ceil(target.scrollTop + target.offsetHeight) === target.scrollHeight && !spinning) {
            const result = onPopupScroll(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,
                }))
            }
        }
    }
    onHandleSearch(value){
        const { onSearch } = this.props
        if(Tools.isFunction(onSearch)){
            return onSearch(value)
        }
        this.setState({
            serachValue: value,
        }, this.onHandleSearchOptions)
    }
    onHandleChange(value,options){
        const { onChange } = this.props
        //clear all
        if(!value || (value && Tools.isEmptyArray(value))){
            this.onHandleSearchOptions()
        }
        //notice
        if(Tools.isFunction(onChange)){
            return 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>
        )
    }


}






