'use client';
import * as React from 'react';
import { useGaugeChartStyles } from './useGaugeChartStyles.styles';
import { select as d3Select } from 'd3-selection';
import { arc as d3Arc } from 'd3-shape';
import { Points, areArraysEqual, formatScientificLimitWidth, getAccessibleDataObject, getColorFromToken, getNextColor, pointTypes, useRtl, ChartTitle } from '../../utilities/index';
import { formatToLocaleString } from '@fluentui/chart-utilities';
import { SVGTooltipText } from '../../utilities/SVGTooltipText';
import { Legends, Shape } from '../Legends/index';
import { useArrowNavigationGroup } from '@fluentui/react-tabster';
import { ChartPopover } from '../CommonComponents/ChartPopover';
import { useImageExport } from '../../utilities/hooks';
const GAUGE_MARGIN = 16;
const LABEL_WIDTH = 36;
const LABEL_HEIGHT = 16;
const LABEL_OFFSET = 4;
const TITLE_OFFSET = 11;
const EXTRA_NEEDLE_LENGTH = 4;
export const ARC_PADDING = 2;
export const BREAKPOINTS = [
    {
        minRadius: 52,
        arcWidth: 12,
        fontSize: 20
    },
    {
        minRadius: 70,
        arcWidth: 16,
        fontSize: 24
    },
    {
        minRadius: 88,
        arcWidth: 20,
        fontSize: 32
    },
    {
        minRadius: 106,
        arcWidth: 24,
        fontSize: 32
    },
    {
        minRadius: 124,
        arcWidth: 28,
        fontSize: 40
    },
    {
        minRadius: 142,
        arcWidth: 32,
        fontSize: 40
    }
];
export const calcNeedleRotation = (chartValue, minValue, maxValue)=>{
    let needleRotation = (chartValue - minValue) / (maxValue - minValue) * 180;
    if (needleRotation < 0) {
        needleRotation = 0;
    } else if (needleRotation > 180) {
        needleRotation = 180;
    }
    return needleRotation;
};
export const getSegmentLabel = (segment, minValue, maxValue, variant, isAriaLabel = false)=>{
    if (isAriaLabel) {
        return minValue === 0 && variant === 'single-segment' ? `${segment.legend}, ${segment.size} out of ${maxValue} or ${(segment.size / maxValue * 100).toFixed()}%` : `${segment.legend}, ${segment.start} to ${segment.end}`;
    }
    return minValue === 0 && variant === 'single-segment' ? `${segment.size} (${(segment.size / maxValue * 100).toFixed()}%)` : `${segment.start} - ${segment.end}`;
};
export const getChartValueLabel = (chartValue, minValue, maxValue, chartValueFormat, forCallout = false)=>{
    if (forCallout) {
        // When displaying the chart value as a percentage, use fractions in the callout, and vice versa.
        // This helps clarify the actual value and avoid repetition.
        return minValue !== 0 ? chartValue.toString() : chartValueFormat === 'fraction' ? `${(chartValue / maxValue * 100).toFixed()}%` : `${chartValue}/${maxValue}`;
    }
    return typeof chartValueFormat === 'function' ? chartValueFormat([
        chartValue - minValue,
        maxValue - minValue
    ]) : minValue !== 0 ? chartValue.toString() : chartValueFormat === 'fraction' ? `${chartValue}/${maxValue}` : `${(chartValue / maxValue * 100).toFixed()}%`;
};
export const GaugeChart = /*#__PURE__*/ React.forwardRef((props, forwardedRef)=>{
    var _props_legendProps;
    const _getMargins = ()=>{
        const { hideMinMax, chartTitle, sublabel } = props;
        return {
            left: (!hideMinMax ? LABEL_OFFSET + LABEL_WIDTH : 0) + GAUGE_MARGIN,
            right: (!hideMinMax ? LABEL_OFFSET + LABEL_WIDTH : 0) + GAUGE_MARGIN,
            top: (chartTitle ? TITLE_OFFSET + LABEL_HEIGHT : EXTRA_NEEDLE_LENGTH / 2) + GAUGE_MARGIN,
            bottom: (sublabel ? LABEL_OFFSET + LABEL_HEIGHT : 0) + GAUGE_MARGIN
        };
    };
    const _margins = _getMargins();
    const _legendsHeight = !props.hideLegend ? 32 : 0;
    const { chartContainerRef: _rootElem, legendsRef: _legendsRef } = useImageExport(props.componentRef, props.hideLegend, false);
    const _isRTL = useRtl();
    const [width, setWidth] = React.useState(140 + _getMargins().left + _getMargins().right);
    const [height, setHeight] = React.useState(70 + _getMargins().top + _getMargins().bottom + _legendsHeight);
    const [hoveredLegend, setHoveredLegend] = React.useState('');
    const [selectedLegends, setSelectedLegends] = React.useState(((_props_legendProps = props.legendProps) === null || _props_legendProps === void 0 ? void 0 : _props_legendProps.selectedLegends) || []);
    const [focusedElement, setFocusedElement] = React.useState('');
    const [isPopoverOpen, setPopoverOpen] = React.useState(false);
    const [hoverXValue, setHoverXValue] = React.useState('');
    const [hoverYValues, setHoverYValues] = React.useState([]);
    const [refSelected, setRefSelected] = React.useState(null);
    const prevPropsRef = React.useRef(null);
    const _width = props.width || width;
    const _height = props.height || height;
    const _outerRadius = Math.min((_width - (_margins.left + _margins.right)) / 2, _height - (_margins.top + _margins.bottom + _legendsHeight));
    const { arcWidth, chartValueSize } = _getStylesBasedOnBreakpoint();
    const _innerRadius = _outerRadius - arcWidth;
    let _minValue;
    let _maxValue;
    let _segments;
    let _calloutAnchor = '';
    React.useEffect(()=>{
        if (_rootElem.current) {
            setWidth(_rootElem.current.clientWidth);
            setHeight(_rootElem.current.clientHeight);
        }
    }, []);
    React.useEffect(()=>{
        if (prevPropsRef.current) {
            var _prevProps_legendProps, _props_legendProps;
            const prevProps = prevPropsRef.current;
            if (!areArraysEqual((_prevProps_legendProps = prevProps.legendProps) === null || _prevProps_legendProps === void 0 ? void 0 : _prevProps_legendProps.selectedLegends, (_props_legendProps = props.legendProps) === null || _props_legendProps === void 0 ? void 0 : _props_legendProps.selectedLegends)) {
                var _props_legendProps1;
                setSelectedLegends(((_props_legendProps1 = props.legendProps) === null || _props_legendProps1 === void 0 ? void 0 : _props_legendProps1.selectedLegends) || []);
            }
        }
        prevPropsRef.current = props;
    }, [
        props
    ]);
    const classes = useGaugeChartStyles(props);
    function _getStylesBasedOnBreakpoint() {
        for(let index = BREAKPOINTS.length - 1; index >= 0; index -= 1){
            if (_outerRadius >= BREAKPOINTS[index].minRadius) {
                return {
                    arcWidth: BREAKPOINTS[index].arcWidth,
                    chartValueSize: BREAKPOINTS[index].fontSize
                };
            }
        }
        return {
            arcWidth: BREAKPOINTS[0].arcWidth,
            chartValueSize: BREAKPOINTS[0].fontSize
        };
    }
    function _processProps() {
        const { minValue = 0, maxValue, segments, roundCorners } = props;
        let total = minValue;
        const processedSegments = segments.map(// eslint-disable-next-line @typescript-eslint/no-explicit-any
        (segment, index)=>{
            const size = Math.max(segment.size, 0);
            total += size;
            return {
                legend: segment.legend,
                size,
                color: typeof segment.color !== 'undefined' ? getColorFromToken(segment.color, false) : getNextColor(index, 0, false),
                accessibilityData: segment.accessibilityData,
                start: total - size,
                end: total
            };
        });
        if (typeof maxValue !== 'undefined' && total < maxValue) {
            processedSegments.push({
                legend: 'Unknown',
                size: maxValue - total,
                color: 'neutralLight',
                start: total,
                end: maxValue
            });
            total = maxValue;
        }
        const arcGenerator = d3Arc().cornerRadius(roundCorners ? 3 : 0).padAngle(ARC_PADDING / _outerRadius).padRadius(_outerRadius);
        const rtlSafeSegments = _isRTL ? Array.from(processedSegments).reverse() : processedSegments;
        let prevAngle = -Math.PI / 2;
        const arcs = rtlSafeSegments.map((segment, index)=>{
            const endAngle = prevAngle + segment.size / (total - minValue) * Math.PI;
            const d = arcGenerator({
                innerRadius: _innerRadius,
                outerRadius: _outerRadius,
                startAngle: prevAngle,
                endAngle
            });
            prevAngle = endAngle;
            return {
                d,
                segmentIndex: _isRTL ? processedSegments.length - 1 - index : index,
                startAngle: prevAngle - segment.size / (total - minValue) * Math.PI,
                endAngle
            };
        });
        _minValue = minValue;
        _maxValue = total;
        _segments = processedSegments;
        return {
            arcs
        };
    }
    function _renderNeedle() {
        const needleRotation = calcNeedleRotation(props.chartValue, _minValue, _maxValue);
        const rtlSafeNeedleRotation = _isRTL ? 180 - needleRotation : needleRotation;
        const strokeWidth = 2;
        const halfStrokeWidth = strokeWidth / 2;
        const needleLength = _outerRadius - _innerRadius + EXTRA_NEEDLE_LENGTH;
        const needleId = `gauge-chart-needle`;
        return /*#__PURE__*/ React.createElement("g", {
            transform: `rotate(${rtlSafeNeedleRotation}, 0, 0)`
        }, /*#__PURE__*/ React.createElement("path", {
            d: `
            M 0,${-halfStrokeWidth - 3}
            L ${-needleLength},${-halfStrokeWidth - 1}
            A ${halfStrokeWidth + 1},${halfStrokeWidth + 1},0,0,0,${-needleLength},${halfStrokeWidth + 1}
            L 0,${halfStrokeWidth + 3}
            A ${halfStrokeWidth + 3},${halfStrokeWidth + 3},0,0,0,0,${-halfStrokeWidth - 3}
          `,
            id: needleId,
            strokeWidth: strokeWidth,
            className: classes.needle,
            transform: `translate(${-_innerRadius + EXTRA_NEEDLE_LENGTH / 2})`,
            "data-is-focusable": true,
            onFocus: (e)=>_handleFocus(e, 'Needle', needleId),
            onBlur: _handleBlur,
            onMouseEnter: (e)=>_handleMouseOver(e, 'Needle', needleId),
            onMouseMove: (e)=>_handleMouseOver(e, 'Needle', needleId),
            role: "img",
            "aria-label": 'Current value: ' + getChartValueLabel(props.chartValue, _minValue, _maxValue, props.chartValueFormat)
        }));
    }
    function _renderLegends() {
        if (props.hideLegend) {
            return null;
        }
        const legends = _segments.map((segment, index)=>{
            const color = segment.color || getNextColor(index, 0, false);
            return {
                title: segment.legend,
                color,
                hoverAction: ()=>{
                    setHoveredLegend(segment.legend);
                },
                onMouseOutAction: ()=>{
                    setHoveredLegend('');
                }
            };
        });
        return /*#__PURE__*/ React.createElement("div", {
            className: classes.legendsContainer
        }, /*#__PURE__*/ React.createElement(Legends, {
            legends: legends,
            centerLegends: true,
            ...props.legendProps,
            onChange: _onLegendSelectionChange,
            legendRef: _legendsRef
        }));
    }
    function _onLegendSelectionChange(// eslint-disable-next-line @typescript-eslint/no-shadow
    selectedLegends, event, currentLegend) {
        var _props_legendProps, _props_legendProps1;
        if ((_props_legendProps = props.legendProps) === null || _props_legendProps === void 0 ? void 0 : _props_legendProps.canSelectMultipleLegends) {
            setSelectedLegends(selectedLegends);
        } else {
            setSelectedLegends(selectedLegends.slice(-1));
        }
        if ((_props_legendProps1 = props.legendProps) === null || _props_legendProps1 === void 0 ? void 0 : _props_legendProps1.onChange) {
            props.legendProps.onChange(selectedLegends, event, currentLegend);
        }
    }
    /**
     * This function checks if the given legend is highlighted or not.
     * A legend can be highlighted in 2 ways:
     * 1. selection: if the user clicks on it
     * 2. hovering: if there is no selected legend and the user hovers over it
     */ function _legendHighlighted(legend) {
        return _getHighlightedLegend().includes(legend);
    }
    /**
     * This function checks if none of the legends is selected or hovered.
     */ function _noLegendHighlighted() {
        return _getHighlightedLegend().length === 0;
    }
    function _getHighlightedLegend() {
        return selectedLegends.length > 0 ? selectedLegends : hoveredLegend ? [
            hoveredLegend
        ] : [];
    }
    // eslint-disable-next-line @typescript-eslint/no-shadow
    function _handleFocus(focusEvent, focusedElement, elementId) {
        _showCallout(focusEvent, focusedElement, true, elementId);
    }
    function _handleBlur() {
        _hideCallout(true);
    }
    function _handleMouseOver(mouseEvent, hoveredElement, elementId) {
        _showCallout(mouseEvent, hoveredElement, false, elementId);
    }
    function _handleMouseOut() {
        _hideCallout(false);
    }
    function _handleCalloutDismiss() {
        _hideCallout(false);
    }
    function _showCallout(event, legend, isFocusEvent, elementId) {
        if (_calloutAnchor === legend) {
            return;
        }
        const targetElement = document.getElementById(elementId);
        _calloutAnchor = legend;
        // eslint-disable-next-line @typescript-eslint/no-shadow
        const hoverXValue = 'Current value is ' + getChartValueLabel(props.chartValue, _minValue, _maxValue, props.chartValueFormat, true);
        // eslint-disable-next-line @typescript-eslint/no-shadow
        const hoverYValues = _segments.filter((segment)=>_noLegendHighlighted() || _legendHighlighted(segment.legend)).map((segment)=>{
            const yValue = {
                legend: segment.legend,
                y: getSegmentLabel(segment, _minValue, _maxValue, props.variant),
                color: segment.color
            };
            return yValue;
        });
        setPopoverOpen([
            'Needle',
            'Chart value'
        ].includes(legend) || _noLegendHighlighted() || _legendHighlighted(legend));
        setRefSelected(targetElement);
        setHoverXValue(hoverXValue);
        setHoverYValues(hoverYValues);
        if (isFocusEvent) {
            setFocusedElement(legend);
        }
    }
    function _hideCallout(isBlurEvent) {
        _calloutAnchor = '';
        setPopoverOpen(false);
        setHoverXValue('');
        setHoverYValues([]);
        if (isBlurEvent) {
            setFocusedElement('');
        }
    }
    function _wrapContent(content, id, maxWidth) {
        const textElement = d3Select(`#${id}`);
        textElement.text(content);
        if (!textElement.node()) {
            return false;
        }
        let isOverflowing = false;
        let textLength = textElement.node().getComputedTextLength();
        while(textLength > maxWidth && content.length > 0){
            content = content.slice(0, -1);
            textElement.text(content + '...');
            isOverflowing = true;
            textLength = textElement.node().getComputedTextLength();
        }
        return isOverflowing;
    }
    // TO DO: Write a common functional component for Multi value callout and divide sub count method
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    function _multiValueCallout(calloutProps) {
        const yValueHoverSubCountsExists = _yValueHoverSubCountsExists(calloutProps.YValueHover);
        return /*#__PURE__*/ React.createElement("div", {
            className: classes.calloutContentRoot
        }, /*#__PURE__*/ React.createElement("div", {
            className: classes.calloutDateTimeContainer,
            style: yValueHoverSubCountsExists ? {
                marginBottom: '11px'
            } : {}
        }, /*#__PURE__*/ React.createElement("div", {
            className: classes.calloutContentX,
            ...getAccessibleDataObject(calloutProps.xAxisCalloutAccessibilityData, 'text', false)
        }, formatToLocaleString(calloutProps.hoverXValue, props.culture))), /*#__PURE__*/ React.createElement("div", {
            className: classes.calloutInfoContainer,
            style: yValueHoverSubCountsExists ? {
                display: 'flex'
            } : {}
        }, calloutProps.YValueHover && calloutProps.YValueHover.map((yValue, index, yValues)=>{
            const isLast = index + 1 === yValues.length;
            const { shouldDrawBorderBottom = false } = yValue;
            return /*#__PURE__*/ React.createElement("div", {
                ...getAccessibleDataObject(yValue.callOutAccessibilityData, 'text', false),
                key: `callout-content-${index}`,
                style: yValueHoverSubCountsExists ? {
                    display: 'inline-block',
                    ...shouldDrawBorderBottom && {
                        paddingBottom: '10px'
                    }
                } : {
                    ...shouldDrawBorderBottom && {
                        paddingBottom: '10px'
                    }
                }
            }, _getCalloutContent(yValue, index, yValueHoverSubCountsExists, isLast));
        }), !!calloutProps.descriptionMessage && /*#__PURE__*/ React.createElement("div", {
            className: classes.descriptionMessage
        }, calloutProps.descriptionMessage)));
    }
    function _yValueHoverSubCountsExists(yValueHover) {
        if (yValueHover) {
            return yValueHover.some((yValue)=>yValue.yAxisCalloutData && typeof yValue.yAxisCalloutData !== 'string');
        }
        return false;
    }
    function _getCalloutContent(xValue, index, yValueHoverSubCountsExists, isLast) {
        const marginStyle = isLast ? {} : {
            marginRight: '16px'
        };
        const toDrawShape = xValue.index !== undefined && xValue.index !== -1;
        const { culture } = props;
        const yValue = formatToLocaleString(xValue.y, culture);
        if (!xValue.yAxisCalloutData || typeof xValue.yAxisCalloutData === 'string') {
            return /*#__PURE__*/ React.createElement("div", {
                style: yValueHoverSubCountsExists ? marginStyle : {}
            }, yValueHoverSubCountsExists && /*#__PURE__*/ React.createElement("div", {
                className: "ms-fontWeight-semibold",
                style: {
                    fontSize: '12pt'
                }
            }, xValue.legend, " (", yValue, ")"), /*#__PURE__*/ React.createElement("div", {
                id: `${index}_${xValue.y}`,
                className: classes.calloutBlockContainer,
                style: {
                    borderInlineStart: `4px solid ${xValue.color}`
                }
            }, toDrawShape && /*#__PURE__*/ React.createElement(Shape, {
                svgProps: {
                    className: classes.shapeStyles
                },
                pathProps: {
                    fill: xValue.color
                },
                shape: Points[xValue.index % Object.keys(pointTypes).length],
                style: {
                    display: 'flex'
                }
            }), /*#__PURE__*/ React.createElement("div", null, /*#__PURE__*/ React.createElement("div", {
                className: classes.calloutlegendText
            }, " ", xValue.legend), /*#__PURE__*/ React.createElement("div", {
                className: classes.calloutContentY
            }, formatToLocaleString(xValue.yAxisCalloutData ? xValue.yAxisCalloutData : xValue.y || xValue.data, culture)))));
        } else {
            const subcounts = xValue.yAxisCalloutData;
            return /*#__PURE__*/ React.createElement("div", {
                style: marginStyle
            }, /*#__PURE__*/ React.createElement("div", {
                className: "ms-fontWeight-semibold",
                style: {
                    fontSize: '12pt'
                }
            }, xValue.legend, " (", yValue, ")"), Object.keys(subcounts).map((subcountName)=>{
                return /*#__PURE__*/ React.createElement("div", {
                    key: subcountName,
                    className: classes.calloutBlockContainer
                }, /*#__PURE__*/ React.createElement("div", {
                    className: classes.calloutlegendText
                }, ' ', formatToLocaleString(subcountName, culture)), /*#__PURE__*/ React.createElement("div", {
                    className: classes.calloutContentY
                }, formatToLocaleString(subcounts[subcountName], culture)));
            }));
        }
    }
    function _getChartTitle() {
        const { chartTitle } = props;
        return (chartTitle ? `${chartTitle}. ` : '') + `Gauge chart with ${_segments.length} segments. `;
    }
    const { arcs } = _processProps();
    const arrowAttributes = useArrowNavigationGroup({
        circular: true,
        axis: 'horizontal'
    });
    return /*#__PURE__*/ React.createElement("div", {
        className: classes.root,
        ref: (el)=>{
            _rootElem.current = el;
        }
    }, /*#__PURE__*/ React.createElement("div", {
        className: classes.chartWrapper,
        ...arrowAttributes
    }, /*#__PURE__*/ React.createElement("svg", {
        className: classes.chart,
        width: _width,
        height: _height - _legendsHeight,
        role: "region",
        "aria-label": _getChartTitle(),
        onMouseLeave: _handleMouseOut
    }, /*#__PURE__*/ React.createElement("g", {
        transform: `translate(${_width / 2}, ${_height - (_margins.bottom + _legendsHeight)})`
    }, props.chartTitle && /*#__PURE__*/ React.createElement(ChartTitle, {
        title: props.chartTitle,
        x: 0,
        y: -(_outerRadius + TITLE_OFFSET),
        className: classes.chartTitle,
        titleStyles: props.titleStyles,
        tooltipClassName: classes.svgTooltip
    }), !props.hideMinMax && /*#__PURE__*/ React.createElement(React.Fragment, null, /*#__PURE__*/ React.createElement("text", {
        x: (_isRTL ? 1 : -1) * (_outerRadius + LABEL_OFFSET),
        y: 0,
        textAnchor: "end",
        className: classes.limits,
        role: "img",
        "aria-label": `Min value: ${_minValue}`
    }, formatScientificLimitWidth(_minValue)), /*#__PURE__*/ React.createElement("text", {
        x: (_isRTL ? -1 : 1) * (_outerRadius + LABEL_OFFSET),
        y: 0,
        textAnchor: "start",
        className: classes.limits,
        role: "img",
        "aria-label": `Max value: ${_maxValue}`
    }, formatScientificLimitWidth(_maxValue))), arcs.map((arc, index)=>{
        const segment = _segments[arc.segmentIndex];
        const arcId = `gauge-chart-arc-${index}`;
        return /*#__PURE__*/ React.createElement(React.Fragment, {
            key: index
        }, /*#__PURE__*/ React.createElement("path", {
            d: arc.d,
            id: arcId,
            strokeWidth: focusedElement === segment.legend ? ARC_PADDING : 0,
            className: classes.segment,
            fill: segment.color,
            opacity: _legendHighlighted(segment.legend) || _noLegendHighlighted() ? 1 : 0.1,
            ...getAccessibleDataObject({
                ariaLabel: getSegmentLabel(segment, _minValue, _maxValue, props.variant, true),
                ...segment.accessibilityData
            }, 'img', true),
            onFocus: (e)=>_handleFocus(e, segment.legend, arcId),
            onBlur: _handleBlur,
            onMouseEnter: (e)=>_handleMouseOver(e, segment.legend, arcId),
            onMouseLeave: (e)=>_handleCalloutDismiss(),
            onMouseMove: (e)=>_handleMouseOver(e, segment.legend, arcId),
            tabIndex: _legendHighlighted(segment.legend) || _noLegendHighlighted() ? 0 : undefined
        }));
    }), _renderNeedle(), /*#__PURE__*/ React.createElement("g", {
        onMouseEnter: (e)=>_handleMouseOver(e, 'Chart value'),
        onMouseMove: (e)=>_handleMouseOver(e, 'Chart value')
    }, /*#__PURE__*/ React.createElement(SVGTooltipText, {
        content: getChartValueLabel(props.chartValue, _minValue, _maxValue, props.chartValueFormat),
        textProps: {
            x: 0,
            y: 0,
            textAnchor: 'middle',
            className: classes.chartValue,
            fontSize: chartValueSize,
            'aria-hidden': 'true'
        },
        maxWidth: _innerRadius * 2 - 24,
        wrapContent: _wrapContent
    })), props.sublabel && /*#__PURE__*/ React.createElement(SVGTooltipText, {
        content: props.sublabel,
        textProps: {
            x: 0,
            y: 4,
            textAnchor: 'middle',
            dominantBaseline: 'hanging',
            className: classes.sublabel
        },
        maxWidth: _innerRadius * 2,
        wrapContent: _wrapContent
    })))), _renderLegends(), !props.hideTooltip && isPopoverOpen && /*#__PURE__*/ React.createElement(ChartPopover, {
        ...props.calloutProps,
        positioning: {
            target: refSelected
        },
        isPopoverOpen: isPopoverOpen,
        customCallout: {
            customizedCallout: _multiValueCallout({
                hoverXValue,
                YValueHover: hoverYValues
            })
        }
    }));
});
GaugeChart.displayName = 'GaugeChart';
