import { h, qsa } from "@/util.ts";
import { type CSSProperties, defineComponent, onBeforeUnmount, onMounted, ref } from "vue";

const carrierStyle = {
    opacity: "0",
    display: "block",
    pointerEvents: "none",
    backgroundColor: "black",
    border: "white solid 1px",
    color: "white",
    padding: "10px",
    position: "absolute",
    zIndex: "1",
    overflow: "hidden",
    height: "0",
    width: "0",
    transition: "opacity 200ms, height 200ms, width 200ms",
} satisfies CSSProperties;

const textCarrierStyle = {
    width: "350px",
    display: "block",
    overflow: "hidden",
} satisfies CSSProperties;

const defaultWidth = 350;

export default defineComponent({
    name: "dj-sexy-tooltip",
    setup() {
        const active = ref(false);

        const carrier = ref<HTMLElement | null>(null);
        const textCarrier = ref<HTMLElement | null>(null);

        onMounted(() => {
            document.body.appendChild(h("style", { textContent: `.tooltip { display: none; }` }));
            document.body.style.position = "relative";

            document.addEventListener("mousemove", (event) => this.rerenderTooltip({ x: event.pageX, y: event.pageY }));
        });

        function rerenderTooltip(mousePos: { x: number; y: number }) {
            const newText = this.getTooltipTextForHoveredElement(mousePos);
            if (newText !== this.textCarrier) {
                this.transitionTooltipSize(newText);
                const tooltipHasText = newText !== "";
                if (tooltipHasText !== this.active) {
                    this.toggleActive();
                }
            }
            if (this.isStillVisible()) {
                this.updatePosition(mousePos);
            }
        }

        function isStillVisible() {
            return getComputedStyle(carrier.value).opacity !== "0";
        }

        function updatePosition(pos: { x: number; y: number }) {
            if (pos.x + 15 + this.carrier.clientWidth <= document.body.scrollWidth) {
                this.carrier.style.left = (pos.x + 15) + "px";
            } else {
                this.carrier.style.left = (document.body.scrollWidth - this.carrier.clientWidth - 5) + "px";
            }
            if (pos.y + this.carrier.clientHeight <= document.body.scrollHeight) {
                this.carrier.style.top = pos.y + "px";
            } else {
                this.carrier.style.top = (document.body.scrollHeight - this.carrier.clientHeight - 5) + "px";
            }
        }

        function toggleActive() {
            if (this.active) {
                this.active = false;
                this.carrier.style.opacity = "0";
                this.carrier.style.padding = "0";
            } else {
                this.active = true;
                this.carrier.style.opacity = "1";
                this.carrier.style.padding = SexyTooltip.carrierStyle.padding;
            }
        }

        function transitionTooltipSize(text: string) {
            const testDiv = h("div");
            testDiv.style.height = "auto";
            testDiv.style.width = SexyTooltip.defaultWidth + "px";
            testDiv.innerHTML = text;
            document.body.appendChild(testDiv);
            const calculatedHeight = testDiv.scrollHeight;
            testDiv.style.display = "inline";
            testDiv.style.width = "auto";
            document.body.removeChild(testDiv);
            const size = { height: calculatedHeight + "px", width: "350px" };
            this.carrier.style.height = size.height;
            this.carrier.style.width = text === "" ? "0" : size.width;
            this.textCarrier.innerHTML = text;
        }

        function getTooltipTextForHoveredElement(mousePos: { x: number; y: number }) {
            for (const elem of this.elementsWithTooltips) {
                const boundingRect = elem.getBoundingClientRect();
                const inYRange = mousePos.y >= boundingRect.top + window.pageYOffset - 1 &&
                    mousePos.y <= boundingRect.bottom + window.pageYOffset + 1;
                const inXRange = mousePos.x >= boundingRect.left + window.pageXOffset - 1 &&
                    mousePos.x <= boundingRect.right + window.pageXOffset + 1;
                if (inYRange && inXRange) {
                    return elem.nextElementSibling?.innerText ?? "";
                }
            }
            return "";
        }

        return () => (
            <div ref={carrier}>
                <span ref={textCarrier} />
            </div>
        );
    },
});
