"use client";

import React, { useEffect, useRef } from "react";

import { REMOTE_DEPS_KEY } from "./constants";
import { setupUikitRemoteDeps } from "./setupRemoteDeps";

const remotePromises = new Map();

function hasRequiredDeps(requiredDeps = []) {
  if (typeof window === "undefined") return false;

  const deps = window[REMOTE_DEPS_KEY];

  return requiredDeps.every((key) => deps?.[key]);
}

function ensureRemoteDeps(requiredDeps = []) {
  if (typeof window === "undefined") return;

  if (!hasRequiredDeps(requiredDeps)) {
    setupUikitRemoteDeps();
  }
}

function loadRemote(componentName, remoteFileUrl) {
  const cacheKey = `${componentName}:${remoteFileUrl}`;

  if (!remotePromises.has(cacheKey)) {
    remotePromises.set(
      cacheKey,
      import(
        /* webpackIgnore: true */
        /* @vite-ignore */
        remoteFileUrl
      ).catch((error) => {
        remotePromises.delete(cacheKey);
        throw error;
      }),
    );
  }

  return remotePromises.get(cacheKey);
}

export function createRemoteElementComponent({
  componentName,
  tagName,
  defaultRemoteUrl,
  requiredDeps,
  normalizeProps,
}) {
  return function RemoteElementComponent({
    theme,
    chakraProviderProps,
    ...props
  }) {
    const containerRef = useRef(null);
    const elementRef = useRef(null);
    const latestPropsRef = useRef(null);

    latestPropsRef.current = {
      ...(normalizeProps ? normalizeProps(props) : props),
      ...(theme !== undefined ? { theme } : {}),
      ...(chakraProviderProps ? { chakraProviderProps } : {}),
    };

    useEffect(() => {
      let active = true;

      ensureRemoteDeps(requiredDeps);

      loadRemote(componentName, defaultRemoteUrl)
        .then(() => {
          if (!active) return;
          if (!containerRef.current) return;

          const element = document.createElement(tagName);

          element.componentProps = latestPropsRef.current;

          containerRef.current.appendChild(element);
          elementRef.current = element;
        })
        .catch((error) => {
          console.error(
            `[uikit/remote/ui] Failed to load ${componentName}:`,
            error,
          );
        });

      return () => {
        active = false;

        if (elementRef.current) {
          elementRef.current.remove();
          elementRef.current = null;
        }
      };
    }, [componentName, defaultRemoteUrl, tagName]);

    useEffect(() => {
      if (!elementRef.current) return;

      elementRef.current.componentProps = latestPropsRef.current;
    });

    return (
      <span
        ref={containerRef}
        style={{
          display: "contents",
        }}
      />
    );
  };
}
