import { StatusBar } from 'expo-status-bar';
import { useEffect, useMemo, useRef, useState } from 'react';
import {
  AppState,
  type AppStateStatus,
  BackHandler,
  Linking,
  Platform,
  StyleSheet,
} from 'react-native';
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
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 { lifecyclePayloadFromAppState } from './src/mobileShellLifecycle';
import {
  resolveMobileShellExternalUrl,
  shouldAcceptMobileShellHostBridgeMessage,
  shouldOpenInMobileShellWebView,
} from './src/mobileShellNavigation';
import { subscribeMobileNetworkStatus } from './src/mobileShellNetwork';
import { MOBILE_SHELL_SAFE_AREA_EDGES } from './src/mobileShellSafeArea';
import {
  buildMobileShellUrl,
  resolveMobileShellBaseWebUrl,
} from './src/mobileShellUrl';
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 = resolveMobileShellBaseWebUrl(
    process.env.EXPO_PUBLIC_GENARRATIVE_WEB_URL,
  );
  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);
      },
      reloadWebView() {
        webViewRef.current?.reload();
      },
    });

    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]);

  useEffect(() => {
    const sendLifecycleEvent = (state: AppStateStatus) => {
      webViewRef.current?.injectJavaScript(
        buildHostBridgeMessageScript({
          bridge: 'GenarrativeHostBridge',
          version: 1,
          event: 'app.lifecycle',
          payload: lifecyclePayloadFromAppState(state),
        }),
      );
    };

    const subscription = AppState.addEventListener(
      'change',
      sendLifecycleEvent,
    );
    sendLifecycleEvent(AppState.currentState);

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

  useEffect(() => {
    return subscribeMobileNetworkStatus((payload) => {
      webViewRef.current?.injectJavaScript(
        buildHostBridgeMessageScript({
          bridge: 'GenarrativeHostBridge',
          version: 1,
          event: 'network.statusChanged',
          payload,
        }),
      );
    });
  }, []);

  const handleMessage = (event: WebViewMessageEvent) => {
    if (
      !shouldAcceptMobileShellHostBridgeMessage(
        event.nativeEvent.url,
        allowedWebOrigin,
      )
    ) {
      return;
    }

    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 (
    <SafeAreaProvider>
      <SafeAreaView style={styles.root} edges={MOBILE_SHELL_SAFE_AREA_EDGES}>
        <StatusBar style="auto" />
        <WebView
          ref={webViewRef}
          source={{ uri: webUrl }}
          javaScriptEnabled
          javaScriptCanOpenWindowsAutomatically={false}
          domStorageEnabled
          mixedContentMode="never"
          originWhitelist={[allowedWebOrigin]}
          allowFileAccess={false}
          allowFileAccessFromFileURLs={false}
          allowUniversalAccessFromFileURLs={false}
          thirdPartyCookiesEnabled={false}
          sharedCookiesEnabled={false}
          webviewDebuggingEnabled={false}
          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}
        />
      </SafeAreaView>
    </SafeAreaProvider>
  );
}

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