import { createEffect, onCleanup, onMount } from "solid-js";
import { Chart, registerables } from "chart.js";

// chart.js v4 is tree-shakeable and ships nothing registered by default; register
// all controllers/elements/scales once so any chart type works (the old UMD shim
// did this implicitly).
Chart.register(...registerables);

type ChartType = "line" | "bar" | "radar" | "doughnut" | "polarArea" | "bubble" | "pie" | "scatter";

interface ReactiveChartProps {
    type: ChartType;
    data: unknown;
    options?: object;
    class?: string;
}

interface ChartInstance {
    destroy(): void;
    update(): void;
    data: unknown;
    options: object;
}

type ChartCtor = new (ctx: CanvasRenderingContext2D, cfg: object) => ChartInstance;

export default function ReactiveChart(props: ReactiveChartProps) {
    let canvasRef: HTMLCanvasElement | undefined;
    let chartInstance: ChartInstance | null = null;

    onMount(() => {
        // Defer initialization until the canvas is connected to the document.
        // @solidjs/router creates route components before inserting them into
        // the DOM, and Chart.js needs getComputedStyle which requires a
        // connected element with ownerDocument.defaultView.
        const init = () => {
            if (!canvasRef) return;
            if (!canvasRef.isConnected) {
                requestAnimationFrame(init);
                return;
            }

            const ctx = canvasRef.getContext("2d");
            if (!ctx) return;

            chartInstance = new (Chart as unknown as ChartCtor)(ctx, {
                type: props.type,
                data: props.data,
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    ...(props.options ?? {}),
                },
            });
        };
        init();
    });

    onCleanup(() => {
        if (chartInstance) {
            chartInstance.destroy();
            chartInstance = null;
        }
    });

    createEffect(() => {
        const data = props.data;
        if (chartInstance) {
            chartInstance.data = data;
            chartInstance.update();
        }
    });

    createEffect(() => {
        const options = props.options;
        if (chartInstance) {
            chartInstance.options = {
                responsive: true,
                maintainAspectRatio: false,
                ...(options ?? {}),
            };
            chartInstance.update();
        }
    });

    return (
        <div class={"h-full " + (props.class || "")}>
            <canvas ref={(el: HTMLCanvasElement) => canvasRef = el}></canvas>
        </div>
    );
}
