import {
  Provider as RemuxProvider,
  StoreConfig,
  Model,
  Config,
  PluginMap
} from "react-remux";
import React, { FC } from "react";
import { invariant, isPlainObject } from "mux-lib";
import { ErrorBoundary, ErrorBoundaryProps } from "../ui/ErrorBoundary";
import {
  RouterProvider,
  connectRouter,
  routerMiddleware,
  createBrowserHistory,
  RouterProviderProps
} from "rmux-router";
import {
  RootFetchProvider,
  RootFetchProviderProps,
  FetchProvider,
  BlockFetchProviderProps
} from "rmux-fetch";

export type BlockProviderProps = BlockFetchProviderProps & StoreConfig;
export interface ProviderProps
  extends RootFetchProviderProps,
    ErrorBoundaryProps,
    RouterProviderProps {
  model?: Model;
  plugins?: PluginMap;
  config?: Config;
}

export const BlockProvider: FC<BlockProviderProps> = props => {
  const {
    model,
    plugins = {},
    children,
    namespace,
    prefix,
    fetchOption,
    errorInterceptor,
    requestInterceptor,
    responseInterceptor,
    config
  } = props;

  invariant(
    namespace !== undefined,
    `[rmux] namespace except get string,but get ${typeof namespace}`
  );

  const injectModel = {
    ...model,
    namespace: model.namespace === undefined ? namespace : model.namespace
  };

  return (
    <RemuxProvider model={injectModel} plugins={plugins} config={config}>
      <FetchProvider
        namespace={namespace}
        prefix={prefix}
        fetchOption={fetchOption}
        errorInterceptor={errorInterceptor}
        requestInterceptor={requestInterceptor}
        responseInterceptor={responseInterceptor}
      >
        {children}
      </FetchProvider>
    </RemuxProvider>
  );
};
export const Provider: FC<ProviderProps> = props => {
  const {
    model,
    plugins = {},
    children,
    loading,
    prefix,
    fetchOption,
    errorInterceptor,
    requestInterceptor,
    responseInterceptor,
    config,
    asyncLoader,
    errorFallback
  } = props;

  const { extraReducers = [], middlewares = [] } = plugins;
  const history = props.history || createBrowserHistory();

  /**
   * 项目初始化的时候 全局的 model fetch 配置项都可以为空
   * 只做 router 的初始化
   */

  if (isPlainObject(model)) {
    const injectPlugins = {
      ...plugins,
      extraReducers: [
        {
          router: connectRouter(history)
        },
        ...extraReducers
      ],
      middlewares: [routerMiddleware(history), ...middlewares]
    };

    return (
      <ErrorBoundary errorFallback={errorFallback}>
        <RemuxProvider model={model} plugins={injectPlugins} config={config}>
          <RootFetchProvider
            prefix={prefix}
            fetchOption={fetchOption}
            errorInterceptor={errorInterceptor}
            requestInterceptor={requestInterceptor}
            responseInterceptor={responseInterceptor}
          >
            <RouterProvider
              history={history}
              loading={loading}
              asyncLoader={asyncLoader}
            >
              {children}
            </RouterProvider>
          </RootFetchProvider>
        </RemuxProvider>
      </ErrorBoundary>
    );
  }
  return (
    <ErrorBoundary errorFallback={errorFallback}>
      <RootFetchProvider
        prefix={prefix}
        fetchOption={fetchOption}
        errorInterceptor={errorInterceptor}
        requestInterceptor={requestInterceptor}
        responseInterceptor={responseInterceptor}
      >
        <RouterProvider
          history={history}
          loading={loading}
          asyncLoader={asyncLoader}
        >
          {children}
        </RouterProvider>
      </RootFetchProvider>
    </ErrorBoundary>
  );
};
