import {
  Chain,
  ModalIntegrationStyles,
  Web3Environment,
  WidgetIntegrationPayload,
} from "@src/models";
import { getChains, setApiUrl } from "@src/services";
import { isServer, resolveTokenAddress } from "@src/utils";
import { createWebComponent } from "@src/utils/webComponents";
import { useEffect, useState } from "react";
import { Spinner } from "../Spinner";
import { TxConfigForm } from "../TxConfigForm";
import CSS from "../global.css";
import { addCustomTokensToChains } from "@src/utils/tokens";

const TxWidget = ({
  integratorId,
  dstChain: dstChainId,
  dstToken: dstTokenId,
  srcChain: srcChainId,
  srcToken: srcTokenId,
  customContractCalls = [],
  desc,
  dstDisplayToken,
  lightTheme,
  defaultWalletPicker,
  styles,
  onPendingTransactionsChange,
  dstTokenLocked,
  dstChainLocked,
  srcTokenLocked,
  srcChainLocked,
  onDstTokenChange,
  onDstChainChange,
  onSrcTokenChange,
  onSrcChainChange,
  onConnectWallet,
  bridge,
  override,
  integratorFee,
  integratorFeeReceiverAddress,
  highlightedDstTokens,
}: WidgetIntegrationPayload) => {
  const [loading, setLoading] = useState(true);
  const [supportedChains, setSupportedChains] = useState<Chain[]>([]);
  const [srcTokenAddr, setSrcTokenAddr] = useState<string | undefined>(
    undefined,
  );
  const [dstTokenAddr, setDstTokenAddr] = useState<string | undefined>(
    undefined,
  );

  useEffect(() => {
    (async () => {
      setLoading(true);

      if (override?.apiUrl) {
        setApiUrl(override.apiUrl);
      }

      const chains = (await getChains()).filter(
        ({ web3Environment, bridgeSupported, swapSupported }) =>
          web3Environment === Web3Environment.MAINNET &&
          (bridgeSupported || swapSupported),
      );

      const chainsWithCustomTokens = addCustomTokensToChains(chains);

      setSupportedChains(chainsWithCustomTokens);
      setLoading(false);
    })();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const parsedStyles: ModalIntegrationStyles | undefined = (() => {
    try {
      return typeof styles === "string"
        ? JSON.parse(styles)
        : styles || undefined;
    } catch (error) {
      console.error("Error parsing styles:", error);
    }
  })();

  useEffect(() => {
    const newSrcTokenAddr = resolveTokenAddress(
      srcTokenId,
      srcChainId,
      supportedChains,
    );

    const newDstTokenAddr = resolveTokenAddress(
      dstTokenId,
      dstChainId,
      supportedChains,
    );

    setSrcTokenAddr(newSrcTokenAddr);
    setDstTokenAddr(newDstTokenAddr);
  }, [srcTokenId, srcChainId, dstTokenId, dstChainId, supportedChains]);

  return (
    <>
      <style>{CSS}</style>

      {loading ? (
        <div className="min-h-[532px] w-full flex items-center justify-center">
          <Spinner className="w-10 h-10" />
        </div>
      ) : (
        <TxConfigForm
          integratorId={integratorId}
          dstChainId={dstChainId}
          dstTokenAddr={dstTokenAddr}
          srcChainId={srcChainId}
          srcTokenAddr={srcTokenAddr}
          customContractCalls={customContractCalls}
          desc={desc}
          dstDisplayTokenAddr={dstDisplayToken}
          supportedChains={supportedChains}
          overlay={false}
          lightTheme={lightTheme}
          defaultWalletPicker={defaultWalletPicker}
          styles={parsedStyles}
          onPendingTransactionsChange={onPendingTransactionsChange}
          dstTokenLocked={dstTokenLocked}
          dstChainLocked={dstChainLocked}
          srcTokenLocked={srcTokenLocked}
          srcChainLocked={srcChainLocked}
          onDstTokenChange={onDstTokenChange}
          onDstChainChange={onDstChainChange}
          onSrcTokenChange={onSrcTokenChange}
          onSrcChainChange={onSrcChainChange}
          onConnectWallet={onConnectWallet}
          bridge={bridge}
          override={override}
          integratorFee={integratorFee}
          integratorFeeReceiverAddress={integratorFeeReceiverAddress}
          highlightedDstTokens={highlightedDstTokens}
        />
      )}
    </>
  );
};

/**
 * Web component uses the same names for attributes as {@link TxWidget} for props, but written in kebab-case.
 *
 * Attributes that are `object` or `array` must be provided as stringified JSON.
 *
 * For the reference, see {@link WidgetIntegrationPayload}.
 */

export const createTxWidgetWC = () => {
  if (isServer || typeof customElements === "undefined") {
    return {} as CustomElementConstructor;
  }

  const TxWidgetWC = createWebComponent(TxWidget, {
    shadow: "open",
    props: {
      integratorId: "string",
      dstChain: "string",
      dstToken: "string",
      srcChain: "string",
      srcToken: "string",
      customContractCalls: "json",
      desc: "string",
      dstDisplayToken: "string",
      lightTheme: "boolean",
      defaultWalletPicker: "boolean",
      styles: "json",
      onPendingTransactionsChange: "function",
      dstTokenLocked: "boolean",
      dstChainLocked: "boolean",
      srcTokenLocked: "boolean",
      srcChainLocked: "boolean",
      bridge: "boolean",
      onDstTokenChange: "function",
      onDstChainChange: "function",
      onSrcTokenChange: "function",
      onSrcChainChange: "function",
      override: "json",
      integratorFee: "number",
      integratorFeeReceiverAddress: "string",
      highlightedDstTokens: "json",
    },
  });

  TxWidgetWC.prototype.connectedCallback = function () {
    this.id = "xpay-widget";
  };

  return TxWidgetWC;
};

// Most fields are required here to enforce adding them in TxWidgetWCWrapped.
// callbacks are optional, because it will not be passed in TxWidgetWCWrapped.
export type TxWidgetWCAttributes = {
  "integrator-id": WidgetIntegrationPayload["integratorId"];
  "dst-chain": WidgetIntegrationPayload["dstChain"];
  "dst-token": WidgetIntegrationPayload["dstToken"];
  "src-chain": WidgetIntegrationPayload["srcChain"];
  "src-token": WidgetIntegrationPayload["srcToken"];
  "custom-contract-calls": string | undefined;
  desc: WidgetIntegrationPayload["desc"];
  "dst-display-token": WidgetIntegrationPayload["dstDisplayToken"];
  "light-theme": WidgetIntegrationPayload["lightTheme"];
  "default-wallet-picker": WidgetIntegrationPayload["defaultWalletPicker"];
  styles: string | undefined;
  "on-pending-transactions-change"?:
    | WidgetIntegrationPayload["onPendingTransactionsChange"];
  "dst-token-locked": WidgetIntegrationPayload["dstTokenLocked"] | undefined;
  "dst-chain-locked": WidgetIntegrationPayload["dstChainLocked"] | undefined;
  "src-token-locked": WidgetIntegrationPayload["srcTokenLocked"] | undefined;
  "src-chain-locked": WidgetIntegrationPayload["srcChainLocked"] | undefined;
  "on-dst-token-change"?: WidgetIntegrationPayload["onDstTokenChange"];
  "on-dst-chain-change"?: WidgetIntegrationPayload["onDstChainChange"];
  "on-src-token-change"?: WidgetIntegrationPayload["onSrcTokenChange"];
  "on-src-chain-change"?: WidgetIntegrationPayload["onSrcChainChange"];
  bridge: WidgetIntegrationPayload["bridge"] | undefined;
  override: string | undefined;
  "integrator-fee": WidgetIntegrationPayload["integratorFee"] | undefined;
  "integrator-fee-receiver-address":
    | WidgetIntegrationPayload["integratorFeeReceiverAddress"]
    | undefined;
  "highlighted-dst-tokens": string | undefined;
};
