import * as FloatingUI from "@floating-ui/dom";
import { cancelPropagation, dom, svg, text, type Class, type NodeChildren } from "./dom.util";
import { button } from "./proses";

export interface ContextProperties
{
    placement?: FloatingUI.Placement;
    offset?: number;
    arrow?: boolean;
    class?: Class;
}
export interface PopperProperties
{
    placement?: FloatingUI.Placement;
    offset?: number;
    arrow?: boolean;
    class?: Class;
    content?: NodeChildren;
    delay?: number;

    onShow?: (element: HTMLDivElement) => boolean | void;
    onHide?: (element: HTMLDivElement) => boolean | void;
}

export interface ModalProperties
{
    priority?: boolean;
    closeWhenOutside?: boolean;
}

let teleport: HTMLDivElement;
export function init()
{
    teleport = dom('div', { attributes: { id: 'popper-container' }, class: 'absolute top-0 left-0' });
    document.body.appendChild(teleport);
}

export function popper(container: HTMLElement, properties?: PopperProperties): HTMLElement
{
    let shown = false, timeout: Timer;
    const arrow = svg('svg', { class: 'absolute fill-light-35 dark:fill-dark-35', attributes: { width: "10", height: "7", viewBox: "0 0 30 10" } }, [svg('polygon', { attributes: { points: "0,0 30,0 15,10" } })]);
    const content = dom('div', { class: ['fixed hidden', properties?.class], attributes: { 'data-state': 'closed' } }, [...(properties?.content ?? []), arrow]);

    function update()
    {
        FloatingUI.computePosition(container, content, {
            placement: properties?.placement,
            strategy: 'fixed',
            middleware: [
                properties?.offset ? FloatingUI.offset(properties?.offset) : undefined,
                FloatingUI.flip(),
                properties?.offset ? FloatingUI.shift({ padding: properties?.offset }) : undefined,
                properties?.offset && properties?.arrow ? FloatingUI.arrow({ element: arrow, padding: 8 }) : undefined,
            ]
        }).then(({ x, y, placement, middlewareData }) => {
            Object.assign(content.style, {
                left: `${x}px`,
                top: `${y}px`,
            });

            const side = placement.split('-')[0] as FloatingUI.Side;
            
            content.setAttribute('data-side', side);
            
            if(properties?.offset && properties?.arrow)
            {
                const { x: arrowX, y: arrowY } = middlewareData.arrow!;
        
                const staticSide = {
                    top: 'bottom',
                    right: 'left',
                    bottom: 'top',
                    left: 'right',
                  }[side]!;

                  const rotation = {
                    top: "0",
                    bottom: "180",
                    left: "270",
                    right: "90"
                  }[side]!;
                 
                  Object.assign(arrow.style, {
                    left: arrowX != null ? `${arrowX}px` : '',
                    top: arrowY != null ? `${arrowY}px` : '',
                    right: '',
                    bottom: '',
                    [staticSide]: `-6px`,
                    transform: `rotate(${rotation}deg)`,
                  });
            }
        });
    }

    let stop: () => void | undefined;
    function show()
    {
        if(shown || !properties?.onShow || properties?.onShow(content) !== false)
        {
            clearTimeout(timeout);
    
            timeout = setTimeout(() => {
                if(!shown)
                {
                    teleport!.appendChild(content);
                    
                    content.setAttribute('data-state', 'open');
                    content.classList.toggle('hidden', false);

                    update();
                    stop = FloatingUI.autoUpdate(container, content, update, {
                        animationFrame: true,
                        layoutShift: false,
                        elementResize: false,
                        ancestorScroll: false,
                        ancestorResize: false,
                    });
                }
                shown = true;
            }, properties?.delay ?? 0);
        }
    }
    
    function hide()
    {
        if(!properties?.onHide || properties?.onHide(content) !== false)
        {
            clearTimeout(timeout);

            timeout = setTimeout(() => {
                content.remove();
                stop && stop();

                shown = false;
            }, shown ? properties?.delay ?? 0 : 0);
        }
    }
    
    function link(element: HTMLElement) {
        Object.entries({
            'mouseenter': show,
            'mouseleave': hide,
            'focus': show,
            'blur': hide,
        } as Record<keyof HTMLElementEventMap, () => void>).forEach(([event, listener]) => {
            element.addEventListener(event, listener);
        });
    }

    link(container);
    link(content);

    return container;
}
export function contextmenu(x: number, y: number, content: NodeChildren, properties?: ContextProperties): () => void
{
    const virtual = {
        getBoundingClientRect() {
            return {
                x: x,
                y: y,
                top: y,
                left: x,
                bottom: y,
                right: x,
                width: 0,
                height: 0,
            };
        },
    };

    const arrow = svg('svg', { class: 'absolute fill-light-35 dark:fill-dark-35', attributes: { width: "10", height: "7", viewBox: "0 0 30 10" } }, [svg('polygon', { attributes: { points: "0,0 30,0 15,10" } })]);
    const container = dom('div', { class: ['fixed bg-light-20 dark:bg-dark-20 border border-light-35 dark:border-dark-35 z-50', properties?.class] }, content);

    function update()
    {
        FloatingUI.computePosition(virtual, container, {
            placement: properties?.placement,
            strategy: 'fixed',
            middleware: [
                properties?.offset ? FloatingUI.offset(properties?.offset) : undefined,
                FloatingUI.flip(),
                properties?.offset ? FloatingUI.shift({ padding: properties?.offset }) : undefined,
                properties?.offset && properties?.arrow ? FloatingUI.arrow({ element: arrow, padding: 8 }) : undefined,
            ]
        }).then(({ x, y, placement, middlewareData }) => {
            Object.assign(container.style, {
                left: `${x}px`,
                top: `${y}px`,
            });

            const side = placement.split('-')[0] as FloatingUI.Side;
            
            container.setAttribute('data-side', side);
            
            if(properties?.offset && properties?.arrow)
            {
                const { x: arrowX, y: arrowY } = middlewareData.arrow!;
        
                const staticSide = {
                    top: 'bottom',
                    right: 'left',
                    bottom: 'top',
                    left: 'right',
                  }[side]!;

                  const rotation = {
                    top: "0",
                    bottom: "180",
                    left: "270",
                    right: "90"
                  }[side]!;
                 
                  Object.assign(arrow.style, {
                    left: arrowX != null ? `${arrowX}px` : '',
                    top: arrowY != null ? `${arrowY}px` : '',
                    right: '',
                    bottom: '',
                    [staticSide]: `-6px`,
                    transform: `rotate(${rotation}deg)`,
                  });
            }
        });
    }
    update();

    document.addEventListener('mousedown', close);
    container.addEventListener('mousedown', cancelPropagation);

    const stop = FloatingUI.autoUpdate(virtual, container, update, {
        animationFrame: true,
        layoutShift: false,
        elementResize: false,
        ancestorScroll: false,
        ancestorResize: false,
    });
    teleport!.appendChild(container);

    function close()
    {
        document.removeEventListener('mousedown', close);
        container.removeEventListener('mousedown', cancelPropagation);

        container.remove();
        stop();
    }

    return close;
}

export function modal(content: NodeChildren, properties?: ModalProperties)
{
    const _modalBlocker = dom('div', { class: [' absolute top-0 left-0 bottom-0 right-0 z-0', { 'bg-light-0 dark:bg-dark-0 opacity-70': properties?.priority ?? false }], listeners: { click: properties?.closeWhenOutside ? (() => _modal.remove()) : undefined } });
    const _closer = properties?.priority ? undefined : dom('span', { class: 'absolute top-4 right-4', text: '×', listeners: { click: () => _modal.remove() } });
    const _modal = dom('div', { class: 'fixed flex justify-center items-center top-0 left-0 bottom-0 right-0 inset-0 z-40' }, [ _modalBlocker, dom('div', { class: 'max-h-[85vh] max-w-[450px] bg-light-10 dark:bg-dark-10 border border-light-30 dark:border-dark-30 p-6 text-light-100 dark:text-dark-100 z-10 relative' }, content)])

    teleport.appendChild(_modal);

    return {
        close: () => _modal.remove(),
    }
}

export function confirm(title: string): Promise<boolean>
{
    return new Promise(res => {
        const mod = modal([ dom('div', { class: 'flex flex-col justify-start gap-4' }, [ dom('div', { class: 'text-xl' }, [ text(title) ]), dom('div', { class: 'flex flex-row gap-2' }, [ button(text("Non"), () => (mod.close(), res(false)), 'h-[35px] px-[15px]'), button(text("Oui"), () => (mod.close(), res(true)), 'h-[35px] px-[15px] !border-light-red dark:!border-dark-red text-light:red dark:text-dark-red') ]) ]) ], {
            priority: true,
            closeWhenOutside: false,
        });
    })
}