import * as stylex from "@stylexjs/stylex";
import {
	type MouseEvent,
	type PropsWithChildren,
	type ReactElement,
	memo,
} from "react";
import { Link, type To } from "react-router";

import { controlColor } from "./theme.stylex";
import {
	color,
	font,
	fontSize,
	radius,
	size,
	timingFunction,
	transition,
} from "./tokens.stylex";

import { a11y, interaction, reset } from "./mixins";
import useRippleEffect from "./useRippleEffect";

const styles = stylex.create({
	button: {
		position: "relative", // needed for ripple effect + badge

		fontFamily: font.main,
		fontSize: fontSize.body,

		textAlign: "center",

		borderStyle: "none",
		cursor: {
			default: "pointer",
			":disabled": "not-allowed",
		},

		display: "flex",

		gap: size.rem3,
		justifyContent: "center",
		alignItems: "center",

		transition: `${transition.a11yOutline}, ${transition.themeBackground}, ${transition.themeColor}`,

		// used for badge
		"::after": {
			opacity: 0,
			transition: `opacity ${timingFunction.fast} ease-out`,
		},
	},

	// Neuer Stil für lowercase Text
	lowercase: {
		textTransform: "lowercase",
	},

	// Stile für verschiedene Schriftgewichte
	fontWeightNormal: {
		fontWeight: "normal",
	},

	fontWeightBold: {
		fontWeight: "bold",
	},

	fontWeightLight: {
		fontWeight: 300,
	},

	fontWeightMedium: {
		fontWeight: 500,
	},

	inline: {
		display: "inline-flex",
	},

	icon: {
		fontSize: "120%", // TODO: ASK-UX proportional icon size?
		display: "flex",
	},

	hasBadge: {
		"::after": {
			position: "absolute",

			content: "''",
			aspectRatio: "1/1",

			opacity: 1,

			// not using ds-unit because the border could not be made with that
			borderWidth: "3px",
			borderStyle: "solid",
			borderColor: color.white900,

			backgroundColor: controlColor.buttonPrimaryBackground,

			borderRadius: radius.circle,

			top: size.n_px2,
			right: size.n_px2,
			width: size.px4,
			height: size.px4,
		},
	},

	noPadding: {
		padding: 0,
	},
});

const buttonSizes = stylex.create({
	tiny: {
		gap: size.rem1,
		padding: `${size.rem1} ${size.rem2}`,
		fontSize: fontSize.tiny,
	},
	small: {
		padding: `${size.rem2} ${size.rem4}`,
		fontSize: fontSize.sub,
	},
	medium: {
		padding: `${size.rem3} ${size.rem6}`,
		fontSize: fontSize.body,
	},
	large: {
		padding: `${size.rem4} ${size.rem8}`,
		fontSize: fontSize.h4,
	},
	huge: {
		flexDirection: "column",
		gap: size.rem2,
		padding: `${size.rem4} ${size.rem10}`,
		fontSize: fontSize.h4,
	},
});

const CAN_HOVER = "@media (hover: hover)";

const buttonVariants = stylex.create({
	primary: {
		background: {
			default: controlColor.buttonPrimaryBackground,

			[CAN_HOVER]: {
				":hover": controlColor.buttonPrimaryHoverBackground,
			},
			":active": controlColor.buttonPrimaryActiveBackground,
			":disabled": controlColor.buttonPrimaryDisabledBackground,
		},

		color: {
			default: controlColor.buttonPrimaryColor,
			[CAN_HOVER]: {
				":hover": controlColor.buttonPrimaryHoverColor,
			},
			":active": controlColor.buttonPrimaryActiveColor,
			":disabled": controlColor.buttonPrimaryDisabledColor,
		},
	},

	secondary: {
		background: {
			default: controlColor.buttonPrimaryColor,

			[CAN_HOVER]: {
				":hover": controlColor.buttonPrimaryBackground,
			},
			":active": controlColor.buttonPrimaryActiveBackground,
			":disabled": controlColor.buttonPrimaryDisabledBackground,
		},

		color: {
			default: controlColor.buttonPrimaryBackground,
			[CAN_HOVER]: {
				":hover": controlColor.buttonPrimaryColor,
			},
			":active": controlColor.buttonPrimaryActiveColor,
			":disabled": controlColor.buttonPrimaryDisabledColor,
		},
	},

	subscription: {
		background: {
			default: color.mustard150,
			[CAN_HOVER]: {
				":hover": controlColor.buttonPrimaryHoverBackground,
			},
			":active": controlColor.buttonPrimaryActiveBackground,
			":disabled": controlColor.buttonPrimaryDisabledBackground,
		},

		color: {
			default: controlColor.buttonPrimaryColor,
			[CAN_HOVER]: {
				":hover": controlColor.buttonPrimaryHoverColor,
			},
			":active": controlColor.buttonPrimaryActiveColor,
			":disabled": controlColor.buttonPrimaryDisabledColor,
		},
	},

	tertiary: {
		background: {
			default: controlColor.buttonTertiaryBackground,

			[CAN_HOVER]: {
				":hover": controlColor.buttonTertiaryHoverBackground,
			},
			// TODO: ASK-UX Active style?
			":active": controlColor.buttonTertiaryActiveBackground,
			":disabled": controlColor.buttonTertiaryDisabledBackground,
		},

		color: {
			default: controlColor.buttonTertiaryColor,

			[CAN_HOVER]: {
				":hover": controlColor.buttonTertiaryHoverColor,
			},
			":active": controlColor.buttonTertiaryActiveColor,
			":disabled": controlColor.buttonTertiaryDisabledColor,
		},
	},

	// Quaternary variant: Inverted colors of tertiary
	quaternary: {
		background: {
			default: controlColor.buttonTertiaryHoverBackground,

			[CAN_HOVER]: {
				":hover": controlColor.buttonTertiaryBackground,
			},
			":active": controlColor.buttonTertiaryActiveBackground,
			":disabled": controlColor.buttonTertiaryDisabledBackground,
		},

		color: {
			default: controlColor.buttonTertiaryHoverColor,

			[CAN_HOVER]: {
				":hover": controlColor.buttonTertiaryColor,
			},
			":active": controlColor.buttonTertiaryActiveColor,
			":disabled": controlColor.buttonTertiaryDisabledColor,
		},
	},

	link: {
		color: {
			default: controlColor.linkColor,
			[CAN_HOVER]: {
				":hover": controlColor.linkHoverColor,
			},
			":active": controlColor.linkActiveColor,

			// Nothing here yet?
			// ":disabled": ,
		},

		fontWeight: "normal",
		fontSize: "unset",
		background: "none",
		padding: 0,
		margin: 0,
	},
});

type NativeButtonProps = React.DetailedHTMLProps<
	React.ButtonHTMLAttributes<HTMLButtonElement>,
	HTMLButtonElement
>;

export interface ButtonBaseProps extends PropsWithChildren {
	/**
	 * See aria roles: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles
	 */
	role?: NativeButtonProps["role"];
	title?: string;
	form?: NativeButtonProps["form"];

	variant?:
		| "primary"
		| "tertiary"
		| "link"
		| "subscription"
		| "secondary"
		| "quaternary";
	size?: "tiny" | "small" | "medium" | "large" | "huge";

	icon?: ReactElement<unknown>;
	noPadding?: boolean;
	inline?: boolean;

	/**
	 * Wenn `true`, wird der Text in Kleinbuchstaben angezeigt.
	 * @default false
	 */
	lowercase?: boolean;

	/**
	 * Legt das Schriftgewicht des Buttons fest.
	 * @default "normal"
	 */
	fontWeight?: "normal" | "bold" | "light" | "medium";

	/**
	 * If `true`, shows a dot indicating that there is something new.
	 *
	 * Refs: https://setproduct.com/blog/badge-ui-design
	 */
	badge?: boolean;
}

// Properties and some behavior of this button is based on the Button of MUI:
// https://mui.com/material-ui/react-button/
export interface ButtonButtonProps extends ButtonBaseProps {
	component?: "button" | undefined;

	// Commonly used button props
	onClick?: (event: MouseEvent<HTMLButtonElement>) => void;

	/** Mandatory because the default type is "submit", which is not ideal. */
	type: NativeButtonProps["type"];

	disabled?: boolean;
}

export interface LinkButtonProps extends ButtonBaseProps {
	component: "link";
	disabled?: boolean;
	to: To;

	/**
	 * Also available on the link, so we can implement a share button that links to a URL and can trigger a share functionality.
	 * @remarks If you want to use this, really think it through. Maybe you want to use a `component="button"` instead.
	 */
	onClick?: () => void;
}

export type ButtonProps = ButtonButtonProps | LinkButtonProps;

// TODO: Refactor this to a link button

export default memo(function Button(props: ButtonProps) {
	// Bestimme den fontWeight-Stil basierend auf der Prop oder verwende den Standard "normal"
	const getFontWeightStyle = () => {
		switch (props.fontWeight) {
			case "bold":
				return styles.fontWeightBold;
			case "light":
				return styles.fontWeightLight;
			case "medium":
				return styles.fontWeightMedium;
			default:
				return styles.fontWeightNormal;
		}
	};

	const elementProps = stylex.props(
		interaction.disableTapHighlight,
		reset.buttonStyle,
		a11y.defaultOutline,
		styles.button,
		buttonSizes[props.size ?? "small"],
		buttonVariants[props.variant ?? "primary"],
		props.inline && styles.inline,
		props.noPadding && styles.noPadding,
		props.badge === true && styles.hasBadge,
		props.lowercase && styles.lowercase,
		getFontWeightStyle(),
	);

	const rippleRef = useRippleEffect(props.disabled);
	const refForAnimatedButtons =
		props.noPadding ||
		props.variant === "link" ||
		props.variant === "subscription"
			? undefined
			: rippleRef;

	const onClick = props.onClick;

	if (props.component === "link") {
		return (
			<Link
				ref={refForAnimatedButtons}
				{...elementProps}
				role={props.role}
				title={props.title}
				to={props.to}
				onClick={
					onClick && !props.disabled
						? (e) => {
								e.preventDefault();
								(onClick as () => void)();
							}
						: undefined
				}
			>
				{props.icon && (
					<span {...stylex.props(styles.icon)}>{props.icon}</span>
				)}
				{props.children}
			</Link>
		);
	}
	return (
		<button
			ref={refForAnimatedButtons}
			{...elementProps}
			type={props.type}
			role={props.role}
			form={props.form}
			onClick={onClick}
			disabled={props.disabled}
			title={props.title}
		>
			{props.icon && (
				<span {...stylex.props(styles.icon)}>{props.icon}</span>
			)}
			{props.children}
		</button>
	);
});
