<script setup lang="ts">
/**
 * DashboardKpiRow — 3 KPI-карты (получено лидов / конверсия / активные проекты).
 * Numerics через JetBrains Mono с tabular-nums + count-up анимация (motion #1).
 *
 * Sprint 4 Phase B/3 — split DashboardView (audit O-refactor-04 закрытие).
 * Task 14 (Quiet Luxury) — добавлены ld-kpi__value/ld-kpi__label классы и
 * count-up через useCountUp композабл. Respects prefers-reduced-motion.
 */
import { onMounted, ref, watch, type Ref } from 'vue';
import { useCountUp } from '../../composables/useCountUp';

export interface Kpi {
    label: string;
    value: string;
    unit?: string;
    delta?: { dir: 'up' | 'down' | 'neutral'; text: string };
    sub: string;
    /** UX-аудит 25.06: пояснение для новичка по «?» рядом с подписью. */
    hint?: string;
}

const props = defineProps<{
    kpis: Kpi[];
}>();

/**
 * Парсит KPI value-строку в число. Поддерживает:
 * - целые ('247', '8')
 * - дробные ('18.4')
 * - с пробелами как тысячными ('14 250')
 */
function parseNumeric(raw: string): { value: number; precision: number } {
    const cleaned = raw.replace(/\s+/g, '').replace(',', '.');
    const value = parseFloat(cleaned);
    if (Number.isNaN(value)) return { value: 0, precision: 0 };
    const dotIdx = cleaned.indexOf('.');
    const precision = dotIdx === -1 ? 0 : cleaned.length - dotIdx - 1;
    return { value, precision };
}

/**
 * Форматирует число обратно с пробелами как тысячными
 * (чтобы '14 250' выводилось так же, а не '14250').
 */
function formatNumber(value: number, precision: number): string {
    const fixed = precision === 0 ? Math.round(value).toString() : value.toFixed(precision);
    const [intPart, decPart] = fixed.split('.');
    const withSpaces = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
    return decPart === undefined ? withSpaces : `${withSpaces}.${decPart}`;
}

interface AnimationSlot {
    target: Ref<number>;
    display: Ref<number>;
    start: () => void;
    precision: number;
}

const slots: AnimationSlot[] = [];

function rebuildSlots(): void {
    slots.length = 0;
    for (const kpi of props.kpis) {
        const { value, precision } = parseNumeric(kpi.value);
        const target = ref(value);
        const { display, start } = useCountUp(target, { duration: 600, precision });
        slots.push({ target, display, start, precision });
    }
}

rebuildSlots();

// Если props.kpis сменился (новый range / refetch) — пересобираем слоты
// и перезапускаем анимацию.
watch(
    () => props.kpis,
    () => {
        rebuildSlots();
        slots.forEach((s) => s.start());
    },
    { deep: true },
);

onMounted(() => {
    slots.forEach((s) => s.start());
});

function displayFor(idx: number): string {
    const slot = slots[idx];
    if (!slot) return '';
    return formatNumber(slot.display.value, slot.precision);
}
</script>

<template>
    <v-col v-for="(kpi, idx) in kpis" :key="kpi.label" cols="12" sm="6" md="3">
        <v-card variant="outlined" class="kpi-card pa-4">
            <div class="kpi-label ld-kpi__label ld-label text-body-2 text-medium-emphasis">
                {{ kpi.label }}
                <v-tooltip v-if="kpi.hint" :text="kpi.hint" location="top" max-width="260">
                    <template #activator="{ props: tip }">
                        <v-icon
                            v-bind="tip"
                            size="14"
                            class="kpi-hint ml-1"
                            icon="mdi-help-circle-outline"
                            :aria-label="kpi.hint"
                            tabindex="0"
                        />
                    </template>
                </v-tooltip>
            </div>
            <div class="kpi-value ld-kpi__value ld-mono">
                {{ displayFor(idx) }}
                <span v-if="kpi.unit" class="kpi-unit">{{ kpi.unit }}</span>
            </div>
            <div class="kpi-foot text-caption text-medium-emphasis mt-2">
                <span v-if="kpi.delta?.dir === 'up'" class="delta-up">
                    <v-icon size="14" color="success">mdi-arrow-up</v-icon>
                    {{ kpi.delta.text }}
                </span>
                <span v-else-if="kpi.delta?.dir === 'down'" class="delta-down">
                    <v-icon size="14" color="error">mdi-arrow-down</v-icon>
                    {{ kpi.delta.text }}
                </span>
                <span v-else-if="kpi.delta" class="delta-neutral">{{ kpi.delta.text }}</span>
                <span class="ml-1">{{ kpi.sub }}</span>
            </div>
        </v-card>
    </v-col>
</template>

<style scoped>
.kpi-card {
    background: #fff;
    border-color: #d9d5cd !important;
    transition: border-color 0.15s;
}
.kpi-card:hover {
    border-color: #66635c !important;
}

.kpi-label {
    font-size: 13px;
    margin-bottom: 8px;
}
.kpi-hint {
    color: #9b9484;
    cursor: help;
    vertical-align: middle;
}
.kpi-hint:hover {
    color: #0f6e56;
}

.kpi-value {
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-feature-settings: 'tnum';
    font-size: 32px;
    font-weight: 600;
    line-height: 1.1;
    color: #081319;
}

.kpi-unit {
    font-size: 18px;
    color: #66635c;
    font-weight: 500;
    margin-left: 2px;
}

.kpi-foot {
    display: flex;
    align-items: center;
    gap: 4px;
    flex-wrap: wrap;
}

.delta-up,
.delta-down,
.delta-neutral {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    font-weight: 500;
}
.delta-up {
    color: #1b6e3b;
}
.delta-down {
    color: #b83a3a;
}
</style>
