import { Button } from "primereact/button";
import { Icon } from "../../types/icon";
import { OSLabel } from "../label";
import { useAsyncOperation } from "../../hooks/AsyncOperation";
import { useEffect, useRef, useState } from "react";
import { OSIconStateful } from "../icon/OSIconStateful";
import { OSButtonProps } from "./props";
import { resolveIcon } from "../../internal/helpers/resolveIcon";
import { Tooltip } from "primereact/tooltip";

/**
 * Extension of  Prime React's {@link Button} component.
 * 
 * * Uses {@link Icon} for `icon` field instead of class selectors.
 * * Supports Promise types for `onClick` handling, disabling button during process.
 * * Better support of icon only buttons and no icon buttons.
 * 
 */
export function OSButton({
    icon,
    label,
    allowConcurrentExecutions,
    loadingLabel,
    onClick,
    tooltip,
    tooltipOptions,
    className,
    ...props
}: OSButtonProps) {

    const classes = ["os-button", "place-content-center"];

    //Current state of button. use `state === State.Busy` to check loading instead of `loading`
    const [state, setState] = useState<State>(State.Busy);
    //Manage state of button during `Promise` calls.
    const [asyncHandler, commandRunning] = useAsyncOperation();

    const elementRef = useRef<any>(null);

    //Check all conditions to determine whether button should display as busy
    useEffect(() => {
        if (props.loading || (commandRunning && !allowConcurrentExecutions)) {
            setState(State.Busy);
        } else {
            setState(State.Default);
        }
    }, [commandRunning, props.loading, allowConcurrentExecutions]);

    //Further define classes based on present properties
    if (icon && !label) {
        classes.push("p-button-icon-only");
        classes.push("os-icon-button");
    } else if (label && !icon) {
        classes.push("os-text-button");
    } else if (!label && !icon) {

    } else {
        classes.push("os-icon-text-button");
    }

    icon = resolveIcon(icon);

    /** Pass click event to async handler, to disable while running */
    const clickHandler = async (event: React.MouseEvent<HTMLButtonElement, MouseEvent>): Promise<void> => {
        if (onClick) {
            await asyncHandler(async () => await onClick(event));
        }
    }

    const hasLabel = label !== undefined;

    //Handle icon position
    classes.push("flex");
    switch(props.iconPos) {
        case "right": classes.push("flex-row-reverse"); break;
        case "bottom": classes.push("flex-col-reverse"); break;
        case "top": classes.push("flex-col"); break;
        default: break;
    }

    return (<>
        <Button {...props} ref={elementRef} className={[...classes, className].join(" ")} loading={(state === State.Busy)} onClick={clickHandler} loadingIcon={<></>}>
            {icon && <OSIconStateful className="os-button-icon" {...icon} states={[
                {
                    state: State.Default
                },
                {
                    state: State.Busy,
                    icon: {
                        identifier: "spinner-third",
                        animation: {
                            animation: "spin",
                            animationDuration: "750ms"
                        },
                    }
                }]} currentState={state} />}
            {hasLabel && <OSLabel className="os-button-text" label={(state === State.Busy) && loadingLabel ? loadingLabel : label} />}
        </Button>
        {tooltip && (elementRef !== null) && <Tooltip className="os-button-tooltip" target={elementRef} {...tooltipOptions}>{tooltip}</Tooltip>}
    </>);
}

/**
 * Internal use. Possible states for button.
 * @package
 */
enum State {
    Default,
    Busy
}