import { unsafeCSS, LitElement, html } from "lit";
import { property, state, query } from "lit/decorators.js";
import componentStyle from "./donut-chart.css.js";
import chartCommonStyle from "../../charthelpers/chart-common.css.js";
import chartDebugStyle from "../../charthelpers/chart-debug.css.js";
import chartLegendStyle from "../../charthelpers/chart-legend.css.js";
import { customElement } from "../../decorator.js";
import { Priority } from "../../navigation-instruments/types.js";
import { Chart, DoughnutController, ArcElement, Tooltip } from "chart.js";
import { CHART_DIMENSIONS, CHART_SECTOR_ENHANCED_COLORS, CHART_SECTOR_DEFAULT_COLORS, OUTER_LABEL_CONFIG, CENTER_READOUT_CONFIG } from "../../charthelpers/constants.js";
import { getChartColorsOrDefault, getCssVariableValue } from "../../charthelpers/colors.js";
import { observeThemeChanges } from "../../charthelpers/theme.js";
import { calculateSumTotal, createArcOuterLabelPlugin } from "../../charthelpers/outer-labels.js";
import { calculateFixedHeightChartLayout, formatNumericValue } from "../../charthelpers/canvas-layout.js";
import { getChartTooltipOptions } from "../../charthelpers/tooltip.js";
import { generateLegendHTML } from "../../charthelpers/legend.js";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __decorateClass = (decorators, target, key, kind) => {
  var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
  for (var i = decorators.length - 1, decorator; i >= 0; i--)
    if (decorator = decorators[i])
      result = (kind ? decorator(target, key, result) : decorator(result)) || result;
  if (kind && result) __defProp(target, key, result);
  return result;
};
Chart.register(DoughnutController, ArcElement, Tooltip);
const DONUT_DIMENSIONS = {
  ...CHART_DIMENSIONS,
  GAP_WIDTH: 0,
  GAP_AND_BORDER_COLOR: "--container-section-color",
  REMAINING_COLOR: "--border-outline-color",
  BORDER_RADIUS: 4
};
const DONUT_RECREATE_PROP_NAMES = [
  "half",
  "showOuterLabels",
  "showUnit",
  "outerLabelUnit",
  "outerLabelMaxLength",
  "outerLabelDecimalPlaces",
  "showDebugOverlay",
  "fixedHeight"
];
const DONUT_WATCHED_PROP_NAMES = [
  "legend",
  "data",
  "colors",
  "priority",
  "half",
  "thickness",
  "max",
  "showOuterLabels",
  "showUnit",
  "centerReadoutLabel",
  "centerReadoutUnit",
  "outerLabelUnit",
  "outerLabelMaxLength",
  "outerLabelDecimalPlaces",
  "showDebugOverlay",
  "fixedHeight"
];
let ObcDonutChart = class extends LitElement {
  constructor() {
    super(...arguments);
    this.data = [];
    this.colors = [];
    this.priority = Priority.regular;
    this.half = false;
    this.showOuterLabels = false;
    this.showUnit = false;
    this.outerLabelUnit = "%";
    this.outerLabelMaxLength = 0;
    this.outerLabelDecimalPlaces = 0;
    this.centerReadoutLabel = "Total";
    this.centerReadoutUnit = "%";
    this.max = 100;
    this.thickness = 24;
    this.legend = false;
    this.showDebugOverlay = false;
    this.fixedHeight = 320;
    this.total = 0;
    this.formattedLabels = [];
    this.wasAboveThreshold = false;
  }
  hasAnyChanged(changed, props) {
    return props.some((prop) => changed.has(prop));
  }
  /**
   * Get layout configuration for canvas calculation
   */
  getLayoutConfig() {
    const denominator = this.max > 0 ? this.max : this.total > 0 ? this.total : 1;
    return {
      unit: this.outerLabelUnit,
      showUnit: this.showUnit,
      outerLabelMaxLength: this.outerLabelMaxLength,
      decimalPlaces: this.outerLabelDecimalPlaces,
      data: this.data,
      denominator
    };
  }
  // Calculate derived state BEFORE render
  willUpdate(changed) {
    if (changed.has("data")) {
      this.total = calculateSumTotal(this.data);
    }
  }
  // Update external library AFTER render
  updated(changed) {
    super.updated(changed);
    if (this.hasAnyChanged(changed, DONUT_WATCHED_PROP_NAMES)) {
      const needsRecreation = this.hasAnyChanged(
        changed,
        DONUT_RECREATE_PROP_NAMES
      );
      if (needsRecreation) {
        this.chart?.destroy();
        this.createChart();
      } else {
        this.updateChart();
      }
    }
  }
  firstUpdated() {
    this.createChart();
    this.themeObserver = observeThemeChanges(() => this.updateChartColors());
    this.setupResizeObserver();
  }
  disconnectedCallback() {
    super.disconnectedCallback();
    this.chart?.destroy();
    this.themeObserver?.disconnect();
    this.resizeObserver?.disconnect();
  }
  /**
   * Setup resize observer to detect height threshold crossings
   * Recreates chart when crossing MIN_HEIGHT_WITH_LABELS (192px) to show/hide labels
   * Detect when fixedHeight property changes programmatically (e.g., via Storybook controls or user code)
   */
  setupResizeObserver() {
    if (!this.canvasEl) return;
    this.resizeObserver = new ResizeObserver(() => {
      if (!this.chart) return;
      const height2 = this.canvasEl?.clientHeight ?? 0;
      const isAboveThreshold = height2 >= CHART_DIMENSIONS.MIN_HEIGHT_WITH_LABELS;
      if (isAboveThreshold !== this.wasAboveThreshold) {
        this.wasAboveThreshold = isAboveThreshold;
        this.chart.destroy();
        this.createChart();
      } else {
        this.updateChart();
      }
    });
    this.resizeObserver.observe(this.canvasEl);
    const height = this.canvasEl.clientHeight;
    this.wasAboveThreshold = height >= CHART_DIMENSIONS.MIN_HEIGHT_WITH_LABELS;
  }
  prepareChartData() {
    const values = this.data.map((d) => d.value);
    const labels = this.data.map((d) => d.label);
    const remaining = Math.max(0, this.max - this.total);
    const chartColors = getChartColorsOrDefault(
      this,
      this.colors,
      this.priority === Priority.enhanced ? CHART_SECTOR_ENHANCED_COLORS : CHART_SECTOR_DEFAULT_COLORS
    );
    const segmentColors = values.map(
      (_, index) => chartColors[index % chartColors.length] ?? getCssVariableValue(this, DONUT_DIMENSIONS.REMAINING_COLOR)
    );
    return {
      values: [...values, remaining],
      labels: [...labels, "Remaining"],
      colors: [
        ...segmentColors,
        getCssVariableValue(this, DONUT_DIMENSIONS.REMAINING_COLOR)
      ]
    };
  }
  /**
   * Build a dataset configuration object shared by both createChart and updateChart
   */
  createDatasetConfig(values, colors) {
    return {
      data: values,
      backgroundColor: colors,
      borderWidth: 1,
      borderColor: getCssVariableValue(
        this,
        DONUT_DIMENSIONS.GAP_AND_BORDER_COLOR
      ),
      borderRadius: this.getBorderRadius(values),
      spacing: DONUT_DIMENSIONS.GAP_WIDTH
    };
  }
  /**
   * Get Chart.js options using centralized fixed height layout calculation
   */
  getChartOptions() {
    const dimensions = calculateFixedHeightChartLayout({
      fixedHeight: this.fixedHeight,
      isHalfMode: this.half,
      showOuterLabels: this.showOuterLabels,
      canvasEl: this.canvasEl,
      layoutConfig: this.getLayoutConfig(),
      host: this
    });
    this.formattedLabels = dimensions.formattedLabels;
    this.style.setProperty("--chart-width", `${dimensions.calculatedWidth}px`);
    this.style.setProperty("--chart-height", `${dimensions.actualHeight}px`);
    const dynamicPadding = dimensions.isTooSmall ? { top: 0, right: 0, bottom: 0, left: 0 } : {
      top: dimensions.topPadding,
      right: dimensions.rightPadding,
      bottom: dimensions.bottomPadding,
      left: dimensions.leftPadding
    };
    return {
      responsive: true,
      maintainAspectRatio: true,
      aspectRatio: dimensions.aspectRatio,
      rotation: this.half ? -90 : 0,
      circumference: this.half ? 180 : 360,
      cutout: `${(DONUT_DIMENSIONS.CHART_WIDTH - this.thickness * 2) / DONUT_DIMENSIONS.CHART_WIDTH * 100}%`,
      layout: {
        padding: dynamicPadding ?? DONUT_DIMENSIONS.CANVAS_PADDING
      },
      plugins: {
        legend: {
          display: false,
          labels: {
            generateLabels: () => []
            // Prevent Chart.js from generating labels internally
          },
          onClick: () => {
          }
          // Disable legend click handler
        },
        tooltip: {
          ...getChartTooltipOptions(this),
          enabled: !dimensions.isTooSmall,
          filter: (tooltipItem) => tooltipItem.label !== "Remaining",
          callbacks: {
            title: () => "",
            // No title line
            label: (context) => {
              const label = context.label ?? "";
              const value = context.parsed;
              const denominator = this.max > 0 ? this.max : this.total > 0 ? this.total : 1;
              const isPercentage = this.outerLabelUnit === "%";
              const numericValue = formatNumericValue(
                value,
                denominator,
                isPercentage,
                this.outerLabelDecimalPlaces
              );
              const showUnit = this.showUnit && this.outerLabelUnit;
              return showUnit ? `${label} ${numericValue}${this.outerLabelUnit}` : `${label} ${numericValue}`;
            }
          }
        }
      },
      animation: {
        animateRotate: false,
        animateScale: false
      }
    };
  }
  createOuterLabelDonutPlugin() {
    const basePlugin = createArcOuterLabelPlugin(this, {
      formattedLabels: this.formattedLabels
    });
    const originalAfterDatasetsDraw = basePlugin.afterDatasetsDraw;
    if (originalAfterDatasetsDraw) {
      basePlugin.afterDatasetsDraw = (chart, args, pluginOptions, options) => {
        const canvasHeight = this.canvasEl?.clientHeight ?? 0;
        if (canvasHeight < CHART_DIMENSIONS.MIN_HEIGHT_WITH_LABELS) {
          return;
        }
        originalAfterDatasetsDraw(chart, args, pluginOptions, options);
      };
    }
    return basePlugin;
  }
  createCenterReadoutDonutPlugin() {
    return {
      id: "centerText",
      beforeDraw: (chart) => {
        const { ctx, chartArea } = chart;
        const { width, height, left, top } = chartArea;
        const canvasHeight = this.canvasEl?.clientHeight ?? 0;
        if (canvasHeight < CHART_DIMENSIONS.MIN_HEIGHT_WITH_LABELS) {
          return;
        }
        ctx.save();
        ctx.textAlign = "center";
        ctx.textBaseline = "middle";
        const centerX = left + width / 2;
        const fontFamily = getCssVariableValue(
          this,
          OUTER_LABEL_CONFIG.fontFamily
        );
        const lineGap = 4;
        const valueFontWeight = getCssVariableValue(
          this,
          CENTER_READOUT_CONFIG.value.fontWeightVar
        );
        const valueFontSize = getCssVariableValue(
          this,
          CENTER_READOUT_CONFIG.value.fontSizeVar
        );
        const valueColor = getCssVariableValue(
          this,
          CENTER_READOUT_CONFIG.value.fontColorVar
        );
        ctx.font = `${valueFontWeight} ${valueFontSize} ${fontFamily}`;
        const centerValueText = this.total.toFixed(0);
        const valueMetrics = ctx.measureText(centerValueText);
        const valueHeight = valueMetrics.actualBoundingBoxAscent + valueMetrics.actualBoundingBoxDescent;
        const labelFontWeight = getCssVariableValue(
          this,
          CENTER_READOUT_CONFIG.label.fontWeightVar
        );
        const labelFontSize = getCssVariableValue(
          this,
          CENTER_READOUT_CONFIG.label.fontSizeVar
        );
        const labelColor = getCssVariableValue(
          this,
          CENTER_READOUT_CONFIG.label.fontColorVar
        );
        const unitFontWeight = getCssVariableValue(
          this,
          CENTER_READOUT_CONFIG.unit.fontWeightVar
        );
        const unitFontSize = getCssVariableValue(
          this,
          CENTER_READOUT_CONFIG.unit.fontSizeVar
        );
        const unitColor = getCssVariableValue(
          this,
          CENTER_READOUT_CONFIG.unit.fontColorVar
        );
        const centerReadoutLabelText = this.centerReadoutLabel;
        const centerReadoutUnitText = this.centerReadoutUnit;
        const space = centerReadoutUnitText ? " " : "";
        ctx.font = `${labelFontWeight} ${labelFontSize} ${fontFamily}`;
        const labelMetrics = ctx.measureText(centerReadoutLabelText);
        const spaceMetrics = ctx.measureText(space);
        ctx.font = `${unitFontWeight} ${unitFontSize} ${fontFamily}`;
        const unitMetrics = ctx.measureText(centerReadoutUnitText);
        const labelHeight = Math.max(
          labelMetrics.actualBoundingBoxAscent + labelMetrics.actualBoundingBoxDescent,
          unitMetrics.actualBoundingBoxAscent + unitMetrics.actualBoundingBoxDescent
        );
        let valueY;
        let labelY;
        if (this.half) {
          const meta = chart.getDatasetMeta(0);
          const arcCenterY = meta.data[0] ? meta.data[0].y : top + height;
          labelY = arcCenterY - Math.max(
            labelMetrics.actualBoundingBoxDescent,
            unitMetrics.actualBoundingBoxDescent
          );
          valueY = labelY - labelHeight - lineGap - valueHeight + valueMetrics.actualBoundingBoxAscent;
        } else {
          const totalTextHeight = valueHeight + lineGap + labelHeight;
          const centerY = top + height / 2;
          valueY = centerY - totalTextHeight / 2 + valueMetrics.actualBoundingBoxAscent;
          labelY = valueY + valueMetrics.actualBoundingBoxDescent + lineGap + Math.max(
            labelMetrics.actualBoundingBoxAscent,
            unitMetrics.actualBoundingBoxAscent
          );
        }
        ctx.font = `${valueFontWeight} ${valueFontSize} ${fontFamily}`;
        ctx.fillStyle = valueColor;
        ctx.fillText(centerValueText, centerX, valueY);
        ctx.font = `${labelFontWeight} ${labelFontSize} ${fontFamily}`;
        const labelWidth = labelMetrics.width;
        const spaceWidth = spaceMetrics.width;
        ctx.font = `${unitFontWeight} ${unitFontSize} ${fontFamily}`;
        const unitWidth = unitMetrics.width;
        const totalWidth = labelWidth + (centerReadoutUnitText ? spaceWidth + unitWidth : 0);
        ctx.font = `${labelFontWeight} ${labelFontSize} ${fontFamily}`;
        ctx.fillStyle = labelColor;
        ctx.fillText(
          centerReadoutLabelText,
          centerX - totalWidth / 2 + labelWidth / 2,
          labelY
        );
        if (centerReadoutUnitText) {
          ctx.font = `${labelFontWeight} ${labelFontSize} ${fontFamily}`;
          ctx.fillStyle = labelColor;
          ctx.fillText(
            space,
            centerX - totalWidth / 2 + labelWidth + spaceWidth / 2,
            labelY
          );
          ctx.font = `${unitFontWeight} ${unitFontSize} ${fontFamily}`;
          ctx.fillStyle = unitColor;
          ctx.fillText(
            centerReadoutUnitText,
            centerX + totalWidth / 2 - unitWidth / 2,
            labelY
          );
        }
        ctx.restore();
      }
    };
  }
  getBorderRadius(values) {
    if (!this.half) {
      return void 0;
    }
    const remaining = Math.max(0, this.max - this.total);
    const hasRemainingSegment = remaining > 0;
    return values.map((_value, index) => {
      if (index === 0) {
        return {
          outerStart: DONUT_DIMENSIONS.BORDER_RADIUS,
          outerEnd: 0,
          innerStart: DONUT_DIMENSIONS.BORDER_RADIUS,
          innerEnd: 0
        };
      }
      const isLastVisibleSegment = hasRemainingSegment ? index === values.length - 1 : index === values.length - 2;
      if (isLastVisibleSegment) {
        return {
          outerStart: 0,
          outerEnd: DONUT_DIMENSIONS.BORDER_RADIUS,
          innerStart: 0,
          innerEnd: DONUT_DIMENSIONS.BORDER_RADIUS
        };
      }
      return 0;
    });
  }
  createChart() {
    if (!this.canvasEl || !this.canvasEl.isConnected) return;
    const ctx = this.canvasEl.getContext("2d");
    if (!ctx) return;
    const { values, labels, colors } = this.prepareChartData();
    this.chart = new Chart(ctx, {
      type: "doughnut",
      data: {
        labels,
        datasets: [this.createDatasetConfig(values, colors)]
      },
      options: this.getChartOptions(),
      plugins: [
        // Always include plugins; they check height internally during rendering
        ...this.showOuterLabels ? [this.createOuterLabelDonutPlugin()] : [],
        this.createCenterReadoutDonutPlugin()
      ]
    });
    requestAnimationFrame(() => this.updateLegend());
  }
  updateChart() {
    if (!this.chart || !this.canvasEl || !this.canvasEl.isConnected) return;
    const { values, labels, colors } = this.prepareChartData();
    this.chart.data.labels = labels;
    this.chart.data.datasets = [this.createDatasetConfig(values, colors)];
    const latestOptions = this.getChartOptions();
    if (this.chart.options) {
      Object.assign(this.chart.options, latestOptions);
    }
    this.chart.update();
    requestAnimationFrame(() => this.updateLegend());
  }
  /**
   * Update only chart colors without recalculating layout
   * Used by theme observer for efficient theme changes
   */
  updateChartColors() {
    if (!this.chart) return;
    const { colors } = this.prepareChartData();
    if (this.chart.data.datasets[0]) {
      this.chart.data.datasets[0].backgroundColor = colors;
      this.chart.data.datasets[0].borderColor = getCssVariableValue(
        this,
        DONUT_DIMENSIONS.GAP_AND_BORDER_COLOR
      );
    }
    this.chart.update("none");
    this.updateLegend();
  }
  /**
   * Update the legend HTML content
   */
  updateLegend() {
    if (!this.legend || !this.legendDiv || !this.chart) return;
    const meta = this.chart.getDatasetMeta(0);
    if (!meta || !meta.controller) {
      return;
    }
    if (!this.data || this.data.length === 0) {
      this.legendDiv.innerHTML = "";
      return;
    }
    const denominator = this.max > 0 ? this.max : this.total > 0 ? this.total : 1;
    const isPercentage = this.outerLabelUnit === "%";
    try {
      const legendItems = this.data.map((item, i) => {
        const style = meta.controller.getStyle(i, false);
        const numericValue = formatNumericValue(
          item.value,
          denominator,
          isPercentage,
          this.outerLabelDecimalPlaces
        );
        const showUnit = this.showUnit && this.outerLabelUnit;
        const value = `${numericValue}`;
        const unit = showUnit ? `${this.outerLabelUnit}` : "";
        return {
          fillStyle: style.backgroundColor,
          label: item.label,
          value,
          unit
        };
      });
      const legendHTML = generateLegendHTML(legendItems);
      this.legendDiv.innerHTML = legendHTML;
    } catch (error) {
      console.debug(
        "[obc-donut-chart] updateLegend: error generating legend HTML",
        error
      );
    }
  }
  render() {
    return html`
      <div class="wrapper">
        <canvas></canvas>
        ${this.legend ? html`<div class="legend"></div>` : ""}
      </div>
    `;
  }
};
ObcDonutChart.styles = [
  unsafeCSS(componentStyle),
  unsafeCSS(chartCommonStyle),
  unsafeCSS(chartLegendStyle),
  unsafeCSS(chartDebugStyle)
];
__decorateClass([
  property({ attribute: false })
], ObcDonutChart.prototype, "data", 2);
__decorateClass([
  property({ attribute: false })
], ObcDonutChart.prototype, "colors", 2);
__decorateClass([
  property({ type: String })
], ObcDonutChart.prototype, "priority", 2);
__decorateClass([
  property({ type: Boolean, reflect: true })
], ObcDonutChart.prototype, "half", 2);
__decorateClass([
  property({ type: Boolean })
], ObcDonutChart.prototype, "showOuterLabels", 2);
__decorateClass([
  property({ type: Boolean })
], ObcDonutChart.prototype, "showUnit", 2);
__decorateClass([
  property({ type: String })
], ObcDonutChart.prototype, "outerLabelUnit", 2);
__decorateClass([
  property({
    type: Number
  })
], ObcDonutChart.prototype, "outerLabelMaxLength", 2);
__decorateClass([
  property({
    type: Number
  })
], ObcDonutChart.prototype, "outerLabelDecimalPlaces", 2);
__decorateClass([
  property({ type: String })
], ObcDonutChart.prototype, "centerReadoutLabel", 2);
__decorateClass([
  property({ type: String })
], ObcDonutChart.prototype, "centerReadoutUnit", 2);
__decorateClass([
  property({ type: Number })
], ObcDonutChart.prototype, "max", 2);
__decorateClass([
  property({ type: Number })
], ObcDonutChart.prototype, "thickness", 2);
__decorateClass([
  property({ type: Boolean, reflect: true })
], ObcDonutChart.prototype, "legend", 2);
__decorateClass([
  property({ type: Boolean, reflect: true })
], ObcDonutChart.prototype, "showDebugOverlay", 2);
__decorateClass([
  property({ type: Number, reflect: true })
], ObcDonutChart.prototype, "fixedHeight", 2);
__decorateClass([
  state()
], ObcDonutChart.prototype, "total", 2);
__decorateClass([
  state()
], ObcDonutChart.prototype, "formattedLabels", 2);
__decorateClass([
  query("canvas")
], ObcDonutChart.prototype, "canvasEl", 2);
__decorateClass([
  query(".legend")
], ObcDonutChart.prototype, "legendDiv", 2);
ObcDonutChart = __decorateClass([
  customElement("obc-donut-chart")
], ObcDonutChart);
export {
  ObcDonutChart
};
//# sourceMappingURL=donut-chart.js.map
