<template>
    <Teleport to="body">
        <div
            v-if="currentId !== null && currentTarget"
            class="dx-badge"
            :class="{ 'dx-badge--copied': justCopied }"
            :style="badgePosition"
            @click.stop="copyToClipboard"
        >
            <span class="dx-badge__num">#{{ currentId }}</span>
            <span class="dx-badge__meta">{{ tagLabel }} · "{{ textPreview }}"</span>
        </div>
    </Teleport>
    <Teleport to="body">
        <div v-if="overlayMode" class="dx-mini-layer">
            <div v-for="el in overlayElements" :key="el.id" class="dx-mini" :style="miniStyleFor(el.rect)">
                #{{ el.id }}
            </div>
        </div>
    </Teleport>
</template>

<script setup lang="ts">
import { computed, onMounted, onBeforeUnmount, ref, watch } from 'vue';
import { useDevIndices } from '../composables/useDevIndices';

const {
    currentId,
    currentTarget,
    hoverEnabled,
    overlayMode,
    setTarget,
    reset,
    pauseHover,
    walkToParent,
    walkToChild,
    toggleOverlay,
} = useDevIndices();

const cursorX = ref(0);
const cursorY = ref(0);
const justCopied = ref(false);
let mousemoveRAF: number | null = null;

const tagLabel = computed(() => {
    const t = currentTarget.value;
    if (!t) return '';
    return t.tagName.toLowerCase();
});

const textPreview = computed(() => {
    const t = currentTarget.value;
    if (!t) return '';
    const text = (t.textContent ?? '').trim().slice(0, 24);
    return text || '—';
});

const badgePosition = computed(() => ({
    left: `${cursorX.value + 12}px`,
    top: `${cursorY.value + 12}px`,
}));

interface OverlayItem {
    id: number;
    rect: DOMRect;
}

const overlayElements = ref<OverlayItem[]>([]);

function refreshOverlayElements() {
    const nodes = Array.from(document.querySelectorAll<HTMLElement>('[data-dx]'));
    overlayElements.value = nodes
        .map((el) => {
            const idAttr = el.getAttribute('data-dx');
            const id = Number(idAttr);
            if (!Number.isFinite(id)) return null;
            return { id, rect: el.getBoundingClientRect() };
        })
        .filter((x): x is OverlayItem => x !== null);
}

function miniStyleFor(rect: DOMRect) {
    return {
        left: `${rect.left}px`,
        top: `${rect.top}px`,
    };
}

watch(overlayMode, (on) => {
    if (on) {
        refreshOverlayElements();
        window.addEventListener('resize', refreshOverlayElements);
        window.addEventListener('scroll', refreshOverlayElements, true);
    } else {
        overlayElements.value = [];
        window.removeEventListener('resize', refreshOverlayElements);
        window.removeEventListener('scroll', refreshOverlayElements, true);
    }
});

function onMousemove(e: MouseEvent) {
    if (!hoverEnabled.value) return;
    cursorX.value = e.clientX;
    cursorY.value = e.clientY;

    if (mousemoveRAF !== null) return;
    mousemoveRAF = requestAnimationFrame(() => {
        mousemoveRAF = null;
        const el = document.elementFromPoint(e.clientX, e.clientY) as HTMLElement | null;
        if (!el) {
            setTarget(null);
            return;
        }
        const withDx = el.closest('[data-dx]') as HTMLElement | null;
        setTarget(withDx);
    });
}

function onKeydown(e: KeyboardEvent) {
    if (e.altKey && e.shiftKey && (e.key === 'I' || e.key === 'i')) {
        e.preventDefault();
        toggleOverlay();
        return;
    }
    if (e.altKey && e.key === 'ArrowUp') {
        e.preventDefault();
        walkToParent();
        pauseHover(800);
        return;
    }
    if (e.altKey && e.key === 'ArrowDown') {
        e.preventDefault();
        walkToChild();
        pauseHover(800);
        return;
    }
    if (e.key === 'Escape') {
        reset();
        pauseHover(2000);
    }
}

async function copyToClipboard() {
    if (currentId.value === null) return;
    try {
        await navigator.clipboard.writeText(`#${currentId.value}`);
        justCopied.value = true;
        setTimeout(() => (justCopied.value = false), 400);
    } catch {
        // clipboard may be unavailable in some contexts; silent fail OK in dev tool
    }
}

onMounted(() => {
    document.addEventListener('mousemove', onMousemove);
    document.addEventListener('keydown', onKeydown);
});

onBeforeUnmount(() => {
    document.removeEventListener('mousemove', onMousemove);
    document.removeEventListener('keydown', onKeydown);
    if (mousemoveRAF !== null) cancelAnimationFrame(mousemoveRAF);
    if (overlayMode.value) {
        window.removeEventListener('resize', refreshOverlayElements);
        window.removeEventListener('scroll', refreshOverlayElements, true);
    }
});
</script>

<style scoped>
.dx-badge {
    position: fixed;
    z-index: 999999;
    pointer-events: auto;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 4px 10px;
    background: #0f6e56;
    color: #fff;
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-size: 11px;
    line-height: 1.4;
    border-radius: 4px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.24);
    user-select: none;
    transition: background 120ms ease;
}
.dx-badge--copied {
    background: #21a16e;
}
.dx-badge__num {
    background: rgba(255, 255, 255, 0.22);
    padding: 1px 6px;
    border-radius: 3px;
    font-weight: 600;
}
.dx-badge__meta {
    letter-spacing: 0.02em;
    opacity: 0.92;
}
.dx-mini-layer {
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 999998;
}
.dx-mini {
    position: fixed;
    background: #0f6e56;
    color: #fff;
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-size: 9px;
    line-height: 1;
    padding: 1px 3px;
    border-radius: 2px;
    pointer-events: none;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
}
</style>
