<script setup lang="ts">
import { computed, nextTick, ref } from 'vue'
import VueApexCharts from "vue3-apexcharts"

const props = defineProps<{
    title: string
    series: { name: string; data: number[] }[]
    categories: string[]
    footerValue?: string | number
    footerLabel?: string
    maxVisible?: number
    barHeight?: number
    colors?: string[]
    options?: object
    limit?: number
}>()

const BAR_HEIGHT  = computed(() => props.barHeight ?? 32)
const MAX_VISIBLE = computed(() => props.maxVisible ?? 12)

// ── Limit / Show More ─────────────────────────────────────────────────────────

const showAll     = ref(false)
const isExpanding = ref(false)

async function expand() {
    isExpanding.value = true
    await nextTick() // paint "Loading..." before blocking on chart render
    showAll.value = true
    await nextTick() // wait for ApexCharts to finish
    isExpanding.value = false
}

function collapse() {
    showAll.value = false
}

const visibleCategories = computed(() =>
    props.limit && !showAll.value
        ? props.categories.slice(0, props.limit)
        : props.categories
)

const visibleSeries = computed(() =>
    props.limit && !showAll.value
        ? props.series.map(s => ({ ...s, data: s.data.slice(0, props.limit) }))
        : props.series
)

const hasMore = computed(() =>
    !!props.limit && props.categories.length > props.limit
)

const hiddenCount = computed(() =>
    props.limit ? props.categories.length - props.limit : 0
)

// ── Chart dimensions ──────────────────────────────────────────────────────────

const chartHeight = computed(() => visibleCategories.value.length * BAR_HEIGHT.value + 40)
const scrollHeight = computed(() => {
    const visible = Math.min(visibleCategories.value.length, MAX_VISIBLE.value)
    return `${visible * BAR_HEIGHT.value + 40}px`
})

// ── Tooltip state ─────────────────────────────────────────────────────────────

const tooltipVisible = ref(false)
const tooltipX       = ref(0)
const tooltipY       = ref(0)
const hoveredIndex   = ref<number | null>(null)

function onMouseMove(e: MouseEvent) {
    tooltipX.value = e.clientX + 14
    tooltipY.value = e.clientY + 14
}

function onMouseLeave() {
    tooltipVisible.value = false
    hoveredIndex.value   = null
}

// ── Chart options ─────────────────────────────────────────────────────────────

const chartOptions = computed(() => ({
    chart: {
        type: 'bar',
        background: 'transparent',
        fontFamily: 'inherit',
        toolbar: { show: false },
        animations: { enabled: false },
        stacked: true,
        animation: { enabled: false },
        events: {
            dataPointMouseEnter: (_e: unknown, _ctx: unknown, config: { dataPointIndex: number }) => {
                tooltipVisible.value = true
                hoveredIndex.value   = config.dataPointIndex
            },
            dataPointMouseLeave: () => {
                tooltipVisible.value = false
                hoveredIndex.value   = null
            },
        },
    },
    theme: { mode: 'dark' },
    plotOptions: {
        bar: {
            horizontal: true,
            barHeight: '60%',
            borderRadius: 3,
            borderRadiusWhenStacked: 'last',
        },
    },
    colors: props.colors ?? ['#4da6ff', '#ffc107'],
    dataLabels: { enabled: false },
    xaxis: {
        categories: visibleCategories.value,
        labels: { show: false },
        axisBorder: { show: false },
        axisTicks: { show: false },
    },
    yaxis: {
        labels: { style: { colors: '#778899', fontSize: '12px' } },
    },
    grid: { show: false },
    legend: {
        show: true,
        position: 'top',
        horizontalAlign: 'right',
        labels: { colors: '#778899' },
        markers: { width: 8, height: 8, radius: 2 },
        itemMargin: { horizontal: 8 },
    },
    tooltip: { enabled: false },
    states: {
        hover: { filter: { type: 'lighten', value: 0.1 } },
    },
}))
</script>

<template>
    <div class="chart-wrap">
        <div class="chart-title">{{ title }}</div>

        <div v-if="categories.length" class="chart-outer">
            <div
                class="chart-scroll"
                :style="{ height: scrollHeight }"
                @mousemove="onMouseMove"
                @mouseleave="onMouseLeave"
            >
                <VueApexCharts
                    type="bar"
                    :height="chartHeight"
                    :options="options ?? chartOptions"
                    :series="visibleSeries"
                />
            </div>

            <slot
                name="tooltip"
                :visible="tooltipVisible"
                :x="tooltipX"
                :y="tooltipY"
                :index="hoveredIndex"
            />

            <div v-if="hasMore || (limit && showAll)" class="show-more-wrap">
                <button
                    v-if="!showAll"
                    class="show-more"
                    :disabled="isExpanding"
                    :class="{ loading: isExpanding }"
                    @click="expand"
                >
                    <span v-if="isExpanding" class="spinner" />
                    <span>{{ isExpanding ? 'Loading…' : `Show ${hiddenCount} more` }}</span>
                </button>
                <button v-else class="show-more" @click="collapse">
                    Show less
                </button>
            </div>

            <div v-if="footerValue !== undefined" class="chart-footer">
                <span class="total-count">{{ footerValue }}</span>
                <span class="total-label">{{ footerLabel }}</span>
            </div>
        </div>

        <div v-else class="chart-empty">No data available</div>
    </div>
</template>

<style scoped>
.chart-wrap {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.chart-title {
    font-size: 13px;
    font-weight: 500;
    color: #556677;
    text-transform: uppercase;
    letter-spacing: 0.08em;
}

.chart-outer {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.chart-scroll {
    overflow-y: auto;
    overflow-x: hidden;
    scrollbar-width: thin;
    scrollbar-color: #334455 transparent;
}

.chart-scroll::-webkit-scrollbar { width: 4px; }
.chart-scroll::-webkit-scrollbar-track { background: transparent; }
.chart-scroll::-webkit-scrollbar-thumb { background: #334455; border-radius: 2px; }

.show-more-wrap {
    display: flex;
    padding-left: 2px;
}

.show-more {
    display: flex;
    align-items: center;
    gap: 6px;
    background: none;
    border: 1px solid #334455;
    border-radius: 4px;
    color: #778899;
    cursor: pointer;
    font-size: 12px;
    padding: 4px 10px;
    transition: color 0.15s, border-color 0.15s;
}

.show-more:hover:not(:disabled) {
    border-color: #4da6ff;
    color: #4da6ff;
}

.show-more:disabled {
    cursor: default;
    opacity: 0.6;
}

.spinner {
    width: 10px;
    height: 10px;
    border: 1.5px solid #556677;
    border-top-color: #4da6ff;
    border-radius: 50%;
    animation: spin 0.6s linear infinite;
    flex-shrink: 0;
}

@keyframes spin {
    to { transform: rotate(360deg); }
}

.chart-footer {
    display: flex;
    align-items: baseline;
    gap: 6px;
    padding-left: 4px;
}

.total-count {
    font-size: 28px;
    font-weight: 600;
    color: #e0e6f0;
    line-height: 1;
}

.total-label {
    font-size: 13px;
    color: #556677;
}

.chart-empty {
    height: 280px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    color: #445566;
}

:deep(svg) {
    outline: none;
}
</style>
