import React, { Fragment, useEffect, useState, useImperativeHandle } from "react";
import { createSubscriptionHook } from "@ywfe/hook-subscription";
import { Form, Button, Row, Col } from 'antd';
// import { DownOutlined, UpOutlined } from '@ant-design/icons';
import {YIcon} from '@/icon';
import * as Utils from "./utils";
import {
  YFilterProps,
  FilterContextProps,
  YFilterState,
  ConfigItem,
  OptionsItem,
  BeforeRequestParams
} from "./filter.interface";
import CONFIG_CONST from "./filter.config";
import {
  FilterOperationStyle,
  TabsStyle,
  CustomStyle,
  RowColOverflowStyle
} from "./styles";
import { useWidth } from "./useWidth";
import {
  initState,
  reducer,
} from "./filter.reducer";
import { FilterContext } from './filter.context';
import { YSelect } from '../select';

const YFilter: React.FC<YFilterProps> = React.forwardRef((props, filterRef) => {
  const {
    filterKey = undefined,
    filterFormRef,
    formProps = {},
    config = [],
    operationExtraContent,
    formBottomExtraContent,
    beforeRequest,
    requiredRequestParams,
    onBeforeRequest,
    request,
    onAfterRequest,
    onChange,
    isTrim = true,
    tabs,
    restoreDefaultValues = true,
    okSumbitText = "查询",
    okResetText = "重置",
    visibleReset = true,
    sumbitProps,
    resetProps,
    okSumbit,
    okReset,
    isResetRequest = true,
    isTabsClearParams = true,
    defaultTableParams,
    onReload,
    isLabelStyle = true,
    labelStyle,
    isTabsUnderForm = true,
  } = props;

  // 实例仓库
  const [store] = useState(() => createSubscriptionHook(reducer, initState));

  // 外界自定义参数收集器
  const [customFormData, setCustomFormData] = useState({});

  const [form] = Form.useForm();

  // 收起 & 展开, 如果小于指定个数，则永久展开
  const [isOpen, setOpen] = useState(() => {
    if (props.defaultIsOpen === undefined) {
      return config.length > CONFIG_CONST.SHOW_MAX_SEARCH_NUM;
    } else {
      return props.defaultIsOpen;
    }
  });

  // 所有 config 里面 apiParam 集合
  const [configApiParams] = useState(() => {
    const apiParams: string[] = [];
    config.forEach((configItem: ConfigItem) => {
      configItem &&
        configItem.apiParam &&
        apiParams.push(configItem.apiParam)
    });
    return apiParams;
  })

  // 如果配置tab，则第几个是激活状态
  const [tabActiveIndex, setTabActiveIndex] = useState(tabs && tabs.activeIndex || 0);

  // 校验并且初始化默认值
  const [initDefaultValues] = useState(() => Utils.validateDefaultValues(config));

  useEffect(() => {
    // 校验属性，必须唯一，有重复直接抛出异常
    Utils.validateAttrnames(config, tabs);
    // 校验 placeholder
    Utils.validatePlaceholders(config);
  }, [])

  // 对应的type属性转组件
  const [transformConfig, setTransformConfig] = useState(() => Utils.transformToComponent(config));

  useEffect(() => {
    if (filterKey !== undefined) {
      setTransformConfig(() => Utils.transformToComponent(config))
    }
  }, [filterKey]);

  /**
   * 获取所有 config 配置属性
   * @param index 当有tab切换时，index起作用
   */
  const configValues = (index?: number) => {
    const values = form.getFieldsValue();
    const reg = new RegExp(`^${CONFIG_CONST.EXTERNAL_RENDER_MARK}.*`);
    // 移除所有自定义行为的参数
    for (const key in values) {
      if (Object.prototype.hasOwnProperty.call(values, key)) {
        if (reg.test(key)) delete values[key];
      }
    }

    // 把对应的时间戳转换对应format格式
    Utils.handleDateOrRangePicker(transformConfig, values);
    // 移除表单中的undefined
    Utils.removeUndefined(values);
    if (tabs && tabs.options) {
      if (typeof index === 'undefined' && typeof tabs.options[tabActiveIndex].value !== 'undefined') {
        values[tabs.apiParam] = tabs.options[tabActiveIndex].value;
      } else if (typeof index !== 'undefined' && typeof tabs.options[index].value !== 'undefined') {
        values[tabs.apiParam] = tabs.options[index].value;
      }
    }
    return values || {};
  }

  /**
   * 获取有效的查询结果，返回搜索的处理之后的搜索结果
   * @param index 当有tab切换时，index起作用
   */
  const effectiveValues = (index?: number) => {
    const values = configValues(index);
    // 把外界自定义参数合并到 values 上面
    Object.assign(values, customFormData);
    return values;
  };

  // 多余参数改变值
  const limitKeyChangeValue = (apiParam: string) => {
    return (value: any): void => {
      setCustomFormData({
        ...customFormData,
        [apiParam.substring(CONFIG_CONST.EXTERNAL_RENDER_MARK.length)]: value,
      });
    }
  };

  const unLimitKeyChangeValue = (apiParam: any, value?: any): void => {
    if (typeof apiParam === 'string') {
      setCustomFormData({
        ...customFormData,
        [apiParam]: value,
      });
    }
    if (Object.prototype.toString.call(apiParam) === '[object Object]') {
      setCustomFormData({
        ...customFormData,
        ...apiParam,
      });
    }
  };

  // 仓库 + 派发动作
  const [state, dispatch] = store();

  /**
   * 在每次请求之前是否有必选的参数集合
   */
  const isRequiredRequest = (isFirstRequest: boolean): boolean => {
    let flag = false;
    if (typeof requiredRequestParams !== 'undefined' &&
      Array.isArray(requiredRequestParams) &&
      requiredRequestParams.length !== 0) {
      // 没有包含的 key 值集合
      const noIncludesrequiredParams: string[] = [];
      // 没有被包含的必填配置项
      const noIncludesrequiredArray: BeforeRequestParams[] = [];
      // 请求之前所有的key值
      const keys = Object.keys(effectiveValues());

      requiredRequestParams.forEach((item: string): void => {
        if (!keys.includes(item)) {
          flag = true;
          noIncludesrequiredParams.push(item)
        };
      })

      noIncludesrequiredParams.length > 0 &&
        noIncludesrequiredParams.forEach((apiParam: string): void => {
          const index = configApiParams.indexOf(apiParam);
          noIncludesrequiredArray.push({
            apiParam,
            index,
            configItem: config[index],
          })
        })

      if (typeof beforeRequest === "function") {
        beforeRequest(isFirstRequest, noIncludesrequiredArray);
      }
    } else {
      if (typeof beforeRequest === "function") {
        beforeRequest(isFirstRequest);
      }
    }
    return flag;
  }

  // 如果外界传 request ，则初始化第一次请求
  useEffect(() => {
    const flag = isRequiredRequest(true);
    async function fetchData() {
      if (flag) {
        return;
      }
      await fetchServiceData(true);
    }
    void fetchData();
  }, []);

  /**
   * 是否是首次加载
   * @param isFirstRequest
   * @param isFirstPage 默认是从第一页开始请求
   * @param tabIndex 第三个参数是有tab切换时，setTabActiveIndex bug
   */
  async function fetchServiceData(isFirstRequest = false, isFirstPage = true, tabIndex?: number): Promise<void> {
    const flag = isRequiredRequest(false);
    if (flag) {
      return;
    }

    typeof onBeforeRequest === "function" && onBeforeRequest();

    if (typeof request !== 'function') {
      return;
    }

    if (isFirstRequest) {
      // 先切换自发请求状态
      dispatch({
        type: 'fetch_api_data',
        payload: {
          data: {
            isLoading: true,
            isReset: !state.isReset,
            // 初始化请求，第一次请求，把默认参数初始化到仓库
            _data: {
              _pageIndex: defaultTableParams?.pageIndex || CONFIG_CONST.PAGE_INDEX,
              _pageSize: defaultTableParams?.pageSize || CONFIG_CONST.PAGE_SIZE,
            }
          },
        },
      });
    } else {
      const initFilterData: {
        isLoading?: boolean;
        isReset?: boolean;
      } = {
        isLoading: true,
      }
      // 只有在加载第一页时，才更新 isReset，更新 isReset 是为了和告诉业务使用者，数据从第一页开始了
      if (isFirstPage) {
        initFilterData.isReset = !state.isReset;
      }
      dispatch({
        type: 'fetch_api_data',
        payload: {
          data: initFilterData,
        },
      });
    }

    // 开始请求数据
    let resultData: YFilterState;
    let filterData: any;
    try {
      // 首次加载
      if (isFirstRequest) {
        const initParams = {
          [defaultTableParams?.apiPageIndex || CONFIG_CONST.API_PAGE_INDEX]: defaultTableParams?.pageIndex || CONFIG_CONST.PAGE_INDEX,
          [defaultTableParams?.apiPageSize || CONFIG_CONST.API_PAGE_SIZE]: defaultTableParams?.pageSize || CONFIG_CONST.PAGE_SIZE,
        }
        filterData = await request({ ...initParams, ...effectiveValues() });
      } else {
        // 非首次请求，请求到的每页多少个，要从仓库里面取
        // 从第一页开始请求, tabIndex 不等于 undefined，目前只有在 tab 切换时使用
        const initParams = tabIndex === undefined ? effectiveValues() : effectiveValues(tabIndex);
        let params = {
          ...initParams,
          [defaultTableParams?.apiPageSize || CONFIG_CONST.API_PAGE_SIZE]: state?._data?._pageSize || CONFIG_CONST.PAGE_SIZE
        };
        // 从第一页开始请求
        if (isFirstPage) {
          params[defaultTableParams?.apiPageIndex || CONFIG_CONST.API_PAGE_INDEX] = CONFIG_CONST.PAGE_INDEX;
        } else {
          // 从当前页嘛开始
          params[defaultTableParams?.apiPageIndex || CONFIG_CONST.API_PAGE_INDEX] = state._data?._pageIndex || CONFIG_CONST.PAGE_INDEX;
        }
        filterData = await request(params);
      }
      resultData = {
        isLoading: false,
        data: filterData,
        error: {
          isError: false,
          message: null,
        }
      };
    } catch (err) {
      console.log("传入filter内部请求函数异常", err);
      resultData = {
        isLoading: false,
        data: null,
        error: {
          isError: true,
          message: err,
        }
      };
    }

    // 请求渲染完之后
    Promise.resolve(null).then(() => {
      if (typeof onAfterRequest === "function") {
        onAfterRequest(filterData);
      }
    })

    if (isFirstRequest || isFirstPage) {
      resultData.isReset = state.isReset;
    }

    dispatch({
      type: 'fetch_api_data',
      payload: {
        data: resultData,
      },
    });
  }

  /**
   * 重置时清空，重新请求
   */
  async function resetFetchServiceData() {
    typeof onBeforeRequest === "function" && onBeforeRequest();
    if (typeof request !== 'function') {
      return;
    }
    dispatch({
      type: 'fetch_api_data',
      payload: {
        data: {
          isLoading: true,
          isReset: !state.isReset
        },
      },
    });
    // 开始请求数据
    let resultData: YFilterState;
    let filterData: any;
    try {
      const initParams = {
        [defaultTableParams?.apiPageIndex || CONFIG_CONST.API_PAGE_INDEX]: defaultTableParams?.pageIndex || CONFIG_CONST.PAGE_INDEX,
        [defaultTableParams?.apiPageSize || CONFIG_CONST.API_PAGE_SIZE]: defaultTableParams?.pageSize || CONFIG_CONST.PAGE_SIZE,
      }
      filterData = await request({ ...configValues(), ...initParams });
      resultData = {
        isLoading: false,
        isReset: state.isReset,
        data: filterData,
        _data: {
          _pageIndex: defaultTableParams?.pageIndex || CONFIG_CONST.PAGE_INDEX,
          _pageSize: defaultTableParams?.pageSize || CONFIG_CONST.PAGE_SIZE,
        },
        error: {
          isError: false,
          message: null,
        }
      };
    } catch (err) {
      console.log("重置传入filter内部请求函数异常", err);
      resultData = {
        isLoading: false,
        isReset: state.isReset,
        data: null,
        error: {
          isError: true,
          message: err,
        }
      };
    }

    Promise.resolve(null).then(() => {
      if (typeof onAfterRequest === "function") {
        onAfterRequest(filterData);
      }
    })

    dispatch({
      type: 'fetch_api_data',
      payload: {
        data: resultData,
      },
    });
  }

  // 提交，查询
  const onFinish = async () => {
    typeof okSumbit === "function" && okSumbit(effectiveValues());
    await fetchServiceData();
  };

  // 重置所有属性
  const onReset = async () => {
    typeof okReset === "function" && okReset();
    const fieldsValue = Utils.resetValues(transformConfig);
    if (restoreDefaultValues) {
      // 重置到初始化位置
      form.setFieldsValue({ ...fieldsValue, ...initDefaultValues });
    } else {
      form.setFieldsValue(fieldsValue); // 所有搜索数据全部清空,置为undefined
    }
    // 清空所有自定义属性
    setCustomFormData({});

    // 如果有必选参数，则不帮助发请求
    if (typeof requiredRequestParams !== 'undefined' &&
      Array.isArray(requiredRequestParams) &&
      requiredRequestParams.length !== 0) {
      return;
    }

    if (isResetRequest) {
      await resetFetchServiceData();
    }
  };

  const childs = React.Children.map(props.children, (child) => React.cloneElement(child as React.FunctionComponentElement<FilterContextProps>, {
    store,
    filters: effectiveValues(),
    tableParams: defaultTableParams
  }));

  // 子组件包裹在外层的组件
  const outerLayerType = (el: ConfigItem): React.ReactNode => (
    <el._type.Group
      {...el.originalProps}
    >
      {optionsHTML(el._type, el.options)}
    </el._type.Group>
  );

  // 子组件包裹在内层的组件
  const innerLayerType = (el: ConfigItem) => (
    <el._type
      {...el.originalProps}
    >
      {optionsHTML(el._type.Option, el.options)}
    </el._type>
  );

  const optionsHTML = (Type: any, options: (OptionsItem[] | string | undefined)): React.ReactNode => {
    if (options) {
      if (typeof options === "string") {
        return options;
      }
      if (Array.isArray(options)) {
        return (
          options.map((val: OptionsItem, i: number) => (
            <Type
              // eslint-disable-next-line react/no-array-index-key
              key={i}
              value={val.value}
              disabled={val.disabled}
            >
              {val.label}
            </Type>
          ))
        )
      }
    }
    return null;
  };

  const [screenWidth] = useWidth(); // 去适配antd的屏幕宽度
  const colConfig = (i: number) => Utils.adaptiveCol(i, screenWidth);

  const autoStyle = (i: number): React.CSSProperties => {
    if (transformConfig.length > CONFIG_CONST.SHOW_MAX_SEARCH_NUM && i >= CONFIG_CONST.SHOW_MAX_SEARCH_NUM) {
      return {
        display: isOpen ? "none" : "block",
      };
    }
    return {};
  };

  // tab 切换时发出请求
  const tabFetchData = async (el: OptionsItem, i: number): Promise<void> => {
    if (el.disabled) return;
    if (tabActiveIndex === i) return;
    typeof tabs?.okTabs === "function" && tabs.okTabs(i);
    setTabActiveIndex(i);
    if (isTabsClearParams) {
      await fetchServiceData(false, true, i);
    } else {
      await resetFetchServiceData();
    }
  };

  // 下层组件使用 useContext 时，可以获取对应的值
  const configObject = {
    store,
    onBeforeRequest,
    onAfterRequest,
    filters: effectiveValues(),
    request,
    defaultTableParams
  };

  // filter 外部处理重新请求页面，是否是当前页面，还是从第一页开始请求
  async function againFetchData(isFirstPage = true): Promise<any> {
    if (isFirstPage) {
      await fetchServiceData(false, true);
    } else {
      await fetchServiceData(false, false);
    }
  }

  /**
   * label
   * @param configItem
   */
  const labelHTML = (configItem: ConfigItem): React.ReactNode => {
    if (isLabelStyle) {
      return <span style={{
        display: 'inline-block',
        width: configItem.labelStyle?.width || labelStyle?.width || CONFIG_CONST.LABEL_WIDTH,
        textAlign: configItem.labelStyle?.textAlign || labelStyle?.textAlign
      }}>
        {configItem.label}
      </span>
    }

    return configItem.label;
  }

  /**
   * 统一监听外界 onChange 事件
   * @param changedValues
   */
  const handleChangeKeyValue = (changedValues: { [x: string]: any; }): void => {
    const reg = new RegExp(`^${CONFIG_CONST.EXTERNAL_RENDER_MARK}.*`);
    if (typeof onChange === 'undefined') {
      return;
    }
    const key = Object.keys(changedValues)[0];
    const value = changedValues[key];
    if (reg.test(key)) {
      return;
    }
    if (Array.isArray(value)) {
      const val1 = value[0];
      const val2 = value[1];
      if (Utils.isObject(val1)) {
        onChange(key.substring(CONFIG_CONST.DATE_COMPONENT_MARK.length), [val1 && val1.format(val1._f || "YYYY-MM-DD"), val2 && val2.format(val2._f || "YYYY-MM-DD")]);
      } else {
        onChange(key, value);
      }
    } else {
      if (Utils.isObject(value)) {
        onChange(key.substring(CONFIG_CONST.DATE_COMPONENT_MARK.length), value.format(value._f || "YYYY-MM-DD"));
      } else {
        onChange(key, typeof value === 'string' ? (isTrim ? value.trim() : value) : value);
      }
    }
  }

  /**
   * 存在Tabs切换时，渲染的HTML
   */
  const tabsHTML = () => {
    return (
      <>
        {
          tabs &&
          tabs.options &&
          tabs.options.length > 0 &&
          (
            <TabsStyle
              style={tabs.style}
              role="tabs"
              className={tabs.className}>
              <div role="tabs-item">
                {
                  tabs.options.map((el: OptionsItem, i: number) => (
                    <div
                      role="button"
                      aria-hidden="true"
                      role-active={tabActiveIndex === i ? 'yes' : 'no'}
                      role-disabled={el.disabled ? 'yes' : 'no'}
                      key={i}
                      onClick={() => tabFetchData(el, i)}
                    >
                      {el.label}
                    </div>
                  ))
                }
              </div>
              <div role="tabs-item">
                {
                  tabs.tabBarExtraContent
                  && typeof tabs.tabBarExtraContent === "function"
                  && tabs.tabBarExtraContent(externalExposureParameter(), againFetchData)
                }
              </div>
            </TabsStyle>
          )
        }
      </>
    )
  }

  // 对外暴露的参数集合
  const externalExposureParameter = () => {
    return {
      ...effectiveValues(),
      [defaultTableParams?.apiPageIndex || CONFIG_CONST.API_PAGE_INDEX]: state._data?._pageIndex || CONFIG_CONST.PAGE_INDEX,
      [defaultTableParams?.apiPageSize || CONFIG_CONST.API_PAGE_SIZE]: state._data?._pageSize || CONFIG_CONST.PAGE_SIZE,
    }
  }

  /**
   * filter对外提供实例方法
   */
  useImperativeHandle(filterRef, () => ({
    setFilterConfigItemOptions: (apiParam: number | string, options: OptionsItem[]): void => {
      if (typeof apiParam === "number") {
        transformConfig[apiParam].options = options;
        setTransformConfig([...transformConfig]);
      }
      if (typeof apiParam === "string") {
        let index = -1;
        transformConfig.forEach((configItem: ConfigItem, i: number) => {
          if (apiParam === configItem.apiParam) {
            index = i;
          }
        });
        if (index !== -1) {
          transformConfig[index].options = options;
          setTransformConfig([...transformConfig]);
        }
      }
    }
  }))

  return (
    <FilterContext.Provider value={configObject}>
      { !isTabsUnderForm && tabsHTML()}
      <RowColOverflowStyle>
        <Form
          {...formProps}
          onFinish={onFinish}
          initialValues={initDefaultValues}
          onValuesChange={(changedValues) => {
            handleChangeKeyValue(changedValues);
          }}
          ref={filterFormRef}
          form={form}
          style={{ ...{ width: "100%", overflow: "hidden" }, ...(formProps.style || {}) }}
        >

          <Row gutter={[32, 0]}>
            {
              transformConfig.map((configItem: ConfigItem, index: number) => {
                if (configItem === null || configItem === undefined) return null;
                return (
                  <Fragment
                    key={configItem.apiParam}
                  >
                    {
                      typeof configItem.render === "function" ? (
                        <Col
                          {...{ lg: 8, md: 12, sm: 24 }}
                          style={autoStyle(index)}
                        >
                          <CustomStyle
                            width={configItem.labelStyle?.width || labelStyle?.width || CONFIG_CONST.LABEL_WIDTH}>
                            <Form.Item
                              label={typeof configItem.label === "function" ? configItem.label() : (typeof configItem.label === "string" ? labelHTML(configItem) : null)}
                              name={configItem.apiParam}
                              //@ts-ignore
                              role={typeof configItem.label === 'undefined' ? 'isLabelCustom' : 'isCustom'}
                            >
                              {configItem.render(
                                limitKeyChangeValue(configItem.apiParam),
                                unLimitKeyChangeValue,
                                index,
                                configItem)}
                            </Form.Item>
                          </CustomStyle>
                        </Col>
                      )
                        : (
                          <Col
                            {...{ lg: 8, md: 12, sm: 24 }}
                            style={autoStyle(index)}
                          >
                            {/* 此处时间类型组件添加 前缀, 是为了保留原始Moment类型，再新增返回的时间字符串类型 */}
                            <Form.Item
                              name={configItem._moment ? `${CONFIG_CONST.DATE_COMPONENT_MARK}${configItem.apiParam}` : configItem.apiParam}
                              label={typeof configItem.label === "function" ? configItem.label() : (typeof configItem.label === "string" ? labelHTML(configItem) : null)}
                              rules={[{ required: false }]}
                            >
                              {
                                // 四个自定义组件
                                configItem._custom ? (
                                  <configItem._type
                                    {...configItem.originalProps}
                                  />
                                )
                                  :
                                  (configItem.type === "Select"
                                    ?
                                    (
                                      <YSelect
                                        minWidth={null}
                                        value={configItem.originalProps?.value}
                                        originalProps={{
                                          options: (configItem.options as any),
                                          ...configItem.originalProps
                                        }}
                                        // style={{ 'minWidth': 'auto' }}
                                        {...configItem.yselectProps}
                                      />
                                    )
                                    :
                                    ((configItem.type === "Checkbox" || configItem.type === "Radio") && Array.isArray(configItem.options)
                                      ?
                                      outerLayerType(configItem)
                                      :
                                      innerLayerType(configItem)))
                              }
                            </Form.Item>
                          </Col>
                        )
                    }
                  </Fragment>
                );
              })
            }
            {/* 位置永远是固定的，最后一排最右边 */}
            <Col span={Utils.operationColSpanNumber(isOpen, screenWidth, transformConfig)}>
              <Form.Item>
                <FilterOperationStyle>
                  <div>
                    <Button
                      type="primary"
                      htmlType="submit"
                      {...sumbitProps}
                    >
                      {okSumbitText}
                    </Button>
                    {
                      visibleReset &&
                      <Button
                        onClick={onReset}
                        {...resetProps}
                      >
                        {okResetText}
                      </Button>
                    }
                    {
                      typeof operationExtraContent === "function"
                      && operationExtraContent(externalExposureParameter(), againFetchData)
                    }
                    {
                      transformConfig.length > CONFIG_CONST.SHOW_MAX_SEARCH_NUM
                      && (
                        isOpen
                          ? (
                            <Button
                              type="link"
                              onClick={() => { setOpen(false); }}
                            >
                              展开
                              {/* <DownOutlined />] */}
                              <YIcon name="DownOutlined" />
                            </Button>
                          )
                          : (
                            <Button
                              type="link"
                              onClick={() => { setOpen(true); }}
                            >
                              收起
                              {/* <UpOutlined /> */}
                              <YIcon name="UpOutlined" />
                            </Button>
                          )
                      )
                    }
                  </div>
                </FilterOperationStyle>
              </Form.Item>
            </Col>
          </Row>
        </Form>
      </RowColOverflowStyle>
      {
        typeof formBottomExtraContent === "function" &&
        formBottomExtraContent(externalExposureParameter(), againFetchData)
      }
      { isTabsUnderForm && tabsHTML()}
      {childs}
      {typeof onReload === 'function' && onReload(againFetchData)}
    </FilterContext.Provider>
  );
});

export default YFilter;
