// Heavy chart bundle; split out so it loads only when the dashboard tab is
// actually rendered. chart.js + react-chartjs-2 are ~70KB gzipped together.

import React from 'react';
import {
    Chart as ChartJS,
    CategoryScale,
    LinearScale,
    PointElement,
    LineElement,
    Filler,
    Tooltip,
    Legend,
    type ChartOptions,
    type ChartData,
} from 'chart.js';
import { Line } from 'react-chartjs-2';
import { formatBytes, formatPps } from './firewallFormat';

ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Filler, Tooltip, Legend);

export type FirewallChartBucket = {
    windowStart: number;
    packets_in: string;
    packets_blocked: string;
    packets_passed: string;
};

/** Matches dashboard resolution ids — used only for axis label density. */
export type FirewallChartResolution = '1m' | '1h' | '6h' | '24h' | '7d' | '30d';

/** Evenly spaced tick indices so the x-axis is readable (Chart.js autoSkip on categories is uneven). */
function buildCustomTickIndices(bucketCount: number, resolution: FirewallChartResolution): number[] {
    if (bucketCount <= 0) return [];
    if (bucketCount === 1) return [0];
    const maxTicks =
        resolution === '30d' || resolution === '7d'
            ? 9
            : resolution === '24h' || resolution === '6h'
              ? 8
              : resolution === '1h'
                ? 10
                : resolution === '1m'
                  ? 7
                  : 8;
    const maxT = Math.min(maxTicks, bucketCount);
    const out: number[] = [];
    for (let t = 0; t < maxT; t++) {
        const i = Math.round((t / (maxT - 1)) * (bucketCount - 1));
        out.push(i);
    }
    return [...new Set(out)].sort((a, b) => a - b);
}

/** Human-readable tick at `ts` — short for wide windows so labels do not overlap. */
function formatXTick(ts: number, resolution: FirewallChartResolution): string {
    const d = new Date(ts);
    const now = new Date();
    const useYear = d.getFullYear() !== now.getFullYear();

    if (resolution === '30d' || resolution === '7d') {
        return d.toLocaleDateString(undefined, {
            month: 'short',
            day: 'numeric',
            ...(useYear ? { year: '2-digit' } : {}),
        });
    }
    if (resolution === '24h' || resolution === '6h') {
        return d.toLocaleString(undefined, {
            month: 'short',
            day: 'numeric',
            hour: 'numeric',
            minute: resolution === '24h' ? '2-digit' : undefined,
        });
    }
    if (resolution === '1h') {
        return d.toLocaleString(undefined, { month: 'numeric', day: 'numeric', hour: '2-digit', minute: '2-digit' });
    }
    return d.toLocaleString(undefined, {
        month: 'numeric',
        day: 'numeric',
        hour: '2-digit',
        minute: '2-digit',
        second: '2-digit',
    });
}

function tooltipTitle(ts: number): string {
    return new Date(ts).toLocaleString(undefined, { dateStyle: 'medium', timeStyle: 'short' });
}

function maxSeriesValue(buckets: FirewallChartBucket[]): number {
    let m = 0;
    for (const b of buckets) {
        m = Math.max(m, Number(b.packets_in), Number(b.packets_passed), Number(b.packets_blocked));
    }
    return m > 0 ? m : 0;
}

function chartOptions(
    unit: 'pps' | 'bps',
    buckets: FirewallChartBucket[],
    resolution: FirewallChartResolution,
): ChartOptions<'line'> {
    const maxY = maxSeriesValue(buckets);
    const smallScale = maxY < 20;

    return {
        responsive: true,
        maintainAspectRatio: false,
        interaction: { mode: 'index', intersect: false },
        layout: {
            padding: { top: 4, left: 0, right: 8, bottom: 2 },
        },
        plugins: {
            legend: {
                display: true,
                position: 'bottom',
                labels: {
                    color: '#a3a3a3',
                    font: { size: 11, weight: '500' },
                    usePointStyle: true,
                    boxWidth: 8,
                },
            },
            tooltip: {
                backgroundColor: '#374151',
                borderColor: '#9ca3af',
                borderWidth: 1,
                titleColor: '#f9fafb',
                bodyColor: '#e5e7eb',
                callbacks: {
                    title: (items) => {
                        const i = items[0]?.dataIndex;
                        if (i == null || !buckets[i]) return '';
                        return tooltipTitle(buckets[i].windowStart);
                    },
                    label: (ctx) => {
                        const label = ctx.dataset.label ?? '';
                        const v = ctx.parsed.y ?? 0;
                        const formatted = unit === 'pps' ? formatPps(v) : formatBytes(v);
                        return `${label}: ${formatted}`;
                    },
                },
            },
        },
        scales: {
            x: {
                grid: { display: false },
                afterBuildTicks: (scale) => {
                    const n = buckets.length;
                    const idxs = buildCustomTickIndices(n, resolution);
                    scale.ticks = idxs.map((i) => ({ value: String(i) })) as typeof scale.ticks;
                },
                ticks: {
                    color: '#9ca3af',
                    font: { size: resolution === '30d' || resolution === '7d' ? 10 : 11 },
                    maxRotation: 0,
                    minRotation: 0,
                    autoSkip: false,
                    callback: (tickValue) => {
                        const i = Number(tickValue);
                        if (!Number.isFinite(i) || i < 0 || i >= buckets.length) return '';
                        return formatXTick(buckets[i].windowStart, resolution);
                    },
                },
            },
            y: {
                beginAtZero: true,
                grid: { color: 'rgba(128,128,128,0.15)' },
                ticks: {
                    color: '#6b7280',
                    maxTicksLimit: 8,
                    callback: (raw) => {
                        const v = Number(raw);
                        if (unit === 'bps') return formatBytes(v);
                        if (smallScale && !Number.isInteger(v)) return `${v.toFixed(2)} PPS`;
                        return formatPps(v);
                    },
                },
            },
        },
        elements: { point: { radius: 0, hitRadius: 12 }, line: { tension: 0.25 } },
    };
}

function buildChartData(
    buckets: FirewallChartBucket[],
    fillAreas: boolean,
    _resolution: FirewallChartResolution,
): ChartData<'line'> {
    // Category labels are indices (strings); real dates only on custom x ticks — avoids 120 long strings fighting autoSkip.
    const labels = buckets.map((_, i) => String(i));
    const mk = (label: string, data: number[], border: string, fillBg: string) => ({
        label,
        data,
        borderColor: border,
        backgroundColor: fillAreas ? fillBg : 'transparent',
        fill: fillAreas,
    });
    return {
        labels,
        datasets: [
            mk('Total inbound', buckets.map((b) => Number(b.packets_in)), '#60a5fa', 'rgba(96,165,250,0.12)'),
            mk('Passed', buckets.map((b) => Number(b.packets_passed)), '#34d399', 'rgba(52,211,153,0.12)'),
            mk('Blocked', buckets.map((b) => Number(b.packets_blocked)), '#f87171', 'rgba(248,113,113,0.25)'),
        ],
    };
}

type Props = {
    buckets: FirewallChartBucket[];
    unit?: 'pps' | 'bps';
    /** When false, match “Line mini charts” / stats-only mode — lines only, no solid fills. */
    fillAreas?: boolean;
    /** Controls x-axis tick density (wider windows → more date in each tick). */
    resolution?: FirewallChartResolution;
};

const FirewallChart: React.FC<Props> = ({ buckets, unit = 'pps', fillAreas = true, resolution = '24h' }) => {
    const data = React.useMemo(
        () => buildChartData(buckets, fillAreas, resolution),
        [buckets, fillAreas, resolution],
    );
    const options = React.useMemo(() => chartOptions(unit, buckets, resolution), [unit, buckets, resolution]);
    return <Line data={data} options={options} />;
};

export default FirewallChart;
