import { Slot } from '@radix-ui/react-slot';
import { cva, type VariantProps } from 'class-variance-authority';
import * as React from 'react';
import { Icon } from '@/components/ui/icon';
import { cn } from '@/lib/utils';

type IconButtonSurface = 'text' | 'contained' | 'outlined';

const iconButtonColorStyles: Record<string, Record<IconButtonSurface, string>> = {
    default: {
        text: 'text-foreground hover:bg-foreground/[0.08] active:bg-foreground/[0.12]',
        contained: 'bg-muted text-foreground hover:bg-muted/85 active:bg-muted/75',
        outlined:
            'border-input text-foreground hover:bg-accent hover:text-accent-foreground active:bg-accent/80',
    },
    primary: {
        text: 'text-primary hover:bg-primary/[0.12] active:bg-primary/[0.16]',
        contained:
            'bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/80 dark:text-black',
        outlined: 'border-primary/40 text-primary hover:bg-primary/[0.08] active:bg-primary/[0.12]',
    },
    secondary: {
        text: 'text-muted-foreground hover:bg-foreground/[0.08] hover:text-foreground active:bg-foreground/[0.12]',
        contained:
            'bg-secondary text-secondary-foreground hover:bg-secondary/85 active:bg-secondary/75',
        outlined:
            'border-input text-muted-foreground hover:bg-accent hover:text-foreground active:bg-accent/80',
    },
    info: {
        text: 'text-info hover:bg-info/[0.12] active:bg-info/[0.16]',
        contained: 'bg-info text-info-foreground hover:bg-info/90 active:bg-info/80',
        outlined: 'border-info/40 text-info hover:bg-info/[0.08] active:bg-info/[0.12]',
    },
    success: {
        text: 'text-success hover:bg-success/[0.12] active:bg-success/[0.16]',
        contained: 'bg-success text-success-foreground hover:bg-success/90 active:bg-success/80',
        outlined: 'border-success/40 text-success hover:bg-success/[0.08] active:bg-success/[0.12]',
    },
    warning: {
        text: 'text-warning hover:bg-warning/[0.12] active:bg-warning/[0.16]',
        contained: 'bg-warning text-warning-foreground hover:bg-warning/90 active:bg-warning/80',
        outlined: 'border-warning/40 text-warning hover:bg-warning/[0.08] active:bg-warning/[0.12]',
    },
    danger: {
        text: 'text-destructive hover:bg-destructive/[0.12] active:bg-destructive/[0.16]',
        contained:
            'bg-destructive text-destructive-foreground hover:bg-destructive/90 active:bg-destructive/80',
        outlined:
            'border-destructive/40 text-destructive hover:bg-destructive/[0.08] active:bg-destructive/[0.12]',
    },
    destructive: {
        text: 'text-destructive hover:bg-destructive/[0.12] active:bg-destructive/[0.16]',
        contained:
            'bg-destructive text-destructive-foreground hover:bg-destructive/90 active:bg-destructive/80',
        outlined:
            'border-destructive/40 text-destructive hover:bg-destructive/[0.08] active:bg-destructive/[0.12]',
    },
    ghost: {
        text: 'text-foreground hover:bg-accent/70 hover:text-accent-foreground active:bg-accent/90',
        contained: 'bg-accent/50 text-foreground hover:bg-accent/70 active:bg-accent/90',
        outlined:
            'border-transparent text-foreground hover:bg-accent/70 hover:text-accent-foreground active:bg-accent/90',
    },
    link: {
        text: 'text-primary hover:opacity-80 active:opacity-70',
        contained: 'bg-primary/10 text-primary hover:bg-primary/16 active:bg-primary/20',
        outlined: 'border-primary/30 text-primary hover:bg-primary/[0.06] active:bg-primary/[0.1]',
    },
    inherit: {
        text: 'text-inherit hover:bg-current/[0.08] active:bg-current/[0.12]',
        contained: 'bg-current/10 text-inherit hover:bg-current/16 active:bg-current/20',
        outlined: 'border-current/30 text-inherit hover:bg-current/[0.08] active:bg-current/[0.12]',
    },
};

const iconButtonCompoundVariants = Object.entries(iconButtonColorStyles).flatMap(
    ([color, surfaces]) =>
        (Object.keys(surfaces) as IconButtonSurface[]).map((variant) => ({
            variant,
            color: color as keyof typeof iconButtonColorStyles,
            class: surfaces[variant],
        })),
);

const iconButtonVariants = cva(
    [
        'relative inline-flex p-4 shrink-0 cursor-pointer items-center justify-center rounded-md',
        'transition-[color,background-color,box-shadow,border-color,opacity]',
        'outline-none focus-visible:ring-[3px] focus-visible:ring-ring/40',
        'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-[0.38]',
        '[&_svg]:pointer-events-none [&_svg]:shrink-0',
    ].join(' '),
    {
        variants: {
            variant: {
                text: 'border border-transparent bg-transparent',
                contained: 'border border-transparent shadow-[var(--shadow-surface)]',
                outlined: 'border bg-transparent shadow-[var(--shadow-surface)]',
            },
            color: {
                default: '',
                primary: '',
                secondary: '',
                info: '',
                success: '',
                warning: '',
                danger: '',
                destructive: '',
                ghost: '',
                link: '',
                inherit: '',
            },
            size: {
                xs: "size-6 [&_svg:not([class*='size-'])]:size-4",
                sm: "size-8 [&_svg:not([class*='size-'])]:size-[1.125rem]",
                md: "size-10 [&_svg:not([class*='size-'])]:size-5",
                lg: "size-12 [&_svg:not([class*='size-'])]:size-6",
            },
        },
        compoundVariants: iconButtonCompoundVariants,
        defaultVariants: {
            variant: 'text',
            color: 'default',
            size: 'md',
        },
    },
);

type IconButtonProps = Omit<React.ComponentProps<'button'>, 'color'> &
    VariantProps<typeof iconButtonVariants> & {
        asChild?: boolean;
        /** Iconify icon id (e.g. `solar:menu-dots-vertical-bold-duotone`). */
        icon?: string | null;
    };

function IconButton({
    className,
    variant,
    color,
    size,
    asChild = false,
    icon,
    children,
    type = 'button',
    ...props
}: IconButtonProps) {
    const Comp = asChild ? Slot : 'button';

    return (
        <Comp
            data-slot="icon-button"
            type={asChild ? undefined : type}
            className={cn(iconButtonVariants({ variant, color, size, className }))}
            {...props}
        >
            {icon ? <Icon icon={icon}  /> : children}
        </Comp>
    );
}

export { IconButton, iconButtonVariants, iconButtonColorStyles };
export type { IconButtonProps };
