import React, { useCallback, useMemo, useState, useEffect } from "react";
import { ActivityIndicator, StyleProp, ViewStyle } from "react-native";
import WebView from "react-native-webview";

import { View, Text } from "../..";
import { murmurHash } from "../../utils/stylesheet";
import styles from "./styles";

type Props = {
  errorMessage?: string;
  html: string;
  initialWidth?: number;
  style?: StyleProp<ViewStyle>;
};

const script = `
var MutationObserver =
    window.MutationObserver || window.WebKitMutationObserver;

function reportSize() {
  var target = document.body.firstElementChild || document.querySelector("body");
  var dimensions = target.getBoundingClientRect();

  var data = JSON.stringify({
    id: embedId,
    height: Math.round(dimensions.top + dimensions.bottom),
    width: Math.round(dimensions.left + dimensions.right),
  });

  if (window.ReactNativeWebView) {
    window.ReactNativeWebView.postMessage(data);
  } else {
    window.parent.postMessage(data, "*");
  }
}

var observer = new MutationObserver(reportSize);
observer.observe(document, {
  subtree: true,
  attributes: true
});

window.parent.addEventListener("load", reportSize);
setTimeout(reportSize, 3000); // backup
`;

const Embed = ({ html, errorMessage, initialWidth, style }: Props) => {
  // react-native-web-webview attaches the onMessage handler to the global window,
  // so we need to be able distinguish which messages are for which Embed instance.
  const embedId = useMemo(() => murmurHash(html || ""), [html]);
  const [dimensions, setDimensions] = useState({
    width: initialWidth || "100%",
    height: 0,
  });
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    if (!html) return;
    const width = (html.match(/ width=(["']?)(\d+%?)(?:px)?\1/) || [])[2];
    const height = (html.match(/ height=(["']?)(\d+)(?:px)?\1/) || [])[2];
    if (height) setLoading(false);
    setDimensions((current) => ({
      width: width || current.width,
      height: +height || current.height,
    }));
  }, [html]);

  const onWebViewMessage = useCallback(
    ({ nativeEvent: { data } }) => {
      if (typeof data === "string") {
        try {
          const json = JSON.parse(data);
          if (json.id === embedId) {
            setDimensions({
              width: json.width,
              height: json.height,
            });
            setLoading(false);
          }
        } catch (err) {}
      }
    },
    [embedId]
  );

  const source = `
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <body>
      ${html || ""}
      <style>
        body {
          margin: 0;
          overflow: hidden;
        }
      </style>
      <script>var embedId = "${embedId}";</script>
    </body>
  `;

  const embedDimensions = {
    height: loading || errorMessage ? 32 : dimensions.height,
    width: loading || errorMessage ? "100%" : dimensions.width,
  };

  return (
    <View style={[embedDimensions, styles.container, style]}>
      {loading && !errorMessage && <ActivityIndicator style={styles.spinner} />}
      {errorMessage ? (
        <Text style={styles.errorMessage}>{errorMessage}</Text>
      ) : (
        <WebView
          injectedJavaScript={script}
          scrollEnabled={false}
          onLoad={() => setLoading(false)}
          onMessage={onWebViewMessage}
          automaticallyAdjustContentInsets={false}
          source={{ html: source }}
          containerStyle={embedDimensions}
        />
      )}
    </View>
  );
};

export default Embed;
