import { StatusBar } from 'expo-status-bar';
import { useEffect, useMemo, useRef, useState } from 'react';
import { BackHandler, Linking, Platform, StyleSheet, View } from 'react-native';
import type { WebViewMessageEvent } from 'react-native-webview';
import { WebView } from 'react-native-webview';

import {
  configureMobileHostBridgeNavigation,
  handleMobileHostBridgeMessage,
  resolveMobileHostCapabilities,
} from './src/mobileHostBridge';
import { buildMobileShellUrlFromDeepLink } from './src/mobileShellDeepLink';
import {
  resolveMobileShellExternalUrl,
  shouldOpenInMobileShellWebView,
} from './src/mobileShellNavigation';
import { buildMobileShellUrl } from './src/mobileShellUrl';

const defaultWebUrl = 'http://127.0.0.1:3000/';
const hostVersion = '0.1.0';

function buildHostBridgeMessageScript(message: unknown) {
  return `window.dispatchEvent(new MessageEvent('message', { data: ${JSON.stringify(
    JSON.stringify(message),
  )} })); true;`;
}

export default function App() {
  const webViewRef = useRef<WebView>(null);
  const [canGoBack, setCanGoBack] = useState(false);
  const baseWebUrl = process.env.EXPO_PUBLIC_GENARRATIVE_WEB_URL || defaultWebUrl;
  const mobileShellUrlOptions = useMemo(
    () => ({
      platform: Platform.OS === 'ios' ? 'ios' as const : 'android' as const,
      hostVersion,
      capabilities: resolveMobileHostCapabilities(),
    }),
    [],
  );
  const [webUrl, setWebUrl] = useState(() =>
    buildMobileShellUrl(baseWebUrl, mobileShellUrlOptions),
  );
  const allowedWebOrigin = useMemo(() => new URL(webUrl).origin, [webUrl]);

  useEffect(() => {
    configureMobileHostBridgeNavigation({
      allowedOrigin: allowedWebOrigin,
      openWebViewUrl(url) {
        setWebUrl(url);
      },
    });

    return () => configureMobileHostBridgeNavigation(null);
  }, [allowedWebOrigin]);

  useEffect(() => {
    let disposed = false;

    const openDeepLink = (url: string | null | undefined) => {
      const nextUrl = buildMobileShellUrlFromDeepLink(
        url,
        baseWebUrl,
        mobileShellUrlOptions,
      );
      setWebUrl(nextUrl);
    };

    void Linking.getInitialURL().then((url) => {
      if (!disposed) {
        openDeepLink(url);
      }
    });

    const subscription = Linking.addEventListener('url', (event) => {
      openDeepLink(event.url);
    });

    return () => {
      disposed = true;
      subscription.remove();
    };
  }, [baseWebUrl, mobileShellUrlOptions]);

  useEffect(() => {
    const subscription = BackHandler.addEventListener(
      'hardwareBackPress',
      () => {
        if (!canGoBack) {
          return false;
        }

        webViewRef.current?.goBack();
        return true;
      },
    );

    return () => subscription.remove();
  }, [canGoBack]);

  const handleMessage = (event: WebViewMessageEvent) => {
    void handleMobileHostBridgeMessage(event.nativeEvent.data, (response) => {
      webViewRef.current?.injectJavaScript(
        buildHostBridgeMessageScript(response),
      );
    });
  };

  const handleShouldStartLoad = (request: { url: string }) => {
    if (shouldOpenInMobileShellWebView(request.url, allowedWebOrigin)) {
      return true;
    }

    const externalUrl = resolveMobileShellExternalUrl(request.url);
    if (externalUrl) {
      void Linking.openURL(externalUrl).catch(() => undefined);
    }
    return false;
  };

  return (
    <View style={styles.root}>
      <StatusBar style="auto" />
      <WebView
        ref={webViewRef}
        source={{ uri: webUrl }}
        javaScriptEnabled
        domStorageEnabled
        originWhitelist={[allowedWebOrigin]}
        onMessage={handleMessage}
        onShouldStartLoadWithRequest={handleShouldStartLoad}
        onNavigationStateChange={(event) => {
          setCanGoBack(event.canGoBack);
          webViewRef.current?.injectJavaScript(
            buildHostBridgeMessageScript({
              bridge: 'GenarrativeHostBridge',
              version: 1,
              event: 'navigation.canGoBack',
              payload: {
                canGoBack: event.canGoBack,
              },
            }),
          );
        }}
        setSupportMultipleWindows={false}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  root: {
    flex: 1,
    backgroundColor: '#fffdf9',
  },
});
