import React, { forwardRef, useCallback, memo } from 'react';
import { useTheme, Theme } from '@Acme Corporation/acui';
import { useFormContext } from '../Form';

export type ButtonVariant = 'solid' | 'outline' | 'ghost' | 'link' | 'unstyled';
export type ButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
export type ButtonColorScheme = 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'info' | 'neutral';

export interface ButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'color'> {
  variant?: ButtonVariant;
  size?: ButtonSize;
  colorScheme?: ButtonColorScheme;
  leftIcon?: React.ReactNode;
  rightIcon?: React.ReactNode;
  isLoading?: boolean;
  loadingText?: string;
  isFullWidth?: boolean;
  isDisabled?: boolean;
  isActive?: boolean;
  isFocusable?: boolean;
  spinner?: React.ReactNode;
  spinnerPlacement?: 'start' | 'end';
  form?: string;
  type?: 'button' | 'submit' | 'reset';
  name?: string;
  value?: string;
  className?: string;
  wrapperClassName?: string;
  iconClassName?: string;
  spinnerClassName?: string;
  onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
  onFocus?: (event: React.FocusEvent<HTMLButtonElement>) => void;
  onBlur?: (event: React.FocusEvent<HTMLButtonElement>) => void;
  onKeyDown?: (event: React.KeyboardEvent<HTMLButtonElement>) => void;
  onKeyUp?: (event: React.KeyboardEvent<HTMLButtonElement>) => void;
  onMouseEnter?: (event: React.MouseEvent<HTMLButtonElement>) => void;
  onMouseLeave?: (event: React.MouseEvent<HTMLButtonElement>) => void;
  onMouseDown?: (event: React.MouseEvent<HTMLButtonElement>) => void;
  onMouseUp?: (event: React.MouseEvent<HTMLButtonElement>) => void;
  onTouchStart?: (event: React.TouchEvent<HTMLButtonElement>) => void;
  onTouchEnd?: (event: React.TouchEvent<HTMLButtonElement>) => void;
  onTouchMove?: (event: React.TouchEvent<HTMLButtonElement>) => void;
  onTouchCancel?: (event: React.TouchEvent<HTMLButtonElement>) => void;
}

const Button = forwardRef<HTMLButtonElement, ButtonProps>(({
  variant = 'solid',
  size = 'md',
  colorScheme = 'primary',
  leftIcon,
  rightIcon,
  isLoading = false,
  loadingText,
  isFullWidth = false,
  isDisabled = false,
  isActive = false,
  isFocusable = true,
  spinner,
  spinnerPlacement = 'start',
  form,
  type = 'button',
  name,
  value,
  className = '',
  wrapperClassName = '',
  iconClassName = '',
  spinnerClassName = '',
  children,
  onClick,
  onFocus,
  onBlur,
  onKeyDown,
  onKeyUp,
  onMouseEnter,
  onMouseLeave,
  onMouseDown,
  onMouseUp,
  onTouchStart,
  onTouchEnd,
  onTouchMove,
  onTouchCancel,
  ...props
}, ref) => {
  const theme = useTheme<Theme>();
  const formContext = React.useContext(FormContext);

  const handleClick = useCallback((event: React.MouseEvent<HTMLButtonElement>) => {
    if (isDisabled || isLoading) {
      event.preventDefault();
      return;
    }
    onClick?.(event);
  }, [isDisabled, isLoading, onClick]);

  const handleKeyDown = useCallback((event: React.KeyboardEvent<HTMLButtonElement>) => {
    if (isDisabled || isLoading) {
      event.preventDefault();
      return;
    }
    onKeyDown?.(event);
  }, [isDisabled, isLoading, onKeyDown]);

  const handleKeyUp = useCallback((event: React.KeyboardEvent<HTMLButtonElement>) => {
    if (isDisabled || isLoading) {
      event.preventDefault();
      return;
    }
    onKeyUp?.(event);
  }, [isDisabled, isLoading, onKeyUp]);

  const buttonId = React.useId();
  const loadingId = React.useId();

  const ariaProps = {
    'aria-disabled': isDisabled ? true : undefined,
    'aria-busy': isLoading ? true : undefined,
    'aria-describedby': isLoading && loadingText ? loadingId : undefined,
    'aria-pressed': variant === 'solid' ? isActive : undefined
  } as const;

  const defaultSpinner = (
    <svg
      className={`bf-button-spinner ${spinnerClassName}`}
      viewBox="0 0 24 24"
      role="status"
      aria-hidden="true"
    >
      <circle
        className="bf-button-spinner-circle"
        cx="12"
        cy="12"
        r="10"
        fill="none"
        stroke="currentColor"
        strokeWidth="4"
      />
    </svg>
  );

  const renderContent = () => {
    if (isLoading) {
      return (
        <>
          {spinnerPlacement === 'start' && (spinner || defaultSpinner)}
          {loadingText || children}
          {spinnerPlacement === 'end' && (spinner || defaultSpinner)}
        </>
      );
    }

    return (
      <>
        {leftIcon && (
          <span
            className={`bf-button-icon bf-button-icon-left ${iconClassName}`}
            aria-hidden="true"
          >
            {leftIcon}
          </span>
        )}
        {children}
        {rightIcon && (
          <span
            className={`bf-button-icon bf-button-icon-right ${iconClassName}`}
            aria-hidden="true"
          >
            {rightIcon}
          </span>
        )}
      </>
    );
  };

  return (
    <div
      className={`bf-button-wrapper ${wrapperClassName}`}
      data-variant={variant}
      data-size={size}
      data-color-scheme={colorScheme}
      data-full-width={isFullWidth}
      data-disabled={isDisabled}
      data-loading={isLoading}
      data-active={isActive}
    >
      <button
        ref={ref}
        id={buttonId}
        type={type}
        form={form}
        name={name}
        value={value}
        disabled={isDisabled}
        tabIndex={isFocusable ? 0 : -1}
        onClick={handleClick}
        onFocus={onFocus}
        onBlur={onBlur}
        onKeyDown={handleKeyDown}
        onKeyUp={handleKeyUp}
        onMouseEnter={onMouseEnter}
        onMouseLeave={onMouseLeave}
        onMouseDown={onMouseDown}
        onMouseUp={onMouseUp}
        onTouchStart={onTouchStart}
        onTouchEnd={onTouchEnd}
        onTouchMove={onTouchMove}
        onTouchCancel={onTouchCancel}
        {...ariaProps}
        className={`bf-button ${className}`}
        {...props}
      >
        {renderContent()}
      </button>
      {isLoading && loadingText && (
        <span
          id={loadingId}
          className="bf-button-loading-text"
          aria-live="assertive"
        >
          {loadingText}
        </span>
      )}
    </div>
  );
});

Button.displayName = 'Button';

export default memo(Button);