<script lang="ts">
  import {
    Footer,
    Header,
    Image,
    Package,
    PurchaseButton,
    Stack,
    Timeline,
    Video,
  } from "../..";
  import ButtonNode from "../button/ButtonNode.svelte";
  import Countdown from "../countdown/Countdown.svelte";
  import InputText from "../input-text/InputText.svelte";
  import InputMultipleChoice from "../options/InputMultipleChoice.svelte";
  import InputOption from "../options/InputOption.svelte";
  import InputSingleChoice from "../options/InputSingleChoice.svelte";
  import RedemptionButton from "../redemption-button/RedemptionButton.svelte";
  import TextNode from "../text/TextNode.svelte";
  import type { Component } from "../../types/component";
  import type { Component as SvelteComponent } from "svelte";
  import Carousel from "../carousel/Carousel.svelte";
  import Icon from "../icon/Icon.svelte";
  import TabControl from "../tabs/TabControl.svelte";
  import TabControlButton from "../tabs/TabControlButton.svelte";
  import TabControlToggle from "../tabs/TabControlToggle.svelte";
  import Tabs from "../tabs/Tabs.svelte";
  import WalletButton from "../wallet-button/WalletButton.svelte";
  import SkeletonLoader from "../skeleton-loader/SkeletonLoader.svelte";
  import ExpressPurchaseButton from "../express-purchase-button/ExpressPurchaseButton.svelte";

  interface Props {
    nodeData: Component;
  }

  type RenderableComponent = Exclude<
    Component,
    { type: "tab" | "fallback_header" }
  >;

  const ComponentTypes = {
    button: ButtonNode,
    carousel: Carousel,
    countdown: Countdown,
    express_purchase_button: ExpressPurchaseButton,
    footer: Footer,
    header: Header,
    icon: Icon,
    image: Image,
    input_multiple_choice: InputMultipleChoice,
    input_option: InputOption,
    input_single_choice: InputSingleChoice,
    input_text: InputText,
    package: Package,
    purchase_button: PurchaseButton,
    redemption_button: RedemptionButton,
    skeleton_loader: SkeletonLoader,
    stack: Stack,
    tab_control_button: TabControlButton,
    tab_control_toggle: TabControlToggle,
    tab_control: TabControl,
    tabs: Tabs,
    text: TextNode,
    timeline: Timeline,
    video: Video,
    wallet_button: WalletButton,
  } satisfies {
    [key in RenderableComponent["type"]]: SvelteComponent<
      Extract<RenderableComponent, { type: key }>
    >;
  };

  const isRenderableComponent = (
    component: Component,
  ): component is RenderableComponent => component.type in ComponentTypes;

  /**
   * This function returns the component class and the node data for a given paywall component.
   * It first checks if the component type is supported and returns the corresponding component class.
   * If not supported, it checks if the fallback component type is supported and returns the corresponding component class,
   * finally it throws an error if the component type is not supported and the fallback component type is not supported.
   * @param nodeData:Component - the Component object representing a Node in the paywall
   * @returns [Component<SupportedComponents>, Component] - a tuple containing the component class and the node data
   * @throws Error - if the component type and the fallback component type are not supported
   */
  export const getComponentClass: (
    nodeData: Component,
  ) => [SvelteComponent<Component>, Component] | undefined = (
    nodeData: Component,
  ) => {
    if (isRenderableComponent(nodeData)) {
      return [
        ComponentTypes[nodeData.type] as SvelteComponent<Component>,
        nodeData,
      ];
    }

    if (nodeData.type === "fallback_header") {
      return undefined;
    }

    const { fallback } = nodeData;
    if (fallback && isRenderableComponent(fallback)) {
      return [
        ComponentTypes[fallback.type] as SvelteComponent<Component>,
        fallback,
      ];
    }

    // Manually throwing error for this specific case until
    // it's handled with fallback components
    // throw new Error(`Invalid component type: ${nodeData.type}`);
  };

  const { nodeData, ...restProps }: Props = $props();

  const [ComponentToRender, dataToUse] = $derived(
    getComponentClass(nodeData) ?? [],
  );
</script>

{#if ComponentToRender}
  <ComponentToRender {...(dataToUse as any) || {}} {...restProps} />
{/if}
