import { createSignal, JSXElement, onCleanup, Show } from "solid-js";
import { FloatingRoot, FloatingTrigger, FloatingContent, useFloatingContext, Placement } from "./Floating.tsx";

const TOOLTIP_DEFAULT_OFFSET = 8;
const TOOLTIP_CLS = "bg-neutral-800 text-white text-sm px-2.5 py-1.5 rounded-default shadow-lg max-w-80 relative";

function arrowCls(base: string) {
    const common = "absolute w-0 h-0";
    switch (base) {
        case "top":
            return common + " -bottom-[6px] left-1/2 -translate-x-1/2 border-l-[6px] border-l-transparent border-r-[6px] border-r-transparent border-t-[6px] border-t-neutral-800";
        case "bottom":
            return common + " -top-[6px] left-1/2 -translate-x-1/2 border-l-[6px] border-l-transparent border-r-[6px] border-r-transparent border-b-[6px] border-b-neutral-800";
        case "left":
            return common + " -right-[6px] top-1/2 -translate-y-1/2 border-t-[6px] border-t-transparent border-b-[6px] border-b-transparent border-l-[6px] border-l-neutral-800";
        case "right":
            return common + " -left-[6px] top-1/2 -translate-y-1/2 border-t-[6px] border-t-transparent border-b-[6px] border-b-transparent border-r-[6px] border-r-neutral-800";
        default:
            return common;
    }
}

interface TooltipProps {
    content: JSXElement;
    trigger?: "hover" | "focus";
    placement?: "top" | "bottom" | "left" | "right";
    offset?: number;
    delay?: number;
    children?: JSXElement;
}

export function Tooltip(props: TooltipProps) {
    return (
        <Show when={props.trigger === "focus"} fallback={
            <HoverTooltip content={props.content} placement={props.placement ?? "top"} offset={props.offset ?? TOOLTIP_DEFAULT_OFFSET} delay={props.delay ?? 200}>
                {props.children}
            </HoverTooltip>
        }>
            <FocusTooltip content={props.content} placement={props.placement ?? "top"} offset={props.offset ?? TOOLTIP_DEFAULT_OFFSET}>
                {props.children}
            </FocusTooltip>
        </Show>
    );
}

interface HoverTooltipProps {
    content: JSXElement;
    placement: Placement;
    offset: number;
    delay: number;
    children?: JSXElement;
}

export function HoverTooltip(props: HoverTooltipProps) {
    const [isOpen, setIsOpen] = createSignal(false);

    return (
        <FloatingRoot open={isOpen()} onOpenChange={setIsOpen} placement={props.placement} offset={props.offset} flip={true} shift={true}>
            <HoverTooltipTrigger delay={props.delay} setIsOpen={setIsOpen}>
                {props.children}
            </HoverTooltipTrigger>
            <TooltipContentWithArrow placement={props.placement} setIsOpen={setIsOpen}>
                {props.content}
            </TooltipContentWithArrow>
        </FloatingRoot>
    );
}

interface HoverTriggerProps {
    delay: number;
    setIsOpen: (v: boolean) => void;
    children?: JSXElement;
}

function HoverTooltipTrigger(props: HoverTriggerProps) {
    const { setTriggerRef } = useFloatingContext();
    let hoverOpenTimeoutRef: ReturnType<typeof setTimeout> | null = null;
    let hoverCloseTimeoutRef: ReturnType<typeof setTimeout> | null = null;

    const clearTimeouts = () => {
        if (hoverOpenTimeoutRef) { clearTimeout(hoverOpenTimeoutRef); hoverOpenTimeoutRef = null; }
        if (hoverCloseTimeoutRef) { clearTimeout(hoverCloseTimeoutRef); hoverCloseTimeoutRef = null; }
    };

    const handleMouseEnter = () => {
        clearTimeouts();
        hoverOpenTimeoutRef = setTimeout(() => props.setIsOpen(true), props.delay);
    };

    const handleMouseLeave = () => {
        clearTimeouts();
        hoverCloseTimeoutRef = setTimeout(() => props.setIsOpen(false), 100);
    };

    onCleanup(clearTimeouts);

    return (
        <span ref={(el: HTMLElement) => setTriggerRef(el)} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} style={{ display: "inline-block" }}>
            {props.children}
        </span>
    );
}

interface FocusTooltipProps {
    content: JSXElement;
    placement: Placement;
    offset: number;
    children?: JSXElement;
}

export function FocusTooltip(props: FocusTooltipProps) {
    const [isOpen, setIsOpen] = createSignal(false);

    return (
        <FloatingRoot open={isOpen()} onOpenChange={setIsOpen} placement={props.placement ?? "top"} offset={props.offset ?? TOOLTIP_DEFAULT_OFFSET} flip={true} shift={true}>
            <FocusTooltipTrigger setIsOpen={setIsOpen}>
                {props.children}
            </FocusTooltipTrigger>
            <TooltipContentWithArrow placement={props.placement ?? "top"}>
                {props.content}
            </TooltipContentWithArrow>
        </FloatingRoot>
    );
}

interface FocusTriggerProps {
    setIsOpen: (v: boolean) => void;
    children?: JSXElement;
}

function FocusTooltipTrigger(props: FocusTriggerProps) {
    const { setTriggerRef } = useFloatingContext();

    return (
        <div ref={(el: HTMLElement) => setTriggerRef(el)} onFocusIn={() => props.setIsOpen(true)} onFocusOut={() => props.setIsOpen(false)}>
            {props.children}
        </div>
    );
}

interface TooltipContentProps {
    placement: string;
    setIsOpen?: (v: boolean) => void;
    onMouseEnter?: () => void;
    onMouseLeave?: () => void;
    children?: JSXElement;
}

function TooltipContentWithArrow(props: TooltipContentProps) {
    const { position } = useFloatingContext();
    let hoverCloseTimeoutRef: ReturnType<typeof setTimeout> | null = null;

    const handleMouseEnter = () => {
        if (hoverCloseTimeoutRef) { clearTimeout(hoverCloseTimeoutRef); hoverCloseTimeoutRef = null; }
        props.onMouseEnter?.();
    };

    const handleMouseLeave = () => {
        if (props.setIsOpen) {
            hoverCloseTimeoutRef = setTimeout(() => props.setIsOpen!(false), 50);
        }
        props.onMouseLeave?.();
    };

    onCleanup(() => {
        if (hoverCloseTimeoutRef) clearTimeout(hoverCloseTimeoutRef);
    });

    const actualPlacement = () => position()?.placement ?? props.placement;
    const basePlacement = () => actualPlacement().split("-")[0];

    return (
        <FloatingContent class={TOOLTIP_CLS} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
            {props.children}
            <div class={arrowCls(basePlacement())}/>
        </FloatingContent>
    );
}
