import { LineController, defaults } from "chart.js";
import numeral from "numeral";

export class MetricChart extends LineController {
  static id = "metric";
  static overrides = {
    plugins: {
      legend: {
        display: false,
      },
    },
    scales: {
      x: {
        display: false,
      },
      y: {
        display: false,
      },
    },
  };

  draw() {
    const meta = this.getDataset();
    const pt0 = meta.data[0];
    const ctx = this.chart.ctx;
    const height = this.chart.chartArea.height;
    const width = this.chart.chartArea.width;


  }
}

const drawChart = (ctx: CanvasRenderingContext2D, value: string, label: string, options: {
  left: number,
  top: number,
  height: number,
  width: number,
  align: "start" | "end" | "center",
  labelPosition: "vertical" | "horizontal",
  font?: {
    weight: string,
    size: number,
  }
}) => {

  // ctx.save();
  // ctx.clearRect(0, 0, options.width, options.height);

  // Draw the text
  ctx.fillStyle = defaults.color as string;
  let largest = 1;

  ctx.font = "16px " + defaults.font.family;
  const labelMetrics = ctx.measureText(label + "");
  let labelWidth = labelMetrics.width;
  let labelHeight = labelMetrics.actualBoundingBoxAscent + labelMetrics.actualBoundingBoxDescent;
  let labelVerticalOffset = 0;
  let labelHorizontalOffset = 0;

  let valueWidth = 0;
  let valueHeight = 0;

  let valueVerticalOffset = 0;
  let valueHorizontalOffset = 0;

  let descender = 0;

  if (options.font?.size) {
    largest = options.font.size;
    ctx.font = `${options.font?.weight || 600} ${largest}px ${defaults.font.family}`;
    const metrics = ctx.measureText(value + "");
    valueWidth = metrics.width;
    valueHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
    descender = metrics.actualBoundingBoxDescent;
  } else {
    ctx.font = `${options.font?.weight || 600} ${largest}px ${defaults.font.family}`;
    const metrics = ctx.measureText(value + "");
    valueWidth = metrics.width;
    valueHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
    descender = metrics.actualBoundingBoxDescent;

    let fullWidth = options.labelPosition === "horizontal" ? valueWidth + labelWidth : valueWidth;
    while (fullWidth < options.width && (valueHeight + 6) < options.height * 0.8) {
      largest += 1;
      ctx.font = `${options.font?.weight || 600} ${largest}px ${defaults.font.family}`;
      const metrics = ctx.measureText(value + "");
      valueWidth = metrics.width;
      valueHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
      fullWidth = options.labelPosition === "horizontal" ? valueWidth + labelWidth : valueWidth;
      descender = metrics.actualBoundingBoxDescent;
    };
  }


  ctx.textBaseline = "alphabetic";
  ctx.textAlign = "left";

  if (options.labelPosition === "horizontal") {
    valueVerticalOffset = options.top + options.height;
    labelVerticalOffset = options.top + options.height;
    if (options.align === "start") {
      valueHorizontalOffset = options.left;
      labelHorizontalOffset = options.left + valueWidth + 6;
    } else if (options.align === "center") {
      const totalWidth = valueWidth + labelWidth + 6;
      let start = ((options.width - totalWidth) / 2) + options.left;
      valueHorizontalOffset = start;
      labelHorizontalOffset = start + valueWidth + 6;
    } else if (options.align === "end") {
      valueHorizontalOffset = options.left + options.width - valueWidth - labelWidth - 6;
      labelHorizontalOffset = options.left + options.width - labelWidth;
    }
  } else if (options.labelPosition === "vertical") {
    valueVerticalOffset = options.top + options.height - 18 - descender;
    labelVerticalOffset = options.top + options.height;
    if (options.align === "start") {
      valueHorizontalOffset = options.left;
      labelHorizontalOffset = options.left;
    } else if (options.align === "center") {
      const totalWidth = valueWidth + labelWidth + 6;
      valueHorizontalOffset = options.left + (totalWidth / 2) - (valueWidth / 2);
      labelHorizontalOffset = options.left + (totalWidth / 2) - (labelWidth);
    } else if (options.align === "end") {
      valueHorizontalOffset = options.left + options.width - valueWidth;
      labelHorizontalOffset = options.left + options.width - labelWidth;
    }
  }


  labelHorizontalOffset = Math.max(options.left, labelHorizontalOffset);
  valueHorizontalOffset = Math.max(options.left, valueHorizontalOffset);

  labelHorizontalOffset = Math.min(options.left + options.width - labelWidth, labelHorizontalOffset);
  valueHorizontalOffset = Math.min(options.left + options.width - valueWidth, valueHorizontalOffset);


  ctx.fillText(value, valueHorizontalOffset, valueVerticalOffset, options.width);

  ctx.font = "16px " + defaults.font.family;
  ctx.fillText(label, labelHorizontalOffset, labelVerticalOffset, options.width);

  ctx.restore();
}

export const MetricPlugin = {
  id: "metric",
  afterDraw: (chart: any, args: any, options: any) => {
    const { ctx } = chart;
    if (chart.config.type !== "metric") return;

    const dataset = chart.data.datasets[0];

    // @ts-ignore
    const label = dataset.label || "";
    let raw = dataset.data[0];
    let value: string;
    if (typeof raw === "number") {
      value = numeral(raw).format("0,0.[00]");
    } else if (typeof raw === "string") {
      const s = raw.trim();
      // Only format if the entire string is numeric (allowing optional commas and decimals)
      const numericPattern = /^-?\d{1,3}(?:,\d{3})*(?:\.\d+)?$|^-?\d+(?:\.\d+)?$/;
      if (numericPattern.test(s)) {
        value = numeral(s).format("0,0.[00]");
      } else {
        value = raw;
      }
    } else {
      value = raw + "";
    }

    drawChart(ctx, value, label, {
      left: chart.chartArea.left,
      top: chart.chartArea.top,
      height: chart.chartArea.height,
      width: chart.chartArea.width,

      align: options.align || "start",
      labelPosition: options.labelPosition || "horizontal",
      font: options.font || undefined
    });
  },
};

export default MetricChart;