import { Link } from '@tanstack/react-router';
import { clsx } from 'clsx';
import {
	getDefinedAriaAttributes,
	handleButtonActivationKey
} from '@/components/Inputs/buttonUtils';

interface ButtonInputProps
	extends React.ButtonHTMLAttributes<HTMLButtonElement> {
	children: React.ReactNode;
	onClick?: React.MouseEventHandler<HTMLButtonElement>;
	link?: { to: string; from?: string };
	btnVariant?: 'primary' | 'secondary' | 'tertiary' | 'danger';
	disabled?: boolean;
	size?: 'sm' | 'md' | 'lg';
	className?: string;
	ariaLabel?: string;
	ariaPressed?: boolean;
	ariaExpanded?: boolean;
}

/**
 * A versatile button component that can render as either a <button> or a <Link> element.
 *
 * Use the `btnVariant` prop to adjust the visual style:
 * - "primary" for a green-themed button.
 * - "secondary" for a blue-themed button.
 * - "tertiary" for a neutral, gray-themed button.
 * - "danger" for a red-themed button.
 *
 * The `size` prop controls button dimensions:
 * - "sm" for smaller padding and text.
 * - "md" for default spacing.
 * - "lg" for increased padding and larger, bolder text.
 *
 * @param {ButtonInputProps} props - Props for configuring the button.
 * @return {JSX.Element} The rendered button or link component.
 */
const ButtonInput: React.FC<ButtonInputProps> = ({
	children,
	onClick,
	link,
	btnVariant = 'secondary',
	disabled = false,
	size = 'md',
	className = '',
	ariaLabel,
	ariaPressed,
	ariaExpanded,
	...props
}) => {
	const handleKeyDown = ( e: React.KeyboardEvent<HTMLButtonElement> ) => {
		handleButtonActivationKey({
			e,
			onClick,
			disabled,
			onKeyDown: props.onKeyDown
		});
	};

	const classes = clsx(

		// Base styles for all button variants
		'rounded transition-all duration-200 min-w-fit',

		// Focus styles (distinct from hover)
		'focus:outline-hidden focus:ring-2 focus:ring-offset-2',

		// Variant-specific styles
		{
		'bg-primary text-text-white hover:bg-primary hover:shadow-ringPrimary focus:ring-primary':
			'primary' === btnVariant,
			'bg-blue text-text-white border border-blue-700 hover:bg-wp-blue hover:shadow-ringSecondary focus:ring-blue':
			'secondary' === btnVariant,
			'border border-gray-400 bg-gray-100 text-text-gray hover:bg-gray-200 hover:text-gray hover:shadow-ringNeutral focus:ring-gray-400':
			'tertiary' === btnVariant,
			'bg-red text-text-white hover:bg-red hover:shadow-ringDanger focus:ring-red':
			'danger' === btnVariant
		},

		// Size-specific styles
		{
			'py-0.5 px-3 text-sm font-normal': 'sm' === size, // Small: Reduced padding and smaller text
			'py-1 px-4 text-base font-medium': 'md' === size, // Medium (default): Standard padding and text size
			'py-3 px-8 text-lg font-semibold': 'lg' === size // Large: Increased padding and larger, bolder text
		},

		// Disabled styles
		{
			'opacity-50 cursor-not-allowed focus:ring-0 focus:ring-offset-0':
				disabled
		},
		className
	);

	// Build ARIA attributes, filtering out undefined values
	const ariaAttributes = getDefinedAriaAttributes({
			'aria-label': ariaLabel,
			'aria-pressed': ariaPressed,
			'aria-expanded': ariaExpanded,
			'aria-disabled': disabled ? true : undefined
		});

	if ( link ) {
		if ( disabled ) {

			// Render as disabled span when link is provided but component is disabled
			return (
				<span
					className={classes}
					{...ariaAttributes}
					tabIndex={-1}
					role="button"
				>
					{children}
				</span>
			);
		}

		return (
			<Link
				to={link.to}
				className={classes}
				{...ariaAttributes}
				tabIndex={0}
				role="button"
				onKeyDown={( e ) => {
					if ( 'Enter' === e.key || ' ' === e.key ) {
						e.preventDefault();

						// Link navigation will be handled by the Link component
						( e.target as HTMLElement ).click();
					}
				}}
			>
				{children}
			</Link>
		);
	}

	return (
		<button
			type={props.type || 'button'}
			onClick={onClick}
			onKeyDown={handleKeyDown}
			className={classes}
			disabled={disabled}
			{...ariaAttributes}
			{...props}
		>
			{children}
		</button>
	);
};

ButtonInput.displayName = 'ButtonInput';

export default ButtonInput;
