/* *
 *
 *  (c) 2010-2026 Highsoft AS
 *  Author: Torstein Honsi
 *
 *  A commercial license may be required depending on use.
 *  See www.highcharts.com/license
 *
 *
 * */
'use strict';
import Color from './Color.js';
import H from '../Globals.js';
const { charts } = H;
import PaletteDefaults from './PaletteDefaults.js';
import { diffObjects, extend, isString, objectEach, merge } from '../../Shared/Utilities.js';
/* *
 *
 *  Constants
 *
 * */
const defaultColors = [
    '#2caffe',
    '#544fc5',
    '#00e272',
    '#fe6a35',
    '#6b8abc',
    '#d568fb',
    '#2ee0ca',
    '#fa4b42',
    '#feb56a',
    '#91e8e1'
];
const defaultDarkOverrideColors = [
    null, // Use the same color for the first item in dark mode
    '#00e272',
    '#efdf00'
];
/* *
 * Build the text content for the style tag
 */
const getStyles = (specifier, cssVars) => {
    const reduceToCSS = (
    // For browsers that don't support color-scheme and light-dark, we need
    // to first set the CSS variables as the light scheme.
    supportsColorScheme) => {
        let css = '';
        objectEach(cssVars.light, (value, key) => {
            css += supportsColorScheme && value !== cssVars.dark[key] ?
                `  ${key}: light-dark(${value}, ${cssVars.dark[key]});\n` :
                `  ${key}: ${value};\n`;
        });
        return css;
    };
    const legacy = reduceToCSS(), lightDark = reduceToCSS(true);
    const css = `${specifier || ':root'} {
${legacy}
}
@supports (color: light-dark(#fff, #000)) {
  ${specifier || ':root'} {
${lightDark}
  }
}
.highcharts-container {
  color-scheme: light dark;
}
.highcharts-light .highcharts-container {
  color-scheme: light;
}
.highcharts-dark .highcharts-container {
  color-scheme: dark;
}`;
    return css;
};
/* *
 *
 *  Class
 *
 * */
/* eslint-disable valid-jsdoc */
/**
 * A Palette class holding the palette colors and lifecycle methods for each
 * chart.
 *
 * @class
 * @name Highcharts.Palette
 *
 * @param {Highcharts.Chart} chart
 *        The chart instance
 * @param {Highcharts.PaletteOptions} options
 *        Palette options
 */
export default class Palette {
    /* *
     *
     *  Constructor
     *
     * */
    constructor(renderer, options) {
        this.defaultOptions = PaletteDefaults;
        this.options = merge(PaletteDefaults);
        this.cssVars = { light: {}, dark: {} };
        this.renderer = renderer;
        this.update(options);
    }
    /**
     * Inject the CSS for the palette into the document head or chart
     * container.
     *
     * @internal
     *
     * @param {Highcharts.PaletteOptions} options
     *        Palette options
     */
    injectCSS(options) {
        const { cssVars, renderer } = this, hasSpecificPalette = Object.keys(diffObjects(options, this.defaultOptions)).length > 0, colorCount = Math.max(options.colors?.length || 0, defaultColors.length);
        let colorScheme = 'light';
        const addKebab = (color, key) => {
            if (isString(color)) {
                // Kebab-case the key. Sequences of numbers should be kept
                // but with a preceding dash.
                key = key
                    .replace(/([0-9]+)/g, '-$1')
                    .replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
                cssVars[colorScheme][`--highcharts-${key}`] = color;
            }
        };
        for (const cScheme of ['light', 'dark']) {
            const paletteColors = options[cScheme] || {}, interpolated = {}, neutralColor = new Color(paletteColors?.neutralColor || ''), backgroundColor = new Color(paletteColors?.backgroundColor || ''), highlightColor = new Color(paletteColors?.highlightColor || '');
            colorScheme = cScheme;
            // Interpolate keys
            [3, 5, 10, 20, 40, 60, 80, 100].forEach((fraction) => {
                interpolated[`neutralColor${fraction}`] = backgroundColor
                    .tweenTo(neutralColor, fraction / 100);
                interpolated[`highlightColor${fraction}`] = backgroundColor
                    .tweenTo(highlightColor, fraction / 100);
            });
            // Extended data colors per scheme
            for (let i = 0; i < colorCount; i++) {
                const color = 
                // 1. priority to scheme-specific colors
                paletteColors?.colors?.[i] ||
                    // 2. then fall back to palette-level colors
                    options.colors?.[i] ||
                    // 3. then fall back to dark default colors
                    (cScheme === 'dark' ? defaultDarkOverrideColors : [])?.[i] ||
                    // 4. finally, use default colors
                    defaultColors[i];
                addKebab(color || '#888a', `color${i}`);
            }
            // The rest are stored as named properties
            objectEach(paletteColors, addKebab);
            objectEach(interpolated, addKebab);
            // Default to the light scheme, the dark scheme doesn't define
            // all colors.
            if (cScheme === 'light') {
                extend(cssVars.dark, cssVars.light);
            }
        }
        // Add a style tag to the chart renderer box
        const defs = renderer.defs.element, specifier = hasSpecificPalette ?
            `*[data-highcharts-chart="${renderer.chartIndex}"]` :
            '', style = defs
            .querySelector('style.highcharts-palette') ||
            renderer.box.ownerDocument.createElement('style');
        if (!style.parentNode) {
            style.nonce = 'highcharts';
            style.className = 'highcharts-palette';
            defs.appendChild(style);
        }
        style.textContent = getStyles(specifier, cssVars);
    }
    /**
     * Update the palette with new options. May be called directly as
     * `chart.palette.update()` or indirectly from `chart.update({ palette })`.
     *
     * @function Highcharts.Palette#update
     *
     * @param {Highcharts.PaletteOptions} options
     *        New palette options
     */
    update(options) {
        const renderTo = this.renderer.box.parentElement, chart = charts?.[this.renderer.chartIndex];
        options = merge(true, this.options, options);
        if (chart) {
            chart.options.palette = options;
        }
        if (options.injectCSS !== false) {
            this.injectCSS(options);
        }
        if (renderTo) {
            if (isString(options.colorScheme) &&
                ['light', 'dark', 'inherit'].includes(options.colorScheme)) {
                renderTo.style.colorScheme = options.colorScheme;
            }
            else {
                renderTo.style.removeProperty('color-scheme');
            }
        }
    }
}
