import type { Chain } from "../types"

interface MethodConfig {
	name: string
	method: string
	isSigner?: boolean
}

interface ChainMethods {
	[key: string]:
		| MethodConfig[]
		| {
				viem: MethodConfig[]
				ethers: MethodConfig[]
		  }
}

// Method configurations for each chain
const chainMethods: ChainMethods = {
	Ethereum: {
		viem: [
			{ name: "PublicClient", method: "getPublicClient" },
			{ name: "WalletClient", method: "getWalletClient" },
			{ name: "Message", method: "signMessage", isSigner: true },
		],
		ethers: [
			{ name: "Provider", method: "getWeb3Provider" },
			{ name: "Signer", method: "getSigner" },
			{ name: "Message", method: "signMessage", isSigner: true },
		],
	},
	Solana: [
		{ name: "Connection", method: "getConnection" },
		{ name: "Signer", method: "getSigner" },
		{ name: "Message", method: "signMessage", isSigner: true },
	],
	Starknet: [{ name: "WalletAccount", method: "getWalletAccount" }],
	Algorand: [
		{ name: "Signer", method: "getSigner" },
		{ name: "Message", method: "signMessage", isSigner: true },
	],
	Cosmos: [
		{ name: "OfflineSigner", method: "getOfflineSigner" },
		{ name: "Provider", method: "getProvider" },
		{ name: "Message", method: "signMessage", isSigner: true },
	],
	Sui: [
		{ name: "Client", method: "getSuiClient" },
		{ name: "WalletAccount", method: "getWalletAccount" },
		{ name: "ActiveNetwork", method: "getActiveNetwork" },
	],
}

// Button generator
const generateButton = (label: string, onClick: string) => `
      <button type="button" className="btn btn-primary" onClick={${onClick}}>
        ${label}
      </button>`

// Method generator
const generateMethod = (
	chain: string,
	name: string,
	method: string,
	isSigner = false
) => {
	if (
		chain === "Ethereum" &&
		(method === "getSigner" || method === "getWeb3Provider")
	) {
		return `
  async function fetch${chain}${name}() {
    if (!primaryWallet || !is${chain}Wallet(primaryWallet)) return;
    try {
      setIsLoading(true);
      const result = await ${method}(primaryWallet);
      setResult(safeStringify(result));
    } catch (error) {
      setResult(safeStringify({ error: error instanceof Error ? error.message : 'Unknown error occurred' }));
    } finally {
      setIsLoading(false);
    }
  }`
	}

	return `
  async function fetch${chain}${name}() {
    if (!primaryWallet || !is${chain}Wallet(primaryWallet)) return;
    try {
      setIsLoading(true);
      const result = await primaryWallet.${method}(${isSigner ? '"Hello World"' : ""});
      setResult(safeStringify(result));
    } catch (error) {
      setResult(safeStringify({ error: error instanceof Error ? error.message : 'Unknown error occurred' }));
    } finally {
      setIsLoading(false);
    }
  }`
}

// Chain button generator
const generateChainButtons = (chain: Chain, useViem: boolean) => {
	const buttons: string[] = []

	// Skip if chain not in configuration
	if (!(chain.name in chainMethods)) {
		return ""
	}

	const methods =
		chain.name === "Ethereum"
			? (
					chainMethods[chain.name] as {
						viem: MethodConfig[]
						ethers: MethodConfig[]
					}
				)[useViem ? "viem" : "ethers"]
			: (chainMethods[chain.name] as MethodConfig[])

	for (const { name } of methods) {
		const methodName = `fetch${chain.name}${name}`
		buttons.push(generateButton(`Fetch ${name}`, methodName))
	}

	if (buttons.length === 0) return ""

	return `{primaryWallet && is${chain.name}Wallet(primaryWallet) && (
		<>
			${buttons.join("\n")}
		</>
	)}`
}

export const generateMethodsContent = (
	selectedChains: Chain[],
	useViem: boolean
) => {
	const walletTypeChecks = selectedChains
		.filter((chain) => chain.name in chainMethods)
		.map(
			(chain) =>
				`import { is${chain.name}Wallet } from '@dynamic-labs/${chain.name.toLowerCase()}'`
		)
		.join("\n")

	const ethersImport =
		!useViem && selectedChains.some((chain) => chain.name === "Ethereum")
			? `import { getWeb3Provider, getSigner } from "@dynamic-labs/ethers-v6";\n`
			: ""

	const safeStringify = `
  const safeStringify = (obj: unknown): string => {
    const seen = new WeakSet();
    return JSON.stringify(
      obj,
      (key, value) => {
        if (typeof value === "object" && value !== null) {
          if (seen.has(value)) {
            return "[Circular]";
          }
          seen.add(value);
        }
        return value;
      },
      2
    );
  };
  `

	// Generate all methods for selected chains
	const generatedMethods = selectedChains
		.filter((chain) => chain.name in chainMethods)
		.map((chain) => {
			const methods =
				chain.name === "Ethereum"
					? (
							chainMethods[chain.name] as {
								viem: MethodConfig[]
								ethers: MethodConfig[]
							}
						)[useViem ? "viem" : "ethers"]
					: (chainMethods[chain.name] as MethodConfig[])

			return methods
				.map(({ name, method, isSigner }) =>
					generateMethod(chain.name, name, method, isSigner)
				)
				.join("\n")
		})
		.join("\n")

	return `'use client';
import { useState, useEffect } from 'react';
import { useDynamicContext, useIsLoggedIn, useUserWallets } from "@dynamic-labs/sdk-react-core";
${walletTypeChecks}
${ethersImport}

import './Methods.css';

interface DynamicMethodsProps {
	isDarkMode: boolean;
}

export default function DynamicMethods({ isDarkMode }: DynamicMethodsProps) {
	const isLoggedIn = useIsLoggedIn();
	const { sdkHasLoaded, primaryWallet, user } = useDynamicContext();
	const userWallets = useUserWallets();
	const [isLoading, setIsLoading] = useState(true);
	const [result, setResult] = useState('');
	const [error, setError] = useState<string | null>(null);

	${safeStringify}

	useEffect(() => {
		if (sdkHasLoaded && isLoggedIn && primaryWallet) {
			setIsLoading(false);
		} else {
			setIsLoading(true);
		}
	}, [sdkHasLoaded, isLoggedIn, primaryWallet]);

	function clearResult() {
		setResult('');
		setError(null);
	}

	function showUser() {
		try {
			setResult(safeStringify(user));
			setError(null);
		} catch (err) {
			setError(err instanceof Error ? err.message : 'Failed to stringify user data');
		}
	}

	function showUserWallets() {
		try {
			setResult(safeStringify(userWallets));
			setError(null);
		} catch (err) {
			setError(err instanceof Error ? err.message : 'Failed to stringify wallet data');
		}
	}

	${generatedMethods}

	return (
		<>
			{!isLoading && (
				<div className="dynamic-methods" data-theme={isDarkMode ? 'dark' : 'light'}>
					<div className="methods-container">
						<button className="btn btn-primary" onClick={showUser}>Fetch User</button>
						<button className="btn btn-primary" onClick={showUserWallets}>Fetch User Wallets</button>

						${selectedChains.map((chain) => generateChainButtons(chain, useViem)).join("\n")}
					</div>
					{(result || error) && (
						<div className="results-container">
							{error ? (
								<pre className="results-text error">{error}</pre>
							) : (
								<pre className="results-text">{result}</pre>
							)}
						</div>
					)}
					{(result || error) && (
						<div className="clear-container">
							<button className="btn btn-primary" onClick={clearResult}>Clear</button>
						</div>
					)}
				</div>
			)}
		</>
	);
}`
}
