/* (c) Copyright Frontify Ltd., all rights reserved. */

import { IconCross } from '@frontify/fondue-icons';
import * as RadixPopover from '@radix-ui/react-popover';
import { forwardRef, type CSSProperties, type ForwardedRef, type ReactNode } from 'react';

import { addAutoFocusAttribute, addShowFocusRing } from '#/utilities/domUtilities';

import styles from './styles/flyout.module.scss';

export type FlyoutRootProps = {
    /**
     * Disable interaction with the rest of the page
     * @default false
     */
    modal?: boolean;
    /**
     * The controlled `open` state of the flyout
     * @default false
     */
    open?: boolean;
    /**
     * Event handler called when the `open` state changes
     */
    onOpenChange?: (open: boolean) => void;
    children?: ReactNode;
};

export const FlyoutRoot = ({ children, ...props }: FlyoutRootProps) => {
    return <RadixPopover.Root {...props}>{children}</RadixPopover.Root>;
};
FlyoutRoot.displayName = 'Flyout.Root';

export type FlyoutTriggerProps = {
    /**
     * Change the default rendered element for the one passed as a child, merging their props and behavior.
     * @default true
     */
    asChild?: boolean;
    children?: ReactNode;
    'data-test-id'?: string;
};

export const FlyoutTrigger = (
    { asChild = true, children, 'data-test-id': dataTestId = 'fondue-flyout-trigger', ...props }: FlyoutTriggerProps,
    ref: ForwardedRef<HTMLButtonElement>,
) => {
    return (
        <RadixPopover.Trigger
            onMouseDown={addAutoFocusAttribute}
            data-auto-focus-visible="true"
            data-auto-focus-trigger
            data-test-id={dataTestId}
            asChild={asChild}
            ref={ref}
            {...props}
        >
            {children}
        </RadixPopover.Trigger>
    );
};
FlyoutTrigger.displayName = 'Flyout.Trigger';

export type FlyoutContentProps = {
    /**
     * Add a shadow to the flyout
     * @default "medium"
     */
    shadow?: 'none' | 'medium' | 'large';
    /**
     * Add rounded corners to the flyout
     * @default "medium"
     */
    rounded?: 'none' | 'medium' | 'large';
    /**
     * Define the prefered side of the flyout. Can be overriden by viewport collisions viewport.
     * @default "bottom"
     */
    side?: 'top' | 'right' | 'bottom' | 'left';
    /**
     * Define the prefered alignment of the flyout. Can be overriden by viewport collisions viewport.
     * @default "start"
     */
    align?: 'start' | 'center' | 'end';
    /**
     * Define the padding of the flyout
     * @default "compact"
     */
    padding?: 'none' | 'tight' | 'compact' | 'comfortable' | 'spacious';
    /**
     * Define the fixed width of the flyout
     * @default "fit-content"
     */
    width?: string;
    /**
     * Define the maximum width of the flyout
     * @default "360px"
     */
    maxWidth?: string;
    children?: ReactNode;
    'data-test-id'?: string;
};

export const FlyoutContent = (
    {
        align = 'start',
        maxWidth = '360px',
        padding = 'compact',
        rounded = 'medium',
        width = 'fit-content',
        shadow = 'medium',
        'data-test-id': dataTestId = 'fondue-flyout-content',
        children,
        ...props
    }: FlyoutContentProps,
    ref: ForwardedRef<HTMLDivElement>,
) => {
    return (
        <RadixPopover.Portal>
            <>
                <div className={styles.overlay} />
                <RadixPopover.Content
                    style={
                        {
                            '--flyout-max-width': maxWidth,
                            '--flyout-width': width,
                        } as CSSProperties
                    }
                    ref={ref}
                    align={align}
                    collisionPadding={8}
                    sideOffset={8}
                    className={styles.root}
                    data-flyout-spacing={padding}
                    data-rounded={rounded}
                    data-shadow={shadow}
                    data-test-id={dataTestId}
                    onFocus={addShowFocusRing}
                    {...props}
                >
                    {children}
                </RadixPopover.Content>
            </>
        </RadixPopover.Portal>
    );
};
FlyoutContent.displayName = 'Flyout.Content';

export type FlyoutHeaderProps = {
    /**
     * Show a close button in the header
     * @default false
     */
    showCloseButton?: boolean;
    children?: ReactNode;
    'data-test-id'?: string;
};

export const FlyoutHeader = (
    { showCloseButton, children, 'data-test-id': dataTestId = 'fondue-flyout-header' }: FlyoutHeaderProps,
    ref: ForwardedRef<HTMLDivElement>,
) => {
    return (
        <div data-test-id={dataTestId} ref={ref} className={styles.header}>
            <div>{children}</div>
            {showCloseButton && (
                <RadixPopover.Close role="button" data-test-id={`${dataTestId}-close`} className={styles.close}>
                    <IconCross size={20} />
                </RadixPopover.Close>
            )}
        </div>
    );
};
FlyoutHeader.displayName = 'Flyout.Header';

export type FlyoutFooterProps = { children?: ReactNode; 'data-test-id'?: string };

export const FlyoutFooter = (
    { children, 'data-test-id': dataTestId = 'fondue-flyout-footer' }: FlyoutFooterProps,
    ref: ForwardedRef<HTMLDivElement>,
) => {
    return (
        <div data-test-id={dataTestId} ref={ref} className={styles.footer}>
            {children}
        </div>
    );
};
FlyoutFooter.displayName = 'Flyout.Footer';

export type FlyoutBodyProps = { children?: ReactNode; 'data-test-id'?: string };

export const FlyoutBody = (
    { children, 'data-test-id': dataTestId = 'fondue-flyout-body' }: FlyoutBodyProps,
    ref: ForwardedRef<HTMLDivElement>,
) => {
    return (
        <div data-test-id={dataTestId} ref={ref} data-flyout-spacing="compact" className={styles.body}>
            {children}
        </div>
    );
};
FlyoutBody.displayName = 'Flyout.Body';

export const Flyout = {
    Root: FlyoutRoot,
    Trigger: forwardRef<HTMLButtonElement, FlyoutTriggerProps>(FlyoutTrigger),
    Content: forwardRef<HTMLDivElement, FlyoutContentProps>(FlyoutContent),
    Header: forwardRef<HTMLDivElement, FlyoutHeaderProps>(FlyoutHeader),
    Footer: forwardRef<HTMLDivElement, FlyoutFooterProps>(FlyoutFooter),
    Body: forwardRef<HTMLDivElement, FlyoutBodyProps>(FlyoutBody),
};
