import { forwardRef, useRef, useContext, useMemo, useState } from 'react';
import { warn, isFunction } from '@mpxjs/utils';
import Portal from './mpx-portal/index';
import { usePreventRemove } from '@react-navigation/native';
import { getCustomEvent } from './getInnerListeners';
import { promisify, redirectTo, navigateTo, navigateBack, reLaunch, switchTab } from '@mpxjs/api-proxy';
import { WebView } from 'react-native-webview';
import useNodesRef from './useNodesRef';
import { getCurrentPage, useNavigation } from './utils';
import { RouteContext } from './context';
import { StyleSheet, View, Text } from 'react-native';
const styles = StyleSheet.create({
    loadErrorContext: {
        display: 'flex',
        alignItems: 'center'
    },
    loadErrorText: {
        fontSize: 12,
        color: '#666666',
        paddingTop: '40%',
        paddingBottom: 20,
        paddingLeft: '10%',
        paddingRight: '10%',
        textAlign: 'center'
    },
    loadErrorButton: {
        color: '#666666',
        textAlign: 'center',
        padding: 10,
        borderColor: '#666666',
        borderStyle: 'solid',
        borderWidth: StyleSheet.hairlineWidth,
        borderRadius: 10
    }
});
const _WebView = forwardRef((props, ref) => {
    const { src, bindmessage, bindload, binderror } = props;
    const mpx = global.__mpx;
    const errorText = {
        'zh-CN': {
            text: '网络不可用，请检查网络设置',
            button: '重新加载'
        },
        'en-US': {
            text: 'The network is not available. Please check the network settings',
            button: 'Reload'
        }
    };
    const currentErrorText = errorText[mpx.i18n?.locale || 'zh-CN'];
    if (props.style) {
        warn('The web-view component does not support the style prop.');
    }
    const { pageId } = useContext(RouteContext) || {};
    const [pageLoadErr, setPageLoadErr] = useState(false);
    const currentPage = useMemo(() => getCurrentPage(pageId), [pageId]);
    const webViewRef = useRef(null);
    const fristLoaded = useRef(false);
    const isLoadError = useRef(false);
    const isNavigateBack = useRef(false);
    const statusCode = useRef('');
    const defaultWebViewStyle = {
        position: 'absolute',
        left: 0,
        right: 0,
        top: 0,
        bottom: 0
    };
    const navigation = useNavigation();
    const [isIntercept, setIsIntercept] = useState(false);
    usePreventRemove(isIntercept, (event) => {
        const { data } = event;
        if (isNavigateBack.current) {
            navigation?.dispatch(data.action);
        }
        else {
            webViewRef.current?.goBack();
        }
        isNavigateBack.current = false;
    });
    useNodesRef(props, ref, webViewRef, {
        style: defaultWebViewStyle
    });
    const getHostFromUrl = function (url) {
        if (!url)
            return '';
        // 匹配协议://主机名(:端口) 的模式
        const regex = /^(?:https?|ftp):\/\/([^/?:#]+)(?::(\d+))?/i;
        const match = url.match(regex);
        return match ? match[1] : '';
    };
    const hostValidate = (url) => {
        const host = url && getHostFromUrl(url);
        const hostWhitelists = mpx.config.rnConfig?.webviewConfig?.hostWhitelists || [];
        if (hostWhitelists.length) {
            return hostWhitelists.some((item) => {
                return host.endsWith(item);
            });
        }
        else {
            return true;
        }
    };
    if (!src) {
        return null;
    }
    if (!hostValidate(src)) {
        console.error('访问页面域名不符合domainWhiteLists白名单配置，请确认是否正确配置该域名白名单');
        return null;
    }
    const _reload = function () {
        if (__mpx_mode__ !== 'ios') {
            fristLoaded.current = false; // 安卓需要重新设置
        }
        setPageLoadErr(false);
    };
    const injectedJavaScript = `
    if (window.ReactNativeWebView && window.ReactNativeWebView.postMessage) {
      var _documentTitle = document.title;
      window.ReactNativeWebView.postMessage(JSON.stringify({
        type: 'setTitle',
        payload: {
          _documentTitle: _documentTitle
        }
      }))
      Object.defineProperty(document, 'title', {
        set (val) {
          _documentTitle = val
          window.ReactNativeWebView.postMessage(JSON.stringify({
            type: 'setTitle',
            payload: {
              _documentTitle: _documentTitle
            }
          }))
        },
        get () {
          return _documentTitle
        }
      });
    }
    true;
  `;
    const sendMessage = function (params) {
        return `
      window.mpxWebviewMessageCallback && window.mpxWebviewMessageCallback(${params})
      true;
    `;
    };
    const _changeUrl = function (navState) {
        if (navState.navigationType) { // navigationType这个事件在页面开始加载时和页面加载完成时都会被触发所以判断这个避免其他无效触发执行该逻辑
            currentPage.__webViewUrl = navState.url;
            setIsIntercept(navState.canGoBack);
        }
    };
    const _onLoadProgress = function (event) {
        if (__mpx_mode__ !== 'ios') {
            setIsIntercept(event.nativeEvent.canGoBack);
        }
    };
    const _message = function (res) {
        if (!hostValidate(res.nativeEvent?.url)) {
            return;
        }
        let data = {};
        let asyncCallback;
        const navObj = promisify({ redirectTo, navigateTo, navigateBack, reLaunch, switchTab });
        try {
            const nativeEventData = res.nativeEvent?.data;
            if (typeof nativeEventData === 'string') {
                data = JSON.parse(nativeEventData);
            }
        }
        catch (e) { }
        const args = data.args;
        const postData = data.payload || {};
        const params = Array.isArray(args) ? args : [postData];
        const type = data.type;
        switch (type) {
            case 'setTitle':
                { // case下不允许直接声明，包个块解决该问题
                    const title = postData._documentTitle?.trim();
                    if (title !== undefined) {
                        navigation && navigation.setPageConfig({ navigationBarTitleText: title });
                    }
                }
                break;
            case 'postMessage':
                bindmessage && bindmessage(getCustomEvent('message', {}, {
                    detail: {
                        data: params[0]?.data
                    }
                }));
                asyncCallback = Promise.resolve({
                    errMsg: 'invokeWebappApi:ok'
                });
                break;
            case 'navigateTo':
                asyncCallback = navObj.navigateTo(...params);
                break;
            case 'navigateBack':
                isNavigateBack.current = true;
                asyncCallback = navObj.navigateBack(...params);
                break;
            case 'redirectTo':
                asyncCallback = navObj.redirectTo(...params);
                break;
            case 'switchTab':
                asyncCallback = navObj.switchTab(...params);
                break;
            case 'reLaunch':
                asyncCallback = navObj.reLaunch(...params);
                break;
            default:
                if (type) {
                    const implement = mpx.config.rnConfig.webviewConfig && mpx.config.rnConfig.webviewConfig.apiImplementations && mpx.config.rnConfig.webviewConfig.apiImplementations[type];
                    if (isFunction(implement)) {
                        asyncCallback = Promise.resolve(implement(...params));
                    }
                    else {
                        /* eslint-disable prefer-promise-reject-errors */
                        asyncCallback = Promise.reject({
                            errMsg: `未在apiImplementations中配置${type}方法`
                        });
                    }
                }
                break;
        }
        asyncCallback && asyncCallback.then((res) => {
            if (webViewRef.current?.postMessage) {
                const result = JSON.stringify({
                    type,
                    callbackId: data.callbackId,
                    result: res
                });
                webViewRef.current.injectJavaScript(sendMessage(result));
            }
        }).catch((error) => {
            if (webViewRef.current?.postMessage) {
                const result = JSON.stringify({
                    type,
                    callbackId: data.callbackId,
                    error
                });
                webViewRef.current.injectJavaScript(sendMessage(result));
            }
        });
    };
    const onLoadEndHandle = function (res) {
        fristLoaded.current = true;
        const src = res.nativeEvent?.url;
        if (isLoadError.current) {
            isLoadError.current = false;
            isNavigateBack.current = false;
            const result = {
                type: 'error',
                timeStamp: res.timeStamp,
                detail: {
                    src,
                    statusCode: statusCode.current
                }
            };
            binderror && binderror(result);
        }
        else {
            const result = {
                type: 'load',
                timeStamp: res.timeStamp,
                detail: {
                    src
                }
            };
            bindload?.(result);
        }
    };
    const onLoadEnd = function (res) {
        if (__mpx_mode__ !== 'ios') {
            res.persist();
            setTimeout(() => {
                onLoadEndHandle(res);
            }, 0);
        }
        else {
            onLoadEndHandle(res);
        }
    };
    const onHttpError = function (res) {
        isLoadError.current = true;
        statusCode.current = res.nativeEvent?.statusCode;
    };
    const onError = function () {
        statusCode.current = '';
        isLoadError.current = true;
        if (!fristLoaded.current) {
            setPageLoadErr(true);
        }
    };
    return (<Portal>
        {pageLoadErr
            ? (<View style={[styles.loadErrorContext, defaultWebViewStyle]}>
              <View style={styles.loadErrorText}><Text style={{ fontSize: 14, color: '#999999' }}>{currentErrorText.text}</Text></View>
              <View style={styles.loadErrorButton} onTouchEnd={_reload}><Text style={{ fontSize: 12, color: '#666666' }}>{currentErrorText.button}</Text></View>
            </View>)
            : (<WebView containerStyle={defaultWebViewStyle} source={{ uri: src }} ref={webViewRef} javaScriptEnabled={true} onNavigationStateChange={_changeUrl} onMessage={_message} injectedJavaScript={injectedJavaScript} onLoadProgress={_onLoadProgress} onLoadEnd={onLoadEnd} onHttpError={onHttpError} onError={onError} allowsBackForwardNavigationGestures={true}></WebView>)}
      </Portal>);
});
_WebView.displayName = 'MpxWebview';
export default _WebView;
