import type { ReactNode } from 'react'
import React from 'react'

export type ButtonVariant = 'primary' | 'secondary' | 'success' | 'danger' | 'ghost'
export type ButtonSize = 'small' | 'medium' | 'large'

interface ButtonProps {
  children: ReactNode
  variant?: ButtonVariant
  size?: ButtonSize
  disabled?: boolean
  loading?: boolean
  onClick?: () => void
  type?: 'button' | 'submit' | 'reset'
  className?: string
  'aria-label'?: string
  'aria-expanded'?: boolean
  'aria-haspopup'?: boolean | 'true' | 'false' | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog'
  'aria-selected'?: boolean
  'aria-controls'?: string
  role?: string
  id?: string
}

function getVariantClasses(variant: ButtonVariant): string {
  switch (variant) {
    case 'primary':
      return 'bg-neon-cyan/20 hover:bg-neon-cyan/30 text-neon-cyan border-neon-cyan/50 hover:shadow-glow-cyan'
    case 'secondary':
      return 'bg-cyber-light hover:bg-cyber-dark text-cyber-accent border-neon-cyan/30 hover:border-neon-cyan/50'
    case 'success':
      return 'bg-neon-green/20 hover:bg-neon-green/30 text-neon-green border-neon-green/50 hover:shadow-glow-green'
    case 'danger':
      return 'bg-red-500/20 hover:bg-red-500/30 text-red-400 border-red-500/50'
    case 'ghost':
      return 'bg-transparent hover:bg-cyber-light text-cyber-accent border-transparent hover:border-neon-cyan/30'
    default:
      return 'bg-neon-cyan/20 hover:bg-neon-cyan/30 text-neon-cyan border-neon-cyan/50 hover:shadow-glow-cyan'
  }
}

function getSizeClasses(size: ButtonSize): string {
  switch (size) {
    case 'small':
      return 'px-3 py-1.5 text-sm'
    case 'medium':
      return 'px-4 py-2 text-base'
    case 'large':
      return 'px-6 py-3 text-lg'
    default:
      return 'px-4 py-2 text-base'
  }
}

function LoadingSpinner(): React.ReactElement {
  return (
    <svg
      className="animate-spin h-4 w-4"
      xmlns="http://www.w3.org/2000/svg"
      fill="none"
      viewBox="0 0 24 24"
    >
      <circle
        className="opacity-25"
        cx="12"
        cy="12"
        r="10"
        stroke="currentColor"
        strokeWidth="4"
      />
      <path
        className="opacity-75"
        fill="currentColor"
        d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
      />
    </svg>
  )
}

function ButtonContent({
  children,
  loading,
}: {
  children: ReactNode
  loading: boolean
}): React.ReactElement {
  return (
    <>
      {loading && (
        <span className="mr-2">
          <LoadingSpinner />
        </span>
      )}
      {children}
    </>
  )
}

function getCombinedClasses(
  variant: ButtonVariant,
  size: ButtonSize,
  className: string
): string {
  const variantClasses = getVariantClasses(variant)
  const sizeClasses = getSizeClasses(size)
  const baseClasses = 'inline-flex items-center justify-center rounded-lg font-medium transition-all border focus:outline-none focus:ring-2 focus:ring-neon-cyan disabled:opacity-50 disabled:cursor-not-allowed'
  return `${baseClasses} ${variantClasses} ${sizeClasses} ${className}`.trim()
}

export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>((props, ref): React.ReactElement => {
  const {
    children,
    variant = 'primary',
    size = 'medium',
    disabled = false,
    loading = false,
    onClick,
    type = 'button',
    className = '',
    'aria-label': ariaLabel,
    'aria-expanded': ariaExpanded,
    'aria-haspopup': ariaHaspopup,
    'aria-selected': ariaSelected,
    'aria-controls': ariaControls,
    role: roleProp,
    id,
  } = props
  const combinedClasses = getCombinedClasses(variant, size, className)

  return (
    <button
      ref={ref}
      id={id}
      type={type}
      onClick={onClick}
      disabled={disabled || loading}
      className={combinedClasses}
      role={roleProp}
      aria-label={ariaLabel}
      aria-expanded={ariaExpanded}
      aria-haspopup={ariaHaspopup}
      aria-selected={ariaSelected}
      aria-controls={ariaControls}
      aria-busy={loading}
    >
      <ButtonContent loading={loading}>{children}</ButtonContent>
    </button>
  )
})

Button.displayName = 'Button'
