<script setup>
// ContributionSparklines.vue - Three sparkline curves (sessions, messages, cost)
// rendered from daily activity data over 365 days, using the exact same SVG style
// as ActivitySparkline.vue. Each curve is independently scaled to its own max value,
// filling the full height of its graph area.
// Supports a "combined" mode that overlays all three curves in a single SVG
// with distinct colors (green for messages, blue for sessions, red for cost).

import { ref, computed, watch } from 'vue'
import { useSettingsStore } from '../stores/settings'

const settingsStore = useSettingsStore()
const isTouchDevice = computed(() => settingsStore.isTouchDevice)

const props = defineProps({
    /** Daily activity data: array of { date, user_message_count, session_count, cost } */
    dailyActivity: {
        type: Array,
        required: true,
    },
    /** Whether to display all three curves combined in a single SVG */
    combined: {
        type: Boolean,
        default: false,
    },
    /** Number of most recent days to display (default: 365 = full year) */
    days: {
        type: Number,
        default: 365,
    },
    /** Granularity: 'day' | 'week' | 'month' | 'quarter' */
    granularity: {
        type: String,
        default: 'day',
    },
})

const DAYS = 365
const SVG_HEIGHT = 150
const GRAPH_HEIGHT = 146
const VIEWBOX_HEIGHT = 150 // viewBox goes from 0 to VIEWBOX_HEIGHT, graph draws within 0..SVG_HEIGHT
const MIN_Y = 1.0

/**
 * Build a full 365-day array from sparse daily activity data.
 * Days with no data get 0 values. The array covers [today - 364 days, today].
 */
const dailyMap = computed(() => {
    const map = new Map()
    for (const d of props.dailyActivity) {
        map.set(d.date, d)
    }
    return map
})

const fullYearData = computed(() => {
    const today = new Date()
    today.setHours(0, 0, 0, 0)
    const result = []
    for (let i = DAYS - 1; i >= 0; i--) {
        const d = new Date(today)
        d.setDate(d.getDate() - i)
        const dateStr = toDateStr(d)
        const entry = dailyMap.value.get(dateStr)
        result.push({
            date: dateStr,
            sessions: entry ? entry.session_count || 0 : 0,
            messages: entry ? entry.user_message_count || 0 : 0,
            cost: entry ? (parseFloat(entry.cost) || 0) : 0,
        })
    }
    return result
})

function toDateStr(date) {
    const y = date.getFullYear()
    const m = String(date.getMonth() + 1).padStart(2, '0')
    const d = String(date.getDate()).padStart(2, '0')
    return `${y}-${m}-${d}`
}

// ── Aggregation helpers ─────────────────────────────────────────────

function parseDateStr(dateStr) {
    const [y, m, d] = dateStr.split('-').map(Number)
    return new Date(y, m - 1, d)
}

/** Return ISO week key "YYYY-Www" for a date (weeks start Monday). */
function getISOWeekKey(date) {
    const d = new Date(date)
    d.setDate(d.getDate() + 3 - ((d.getDay() + 6) % 7))
    const yearStart = new Date(d.getFullYear(), 0, 4)
    const weekNo = Math.ceil(((d - yearStart) / 86400000 + 1) / 7)
    return `${d.getFullYear()}-W${String(weekNo).padStart(2, '0')}`
}

/** Format a week range like "Jan 6–12, 2026" or "Dec 30–Jan 5, 2026" (cross-month). */
function formatWeekRange(startStr, endStr) {
    const start = parseDateStr(startStr)
    const end = parseDateStr(endStr)
    const sameMonth = start.getMonth() === end.getMonth() && start.getFullYear() === end.getFullYear()
    if (sameMonth) {
        return `${start.toLocaleDateString('en-US', { month: 'short' })} ${start.getDate()}\u2013${end.getDate()}, ${end.getFullYear()}`
    }
    return `${start.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}\u2013${end.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}`
}

/** Format a month key "YYYY-MM" as "January 2026". */
function formatMonthLabel(monthKey) {
    const [y, m] = monthKey.split('-').map(Number)
    const date = new Date(y, m - 1, 1)
    return date.toLocaleDateString('en-US', { month: 'long', year: 'numeric' })
}

/**
 * Aggregate daily data by ISO week (Monday–Sunday).
 * Drops the first group if its first day is not a Monday (incomplete week at data start).
 */
function aggregateByWeek(days) {
    if (!days.length) return []
    const groups = []
    let currentGroup = null

    for (const d of days) {
        const date = parseDateStr(d.date)
        const weekKey = getISOWeekKey(date)

        if (!currentGroup || currentGroup.weekKey !== weekKey) {
            currentGroup = {
                weekKey,
                startDate: d.date,
                endDate: d.date,
                sessions: 0,
                messages: 0,
                cost: 0,
            }
            groups.push(currentGroup)
        }

        currentGroup.endDate = d.date
        currentGroup.sessions += d.sessions
        currentGroup.messages += d.messages
        currentGroup.cost += d.cost
    }

    // Drop first group if incomplete (doesn't start on Monday)
    if (groups.length > 0) {
        const firstDay = parseDateStr(groups[0].startDate)
        const dayOfWeek = firstDay.getDay() // 0=Sun, 1=Mon
        if (dayOfWeek !== 1) {
            groups.shift()
        }
    }

    return groups.map(g => ({
        sessions: g.sessions,
        messages: g.messages,
        cost: g.cost,
        dateLabel: formatWeekRange(g.startDate, g.endDate),
    }))
}

/**
 * Aggregate daily data by calendar month.
 * Drops the first group if its first day is not the 1st (incomplete month at data start).
 */
function aggregateByMonth(days) {
    if (!days.length) return []
    const groups = []
    let currentGroup = null

    for (const d of days) {
        const monthKey = d.date.substring(0, 7) // "YYYY-MM"

        if (!currentGroup || currentGroup.monthKey !== monthKey) {
            currentGroup = {
                monthKey,
                firstDayOfMonth: d.date,
                sessions: 0,
                messages: 0,
                cost: 0,
            }
            groups.push(currentGroup)
        }

        currentGroup.sessions += d.sessions
        currentGroup.messages += d.messages
        currentGroup.cost += d.cost
    }

    // Drop first group if incomplete (doesn't start on 1st)
    if (groups.length > 0) {
        const firstDate = parseDateStr(groups[0].firstDayOfMonth)
        if (firstDate.getDate() !== 1) {
            groups.shift()
        }
    }

    return groups.map(g => ({
        sessions: g.sessions,
        messages: g.messages,
        cost: g.cost,
        dateLabel: formatMonthLabel(g.monthKey),
    }))
}

/**
 * Aggregate daily data by quarter (Q1=Jan-Mar, Q2=Apr-Jun, Q3=Jul-Sep, Q4=Oct-Dec).
 * Drops the first group if its first day is not the 1st day of its quarter.
 */
function aggregateByQuarter(days) {
    if (!days.length) return []
    const groups = []
    let currentGroup = null

    for (const d of days) {
        const [y, m] = d.date.split('-').map(Number)
        const q = Math.ceil(m / 3)
        const qKey = `${y}-Q${q}`

        if (!currentGroup || currentGroup.qKey !== qKey) {
            currentGroup = {
                qKey,
                year: y,
                quarter: q,
                firstDayOfQuarter: d.date,
                sessions: 0,
                messages: 0,
                cost: 0,
            }
            groups.push(currentGroup)
        }

        currentGroup.sessions += d.sessions
        currentGroup.messages += d.messages
        currentGroup.cost += d.cost
    }

    // Drop first group if incomplete (doesn't start on 1st day of its quarter)
    if (groups.length > 0) {
        const g0 = groups[0]
        const quarterStartMonth = (g0.quarter - 1) * 3 + 1 // 1, 4, 7, or 10
        const firstDate = parseDateStr(g0.firstDayOfQuarter)
        if (firstDate.getMonth() + 1 !== quarterStartMonth || firstDate.getDate() !== 1) {
            groups.shift()
        }
    }

    return groups.map(g => ({
        sessions: g.sessions,
        messages: g.messages,
        cost: g.cost,
        dateLabel: `Q${g.quarter} ${g.year}`,
    }))
}

// ── Displayed data: slice then aggregate ────────────────────────────

const slicedData = computed(() => {
    const data = fullYearData.value
    if (props.days >= DAYS) return data
    return data.slice(data.length - props.days)
})

const displayData = computed(() => {
    const data = slicedData.value
    switch (props.granularity) {
        case 'week': return aggregateByWeek(data)
        case 'month': return aggregateByMonth(data)
        case 'quarter': return aggregateByQuarter(data)
        default: return data.map(d => ({ ...d, dateLabel: formatTooltipDate(d.date) }))
    }
})

/** Label suffix that adapts to the selected granularity */
const periodLabel = computed(() => ({
    day: 'per day',
    week: 'per week',
    month: 'per month',
    quarter: 'per quarter',
}[props.granularity] || 'per day'))

// SVG width is always based on 365 days to keep a consistent stroke thickness
// regardless of the displayed time range or granularity (avoids stroke scaling
// with preserveAspectRatio="none")
const svgWidth = computed(() => DAYS * 3 - 1)

/**
 * Build polyline points for a given data array with its max value.
 * Each curve fills the full graph height based on its own maximum.
 */
function buildPolylinePoints(values, maxValue, graphHeight) {
    if (!values.length) return ''
    const xStep = values.length > 1 ? svgWidth.value / (values.length - 1) : 0
    return values
        .map((count, i) => {
            const x = Math.round(i * xStep * 100) / 100
            const y = maxValue > 0
                ? MIN_Y + (count / maxValue) * (graphHeight - MIN_Y)
                : MIN_Y
            return `${x},${Math.round(y * 100) / 100}`
        })
        .join(' ')
}

// Extract arrays for each metric from the displayed window
const sessionsValues = computed(() => displayData.value.map(d => d.sessions))
const messagesValues = computed(() => displayData.value.map(d => d.messages))
const costValues = computed(() => displayData.value.map(d => d.cost))

// Max values for each metric (each graph is independently scaled)
const sessionsMax = computed(() => Math.max(...sessionsValues.value))
const messagesMax = computed(() => Math.max(...messagesValues.value))
const costMax = computed(() => Math.max(...costValues.value))

// Build polyline points for each metric
const sessionsPoints = computed(() => buildPolylinePoints(sessionsValues.value, sessionsMax.value, GRAPH_HEIGHT))
const messagesPoints = computed(() => buildPolylinePoints(messagesValues.value, messagesMax.value, GRAPH_HEIGHT))
const costPoints = computed(() => buildPolylinePoints(costValues.value, costMax.value, GRAPH_HEIGHT))

// Separate curves config (one SVG per metric, each with its own color)
const separateCurves = computed(() => [
    {
        key: 'sessions',
        label: `Sessions created ${periodLabel.value}`,
        points: sessionsPoints.value,
        gradientId: 'sparkline-contrib-sessions-gradient',
        maskId: 'sparkline-contrib-sessions-mask',
        colorPrefix: 'blue',
    },
    {
        key: 'messages',
        label: `Message turns ${periodLabel.value}`,
        points: messagesPoints.value,
        gradientId: 'sparkline-contrib-messages-gradient',
        maskId: 'sparkline-contrib-messages-mask',
        colorPrefix: 'green',
    },
    {
        key: 'cost',
        label: `Cost ${periodLabel.value}`,
        points: costPoints.value,
        gradientId: 'sparkline-contrib-cost-gradient',
        maskId: 'sparkline-contrib-cost-mask',
        colorPrefix: 'red',
    },
])

// Combined curves config (all three overlaid in one SVG, distinct colors)
const combinedCurves = computed(() => [
    {
        key: 'sessions',
        label: 'Sessions',
        points: sessionsPoints.value,
        gradientId: 'sparkline-combined-sessions-gradient',
        maskId: 'sparkline-combined-sessions-mask',
        colorPrefix: 'blue',
    },
    {
        key: 'messages',
        label: 'Message turns',
        points: messagesPoints.value,
        gradientId: 'sparkline-combined-messages-gradient',
        maskId: 'sparkline-combined-messages-mask',
        colorPrefix: 'green',
    },
    {
        key: 'cost',
        label: 'Cost',
        points: costPoints.value,
        gradientId: 'sparkline-combined-cost-gradient',
        maskId: 'sparkline-combined-cost-mask',
        colorPrefix: 'red',
    },
])

/**
 * Return CSS variable names for gradient stops and stroke based on color prefix.
 * 'green' uses the default sparkline vars, 'blue' and 'red' use the new dedicated vars.
 */
function colorVars(prefix) {
    if (prefix === 'green') {
        return {
            g1: 'var(--sparkline-project-gradient-color-1)',
            g2: 'var(--sparkline-project-gradient-color-2)',
            g3: 'var(--sparkline-project-gradient-color-3)',
            g4: 'var(--sparkline-project-gradient-color-4)',
            stroke: 'var(--sparkline-project-stroke-color)',
        }
    }
    return {
        g1: `var(--sparkline-${prefix}-gradient-color-1)`,
        g2: `var(--sparkline-${prefix}-gradient-color-2)`,
        g3: `var(--sparkline-${prefix}-gradient-color-3)`,
        g4: `var(--sparkline-${prefix}-gradient-color-4)`,
        stroke: `var(--sparkline-${prefix}-stroke-color)`,
    }
}

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

/** Index of the hovered data point in displayData, or null when not hovering */
const hoveredIndex = ref(null)

// Reset hover when the displayed data changes (e.g. range slider moves)
watch(displayData, () => { hoveredIndex.value = null })

/**
 * Map a mouse event to the closest data-point index.
 * Works because points are evenly distributed and preserveAspectRatio="none"
 * linearly maps the SVG viewBox to the rendered CSS width.
 */
function onSvgMouseMove(event) {
    if (isTouchDevice.value) return
    const svg = event.currentTarget
    const rect = svg.getBoundingClientRect()
    const ratio = (event.clientX - rect.left) / rect.width
    const count = displayData.value.length
    hoveredIndex.value = Math.max(0, Math.min(count - 1, Math.round(ratio * (count - 1))))
}

function onSvgMouseLeave() {
    hoveredIndex.value = null
}

/** SVG x-coordinate for the vertical cursor line */
const cursorSvgX = computed(() => {
    if (hoveredIndex.value === null) return 0
    const count = displayData.value.length
    if (count <= 1) return 0
    return (hoveredIndex.value / (count - 1)) * svgWidth.value
})

/** Horizontal position of the tooltip as a percentage (0–100) */
const tooltipLeftPct = computed(() => {
    if (hoveredIndex.value === null) return 0
    const count = displayData.value.length
    if (count <= 1) return 0
    return (hoveredIndex.value / (count - 1)) * 100
})

/** The data entry at the hovered index */
const hoveredData = computed(() => {
    if (hoveredIndex.value === null) return null
    return displayData.value[hoveredIndex.value]
})

/**
 * Format a "YYYY-MM-DD" date string as "Jan 1, 2026".
 * Same convention as ContributionGraph.vue tooltipFormatter.
 */
function formatTooltipDate(dateStr) {
    const [y, m, d] = dateStr.split('-').map(Number)
    const date = new Date(y, m - 1, d)
    return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })
}

/** Format a metric value for display in the tooltip */
function formatMetricValue(key, value) {
    if (key === 'cost') {
        return value === 0 ? 'No cost' : `$${value.toFixed(2)}`
    }
    if (key === 'sessions') {
        return `${value} ${value === 1 ? 'session' : 'sessions'}`
    }
    return `${value} ${value === 1 ? 'message turn' : 'message turns'}`
}

/**
 * Computed averages for the hovered data point.
 * - avgTurnsPerSession: messages / sessions
 * - avgCostPerSession: cost / sessions
 * - avgCostPerTurn: cost / messages
 */
const hoveredAverages = computed(() => {
    const d = hoveredData.value
    if (!d) return null
    const { sessions, messages, cost } = d
    return {
        avgTurnsPerSession: sessions > 0 ? messages / sessions : null,
        avgCostPerSession: sessions > 0 ? cost / sessions : null,
        avgCostPerTurn: messages > 0 ? cost / messages : null,
    }
})

function formatAverage(value, isCost) {
    if (value === null) return '–'
    if (isCost) return `$${value.toFixed(2)}`
    return value.toFixed(1)
}
</script>

<template>
    <div v-if="dailyActivity.length > 0" class="contribution-sparklines">

        <!-- Combined mode: single SVG with all three curves overlaid -->
        <template v-if="combined">
            <div class="sparkline-row">
                <h3 class="sparkline-title">Activity {{ periodLabel }}</h3>
                <div class="sparkline-graph-wrapper">
                    <svg
                        :width="svgWidth"
                        aria-hidden="true"
                        :height="SVG_HEIGHT"
                        class="contribution-sparkline"
                        :viewBox="`0 0 ${svgWidth} ${VIEWBOX_HEIGHT}`"
                        preserveAspectRatio="none"
                        @mousemove="onSvgMouseMove"
                        @mouseleave="onSvgMouseLeave"
                    >
                        <defs>
                            <template v-for="curve in combinedCurves" :key="curve.key">
                                <linearGradient :id="curve.gradientId" x1="0" x2="0" y1="1" y2="0">
                                    <stop offset="0%" :stop-color="colorVars(curve.colorPrefix).g1"></stop>
                                    <stop offset="10%" :stop-color="colorVars(curve.colorPrefix).g2"></stop>
                                    <stop offset="25%" :stop-color="colorVars(curve.colorPrefix).g3"></stop>
                                    <stop offset="50%" :stop-color="colorVars(curve.colorPrefix).g4"></stop>
                                </linearGradient>
                                <mask :id="curve.maskId" x="0" y="0" :width="svgWidth" :height="GRAPH_HEIGHT">
                                    <polyline
                                        :transform="`translate(0, ${GRAPH_HEIGHT}) scale(1,-1)`"
                                        :points="curve.points"
                                        fill="transparent"
                                        :stroke="colorVars(curve.colorPrefix).stroke"
                                        stroke-width="2"
                                    ></polyline>
                                </mask>
                            </template>
                        </defs>

                        <g transform="translate(0, 2.0)">
                            <rect
                                v-for="curve in combinedCurves"
                                :key="curve.key"
                                x="0"
                                y="-2"
                                :width="svgWidth"
                                :height="GRAPH_HEIGHT + 2"
                                :style="`stroke: none; fill: url(#${curve.gradientId}); mask: url(#${curve.maskId});`"
                            ></rect>
                        </g>

                        <!-- Vertical cursor line at hovered point -->
                        <line
                            v-if="hoveredIndex !== null"
                            :x1="cursorSvgX" :x2="cursorSvgX"
                            y1="0" :y2="VIEWBOX_HEIGHT"
                            class="sparkline-cursor-line"
                            stroke="var(--wa-color-text-quiet)"
                            stroke-width="1"
                            stroke-dasharray="4 3"
                            vector-effect="non-scaling-stroke"
                        />
                    </svg>

                    <!-- Hover tooltip (combined: shows all three metrics + averages) -->
                    <div
                        v-if="hoveredIndex !== null && hoveredData && hoveredAverages && !isTouchDevice"
                        class="sparkline-tooltip"
                        :style="{ left: `${tooltipLeftPct}%`, transform: `translateX(-${tooltipLeftPct}%)` }"
                    >
                        <div v-for="c in combinedCurves" :key="c.key" class="sparkline-tooltip-row">
                            <span class="sparkline-tooltip-swatch" :class="`sparkline-tooltip-swatch--${c.colorPrefix}`"></span>
                            <span>{{ formatMetricValue(c.key, hoveredData[c.key]) }}</span>
                        </div>
                        <div class="sparkline-tooltip-separator"></div>
                        <div class="sparkline-tooltip-avg">Avg. turns/session: {{ formatAverage(hoveredAverages.avgTurnsPerSession, false) }}</div>
                        <div class="sparkline-tooltip-avg">Avg. cost/session: {{ formatAverage(hoveredAverages.avgCostPerSession, true) }}</div>
                        <div class="sparkline-tooltip-avg">Avg. cost/turn: {{ formatAverage(hoveredAverages.avgCostPerTurn, true) }}</div>
                        <div class="sparkline-tooltip-date">{{ hoveredData.dateLabel }}</div>
                    </div>
                </div>

                <!-- Legend -->
                <div class="sparkline-legend">
                    <div v-for="curve in combinedCurves" :key="curve.key" class="sparkline-legend-item">
                        <span class="sparkline-legend-swatch" :class="`sparkline-legend-swatch--${curve.colorPrefix}`"></span>
                        <span class="sparkline-legend-text">{{ curve.label }}</span>
                    </div>
                </div>
            </div>
        </template>

        <!-- Separate mode: one SVG per metric (all green) -->
        <template v-else>
            <div v-for="curve in separateCurves" :key="curve.key" class="sparkline-row">
                <h3 class="sparkline-title">{{ curve.label }}</h3>
                <div class="sparkline-graph-wrapper">
                    <svg
                        :width="svgWidth"
                        aria-hidden="true"
                        :height="SVG_HEIGHT"
                        class="contribution-sparkline"
                        :viewBox="`0 0 ${svgWidth} ${VIEWBOX_HEIGHT}`"
                        preserveAspectRatio="none"
                        @mousemove="onSvgMouseMove"
                        @mouseleave="onSvgMouseLeave"
                    >
                        <defs>
                            <linearGradient :id="curve.gradientId" x1="0" x2="0" y1="1" y2="0">
                                <stop offset="0%" :stop-color="colorVars(curve.colorPrefix).g1"></stop>
                                <stop offset="10%" :stop-color="colorVars(curve.colorPrefix).g2"></stop>
                                <stop offset="25%" :stop-color="colorVars(curve.colorPrefix).g3"></stop>
                                <stop offset="50%" :stop-color="colorVars(curve.colorPrefix).g4"></stop>
                            </linearGradient>
                            <mask :id="curve.maskId" x="0" y="0" :width="svgWidth" :height="GRAPH_HEIGHT">
                                <polyline
                                    :transform="`translate(0, ${GRAPH_HEIGHT}) scale(1,-1)`"
                                    :points="curve.points"
                                    fill="transparent"
                                    :stroke="colorVars(curve.colorPrefix).stroke"
                                    stroke-width="2"
                                ></polyline>
                            </mask>
                        </defs>

                        <g transform="translate(0, 2.0)">
                            <rect
                                x="0"
                                y="-2"
                                :width="svgWidth"
                                :height="GRAPH_HEIGHT + 2"
                                :style="`stroke: none; fill: url(#${curve.gradientId}); mask: url(#${curve.maskId});`"
                            ></rect>
                        </g>

                        <!-- Vertical cursor line at hovered point -->
                        <line
                            v-if="hoveredIndex !== null"
                            :x1="cursorSvgX" :x2="cursorSvgX"
                            y1="0" :y2="VIEWBOX_HEIGHT"
                            class="sparkline-cursor-line"
                            stroke="var(--wa-color-text-quiet)"
                            stroke-width="1"
                            stroke-dasharray="4 3"
                            vector-effect="non-scaling-stroke"
                        />
                    </svg>

                    <!-- Hover tooltip (separate: single metric + relevant averages) -->
                    <div
                        v-if="hoveredIndex !== null && hoveredData && hoveredAverages && !isTouchDevice"
                        class="sparkline-tooltip"
                        :style="{ left: `${tooltipLeftPct}%`, transform: `translateX(-${tooltipLeftPct}%)` }"
                    >
                        <div class="sparkline-tooltip-value">{{ formatMetricValue(curve.key, hoveredData[curve.key]) }}</div>
                        <div class="sparkline-tooltip-separator"></div>
                        <template v-if="curve.key === 'sessions'">
                            <div class="sparkline-tooltip-avg">Avg. turns/session: {{ formatAverage(hoveredAverages.avgTurnsPerSession, false) }}</div>
                            <div class="sparkline-tooltip-avg">Avg. cost/session: {{ formatAverage(hoveredAverages.avgCostPerSession, true) }}</div>
                        </template>
                        <template v-else-if="curve.key === 'messages'">
                            <div class="sparkline-tooltip-avg">Avg. turns/session: {{ formatAverage(hoveredAverages.avgTurnsPerSession, false) }}</div>
                            <div class="sparkline-tooltip-avg">Avg. cost/turn: {{ formatAverage(hoveredAverages.avgCostPerTurn, true) }}</div>
                        </template>
                        <template v-else-if="curve.key === 'cost'">
                            <div class="sparkline-tooltip-avg">Avg. cost/session: {{ formatAverage(hoveredAverages.avgCostPerSession, true) }}</div>
                            <div class="sparkline-tooltip-avg">Avg. cost/turn: {{ formatAverage(hoveredAverages.avgCostPerTurn, true) }}</div>
                        </template>
                        <div class="sparkline-tooltip-date">{{ hoveredData.dateLabel }}</div>
                    </div>
                </div>
            </div>
        </template>

    </div>
    <div v-else class="no-data">
        No activity data
    </div>
</template>

<style scoped>
.contribution-sparklines {
    display: flex;
    flex-direction: column;
    gap: var(--wa-space-l);
    padding-inline: var(--wa-space-m);
    max-width: 80rem;
    margin-inline: auto;
    width: 100%;
}

.sparkline-row {
    display: flex;
    flex-direction: column;
    align-items: center;
}

.sparkline-graph-wrapper {
    position: relative;
    width: 100%;
}

.sparkline-title {
    margin-block: var(--wa-space-l) var(--wa-space-s);
    padding-inline: var(--wa-space-m);
    color: var(--wa-color-text-quiet);
    font-size: var(--wa-font-size-l);
}

.contribution-sparkline {
    display: block;
    width: 100%;
    height: 150px;
}

/* Legend */
.sparkline-legend {
    display: flex;
    justify-content: center;
    gap: var(--wa-space-l);
    margin-top: var(--wa-space-s);
}

.sparkline-legend-item {
    display: flex;
    align-items: center;
    gap: var(--wa-space-xs);
}

.sparkline-legend-swatch {
    display: inline-block;
    width: 12px;
    height: 3px;
    border-radius: 1px;
}

.sparkline-legend-swatch--green {
    background: var(--sparkline-project-gradient-color-3);
}

.sparkline-legend-swatch--blue {
    background: var(--sparkline-blue-gradient-color-3);
}

.sparkline-legend-swatch--red {
    background: var(--sparkline-red-gradient-color-3);
}

.sparkline-legend-text {
    font-size: var(--wa-font-size-xs);
    color: var(--wa-color-text-quiet);
}

/* Hover tooltip */
.sparkline-tooltip {
    position: absolute;
    bottom: 100%;
    margin-bottom: var(--wa-space-2xs);
    pointer-events: none;
    z-index: 10;
    background: var(--wa-color-surface-raised);
    border-radius: var(--wa-border-radius-m);
    padding: var(--wa-space-xs) var(--wa-space-s);
    font-size: var(--wa-font-size-s);
    color: var(--wa-color-text-normal);
    white-space: nowrap;
    box-shadow: var(--wa-shadow-s);
}

.sparkline-tooltip-separator {
    border-top: 1px solid var(--wa-color-text-quiet);
    margin-block: var(--wa-space-3xs);
}

.sparkline-tooltip-avg {
    color: var(--wa-color-text-quiet);
    font-size: var(--wa-font-size-s);
}

.sparkline-tooltip-date {
    color: var(--wa-color-text-quiet);
    font-size: var(--wa-font-size-xs);
    margin-top: var(--wa-space-3xs);
}

.sparkline-tooltip-row {
    display: flex;
    align-items: center;
    gap: var(--wa-space-2xs);
}

.sparkline-tooltip-swatch {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
}

.sparkline-tooltip-swatch--green {
    background: var(--sparkline-project-gradient-color-3);
}

.sparkline-tooltip-swatch--blue {
    background: var(--sparkline-blue-gradient-color-3);
}

.sparkline-tooltip-swatch--red {
    background: var(--sparkline-red-gradient-color-3);
}

/* Vertical cursor line inside SVG */
.sparkline-cursor-line {
    pointer-events: none;
}

.no-data {
    font-size: var(--wa-font-size-s);
    color: var(--wa-color-text-quiet);
    text-align: center;
    padding: var(--wa-space-m);
}
</style>
