import React, { type ReactNode, useEffect } from "react";

import type { Token } from "@/types/index";
import type { OrderFlowType } from "@hooks/apis/common/quote/types";
import type { Chain } from "@store/globalSlice";
import type { SupportedLocale } from "./LocalizationProvider";

export interface Theme {
	/** @default 420 */
	width?: "full" | number;

	/** @default base */
	borderRadius?: "none" | "sm" | "base" | "md" | "lg";

	fonts?: {
		primary?: string;
		secondary?: string;
	};
	
	/** Supports hex and rgb formats - #FFFFFF or rgb(255, 255, 255) */
	colors?: {
		text?: {
			primary?: string;
			secondary?: string;
			button?: string;
			theme?: string;
			accent1?: string;
			accent2?: string;
			accent3?: string;
			accent4?: string;
		};
		bg?: {
			layer1?: string;
			layer2?: string;
			layer3?: string;
			accent1?: string;
			accent3?: string;
			accent4?: string;
			main?: string;
			theme?: string;
		};
		border?: {
			strong?: string;
			theme?: string;
		};
		icon?: {
			primary?: string;
			secondary?: string;
			theme?: string;
		};
	};
}

type EventData = {
	title: string;
	message: string;
	action: "APPROVAL" | "SIGN" | "TRANSACTION";
	data?: string;
};

export interface EventHandlers {
	onFromTokenChange?: (value: Token) => void;
	onToTokenChange?: (value: Token) => void;
	onFromChainChange?: (value: Chain) => void;
	onToChainChange?: (value: Chain) => void;
	onEvent?: (
		eventType: "success" | "error" | "warning",
		eventData: EventData,
	) => void;
	onOpenWalletConnect?: (network: "solana" | "eip155") => void;
	onSwapInitiated?: (data: {
		chainId: number;
		hash: string;
		type: OrderFlowType<"solana">;
	}) => void;
	logError?: (error: unknown, context?: Record<string, unknown>) => void;
}

export interface BungeeConfig {
	apiKey: string;
	baseUrl?: string;
	theme?: Theme;

	rpcs?: {
		solana?: string;
		eip155?: string;
	};

	initialValues?: {
		fromChain?: number;
		inputTokens?: string[];
		toChain?: number;
		outputTokens?: string[];
		amount?: string[];
		outputRatio?: number[];
		locale?: SupportedLocale;
		sortPreference?: "fast" | "best";
		swapSlippage?: number;
	};

	supportedTokens?: {
		from?: { [chainId: string]: string[] };
		to?: { [chainId: string]: string[] };
	};

	supportedChains?: {
		from?: number[];
		to?: number[];
	};
	
	eventHandlers?: EventHandlers;

	features?: {
		/** 
		 * To show/hide the toasts within widget
		 * @default true
		 */
		internalToasts?: boolean;

		// not implemented yet
		autoRouting?: boolean;
		refuel?: boolean;
		internalTxHistory?: boolean;
	};

	// NOT IMPLEMENTED YET
	includeDexes?: string[];
	excludeDexes?: string[];
	includeBridges?: string[];
	excludeBridges?: string[];
	feeParams?: {
		feeTakerAddress?: string;
		feePercent?: number;
	};
	destinationPayload?: string;
	openTxItem?: number;
	removeBranding?: boolean;
	// NOT IMPLEMENTED YET
}

export const WidgetConfigContext = React.createContext<BungeeConfig | null>(
	null,
);

export const ConfigProvider = ({
	children,
	config,
}: {
	children: ReactNode;
	config: BungeeConfig | null;
}) => {
	useEffect(() => {
		if (config?.theme) {
			setCssVariables(config.theme);
		}
	}, [config]);

	return (
		<WidgetConfigContext.Provider value={config}>
			{children}
		</WidgetConfigContext.Provider>
	);
};

function isHexCode(str: string) {
	return /^#?([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$/.test(str);
}

function isRgbCode(str: string) {
	return /^rgb\(\s*(\d{1,3}\s*,\s*){2}\d{1,3}\s*\)$/.test(str);
}

function formatRGB(colorCode: string): string {
	if (isHexCode(colorCode)) {
		let hex = colorCode.replace(/^#/, "");
		if (hex.length === 3) {
			hex = hex
				.split("")
				.map((ch) => ch + ch)
				.join("");
		}
		const r = Number.parseInt(hex.slice(0, 2), 16);
		const g = Number.parseInt(hex.slice(2, 4), 16);
		const b = Number.parseInt(hex.slice(4, 6), 16);
		// biome-ignore format:
		return `${r} ${g} ${b}`;
	}

	if (isRgbCode(colorCode)) {
		return colorCode
			.replace(/rgb|\(|\)/g, "")
			.split(",")
			.map((x) => x.trim())
			.join(" ");
	}

	throw new Error("Invalid color code. Supports hex and rgb formats.");
}

function updateCssVariable(variableName: string, newValue: string) {
	const root =
		document.getElementById("bn-swap-wrap") ?? document.documentElement;
	root.style.setProperty(variableName, newValue);
}

function setCssVariables(theme: Theme) {
	const { colors, fonts } = theme;

	if (colors) {
		setColorVars(colors);
	}

	if (fonts) {
		for (const [key, value] of Object.entries(fonts)) {
			if (value) {
				// biome-ignore format:
				updateCssVariable(`--bn-font-${key}`, value);
			}
		}
	}
}

function setColorVars(
	colorSection: Record<string, unknown>,
	prefix: string[] = [],
) {
	for (const [key, value] of Object.entries(colorSection)) {
		const currentPath = [...prefix, key];
		if (typeof value === "object" && value !== null) {
			setColorVars(value as Record<string, unknown>, currentPath);
		} else if (typeof value === "string") {
			try {
				const kebabPath = currentPath.map(camelToKebab).join("-");
				// biome-ignore format: 
				const cssVarName = `--bn-${kebabPath}`;
				const formatted = formatRGB(value);
				updateCssVariable(cssVarName, formatted);
			} catch (err) {
				console.warn(`Skipping invalid color value for ${key}: ${value}`);
			}
		}
	}
}

function camelToKebab(str: string): string {
	return str
		.replace(/([a-z])([A-Z])/g, "$1-$2")     // camelCase → kebab-case
		.replace(/([a-zA-Z])([0-9])/g, "$1-$2")  // word+number → word-number
		.toLowerCase();
}
