import * as React from "react";
import {} from "react-native";
import { useState, useContext, useRef, useEffect } from "react";
import { getDefaultRegisteredComponents } from "../../constants/builder-registered-components";
import { TARGET } from "../../constants/target";
import ComponentsContext from "../../context/components.context";
import type {
  BuilderContextInterface,
  BuilderRenderState,
  RegisteredComponents,
} from "../../context/types";
import { evaluate } from "../../functions/evaluate/evaluate";
import { serializeIncludingFunctions } from "../../functions/register-component";
import { logger } from "../../helpers/logger";
import type { ComponentInfo } from "../../types/components";
import type { Dictionary } from "../../types/typescript";
import Blocks from "../blocks/blocks";
import { getUpdateVariantVisibilityScript } from "../content-variants/helpers";
import DynamicDiv from "../dynamic-div";
import InlinedScript from "../inlined-script";
import EnableEditor from "./components/enable-editor";
import ContentStyles from "./components/styles";
import {
  getContentInitialValue,
  getRootStateInitialValue,
} from "./content.helpers";
import type { ContentProps } from "./content.types";
import { wrapComponentRef } from "./wrap-component-ref";
import { ScrollView } from "react-native";

function ContentComponent(props: ContentProps) {
  const [scriptStr, setScriptStr] = useState(() =>
    getUpdateVariantVisibilityScript({
      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-non-null-asserted-optional-chain
      variationId: props.content?.testVariationId!,
      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-non-null-asserted-optional-chain
      contentId: props.content?.id!,
    })
  );

  function contentSetState(newRootState: BuilderRenderState) {
    setBuilderContextSignal((PREVIOUS_VALUE) => ({
      ...PREVIOUS_VALUE,
      rootState: newRootState,
    }));
  }

  const [registeredComponents, setRegisteredComponents] = useState(() =>
    [
      ...getDefaultRegisteredComponents(),
      ...(props.customComponents || []),
    ].reduce<RegisteredComponents>(
      (acc, { component, ...info }) => ({
        ...acc,
        [info.name]: {
          component: component,
          ...serializeIncludingFunctions(info),
        },
      }),
      {}
    )
  );

  const [builderContextSignal, setBuilderContextSignal] =
    useState<BuilderContextInterface>(() => ({
      strictStyleMode: props.strictStyleMode,
      content: getContentInitialValue({
        content: props.content,
        data: props.data,
      }),
      localState: undefined,
      rootState: getRootStateInitialValue({
        content: props.content,
        data: props.data,
        locale: props.locale,
      }),
      rootSetState: contentSetState,
      context: props.context || {},
      canTrack: props.canTrack,
      apiKey: props.apiKey,
      apiVersion: props.apiVersion,
      componentInfos: [
        ...getDefaultRegisteredComponents(),
        ...(props.customComponents || []),
      ].reduce<Dictionary<ComponentInfo>>(
        (acc, { component: _, ...info }) => ({
          ...acc,
          [info.name]: serializeIncludingFunctions(info),
        }),
        {}
      ),
      inheritedStyles: {},
      BlocksWrapper: props.blocksWrapper || ScrollView,
      BlocksWrapperProps: props.blocksWrapperProps || {},
      nonce: props.nonce || "",
      model: props.model,
    }));

  const hasInitialized = useRef(false);
  if (!hasInitialized.current) {
    if (!props.apiKey) {
      logger.error(
        "No API key provided to `Content` component. This can cause issues. Please provide an API key using the `apiKey` prop."
      );
    }

    // run any dynamic JS code attached to content
    const jsCode = builderContextSignal.content?.data?.jsCode;
    if (jsCode) {
      evaluate({
        code: jsCode,
        context: props.context || {},
        localState: undefined,
        rootState: builderContextSignal.rootState,
        rootSetState: (newState) => {
          setBuilderContextSignal((PREVIOUS_VALUE) => ({
            ...PREVIOUS_VALUE,
            rootState: newState,
          }));
        },
        isExpression: false,
      });
    }
    hasInitialized.current = true;
  }

  useEffect(() => {
    const updateContentAndRootState = () => {
      const nextId = props.content?.id;
      const currentId = builderContextSignal.content?.id;
      if (nextId && nextId !== currentId) {
        // Update content and root state to reflect the new entry
        builderContextSignal.content = getContentInitialValue({
          content: props.content,
          data: props.data,
        });
        builderContextSignal.rootState = getRootStateInitialValue({
          content: props.content,
          data: props.data,
          locale: props.locale,
        });

        // Update AB-test visibility script
        setScriptStr(
          getUpdateVariantVisibilityScript({
            variationId: props.content?.testVariationId || "",
            contentId: nextId,
          })
        );
      }
    };
  }, [props.content, props.data, props.locale]);

  return (
    <ComponentsContext.Provider
      value={{
        registeredComponents: registeredComponents,
      }}
    >
      <EnableEditor
        apiHost={props.apiHost}
        nonce={props.nonce}
        content={props.content}
        data={props.data}
        model={props.model}
        context={props.context}
        apiKey={props.apiKey}
        canTrack={props.canTrack}
        locale={props.locale}
        enrich={props.enrich}
        showContent={props.showContent}
        builderContextSignal={builderContextSignal}
        contentWrapper={props.contentWrapper}
        contentWrapperProps={props.contentWrapperProps}
        trustedHosts={props.trustedHosts}
        isNestedRender={props.isNestedRender}
        {...{
          // eslint-disable-next-line object-shorthand
          setBuilderContextSignal: setBuilderContextSignal,
          // eslint-disable-next-line @typescript-eslint/ban-ts-comment
          // @ts-ignore
          strictStyleMode: props.strictStyleMode,
        }}
      >
        {props.isSsrAbTest ? (
          <InlinedScript
            id="builderio-variant-visibility"
            scriptStr={scriptStr}
            nonce={props.nonce || ""}
          />
        ) : null}
        {TARGET !== "reactNative" ? (
          <ContentStyles
            nonce={props.nonce || ""}
            isNestedRender={props.isNestedRender}
            contentId={builderContextSignal.content?.id}
            cssCode={builderContextSignal.content?.data?.cssCode}
            customFonts={builderContextSignal.content?.data?.customFonts}
          />
        ) : null}
        <Blocks
          blocks={builderContextSignal.content?.data?.blocks}
          context={builderContextSignal}
          registeredComponents={registeredComponents}
          linkComponent={props.linkComponent}
        />
      </EnableEditor>
    </ComponentsContext.Provider>
  );
}

export default ContentComponent;
