import { css, CSSResult, html, LitElement, property, TemplateResult } from "lit-element";
import { Chart, Plugin, Tick, ChartConfiguration, ChartData, ChartOptions } from "chart.js";
import { Legend, Tooltip } from "chart.js";
import { DoughnutController, LineController, BarController } from "chart.js";
import { ArcElement, BarElement } from "chart.js";
import { TimeScale, LinearScale } from "chart.js";
import "chartjs-adapter-moment";
import { FONT_COLOUR_DARK_MODE, FONT_COLOUR_LIGHT_MODE } from "../../pages/flows/FlowDiagram";
import {EVENT_REFRESH} from "../../constants";

Chart.register(Legend, Tooltip);
Chart.register(LineController, BarController, DoughnutController);
Chart.register(ArcElement, BarElement);
Chart.register(TimeScale, LinearScale);

export abstract class AKChart<T> extends LitElement {

    abstract apiRequest(): Promise<T>;
    abstract getChartData(data: T): ChartData;

    chart?: Chart;

    @property()
    centerText?: string;

    fontColour = FONT_COLOUR_LIGHT_MODE;

    static get styles(): CSSResult[] {
        return [css`
            .container {
                height: 100%;
            }
            canvas {
                width: 100px;
                height: 100px;
            }
        `];
    }

    constructor() {
        super();
        window.addEventListener("resize", () => {
            if (this.chart) {
                this.chart.resize();
            }
        });
        window.addEventListener(EVENT_REFRESH, () => {
            this.apiRequest().then((r: T) => {
                if (!this.chart) return;
                this.chart.data = this.getChartData(r);
                this.chart.update();
            });
        });
        const matcher = window.matchMedia("(prefers-color-scheme: light)");
        const handler = (ev?: MediaQueryListEvent) => {
            if (ev?.matches || matcher.matches) {
                this.fontColour = FONT_COLOUR_LIGHT_MODE;
            } else {
                this.fontColour = FONT_COLOUR_DARK_MODE;
            }
            this.chart?.update();
        };
        matcher.addEventListener("change", handler);
        handler();
    }

    firstUpdated(): void {
        this.apiRequest().then((r) => {
            const canvas = <HTMLCanvasElement>this.shadowRoot?.querySelector("canvas");
            if (!canvas) {
                console.warn("Failed to get canvas element");
                return false;
            }
            const ctx = canvas.getContext("2d");
            if (!ctx) {
                console.warn("failed to get 2d context");
                return false;
            }
            this.chart = this.configureChart(r, ctx);
        });
    }

    getChartType(): string {
        return "bar";
    }

    getPlugins(): Plugin[] {
        return [
            {
                id: "center-text",
                beforeDraw: (chart) => {
                    if (!chart.ctx) return;
                    if (!this.centerText) return;
                    const width = chart.width || 0;
                    const height = chart.height || 0;

                    const fontSize = (height / 114).toFixed(2);
                    chart.ctx.font = `${fontSize}em Overpass`;
                    chart.ctx.textBaseline = "middle";
                    chart.ctx.fillStyle = this.fontColour;

                    const textX = Math.round((width - chart.ctx.measureText(this.centerText).width) / 2);
                    const textY = height / 2;

                    chart.ctx.fillText(this.centerText, textX, textY);
                }
            }
        ];
    }

    getOptions(): ChartOptions {
        return {
            maintainAspectRatio: false,
            scales: {
                x: {
                    type: "time",
                    display: true,
                    ticks: {
                        callback: function (tickValue: string | number, index: number, ticks: Tick[]): string {
                            const valueStamp = (ticks[index]);
                            const delta = Date.now() - valueStamp.value;
                            const ago = Math.round(delta / 1000 / 3600);
                            return `${ago} Hours ago`;
                        },
                        autoSkip: true,
                        maxTicksLimit: 8,
                    },
                    stacked: true,
                    grid: {
                        color: "rgba(0, 0, 0, 0)",
                    },
                    offset: true
                },
                y: {
                    type: "linear",
                    display: true,
                    stacked: true,
                    grid: {
                        color: "rgba(0, 0, 0, 0)",
                    },
                }
            },
        } as ChartOptions;
    }

    configureChart(data: T, ctx: CanvasRenderingContext2D): Chart {
        const config = {
            type: this.getChartType(),
            data: this.getChartData(data),
            options: this.getOptions(),
            plugins: this.getPlugins(),
        };
        return new Chart(ctx, config as ChartConfiguration);
    }

    render(): TemplateResult {
        return html`
            <div class="container">
                <canvas></canvas>
            </div>
        `;
    }
}
