import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { CartesianGrid, Line, LineChart, ReferenceLine, Tooltip, XAxis, YAxis, } from 'recharts';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import styled, { useTheme } from 'styled-components';
import { Stack } from '../../spacing';
import { fontSize } from '../../style/theme';
import { useChartLegend } from '../chartlegend/ChartLegendWrapper';
import { addMissingDataPoint, getUnitLabel, } from '../linetemporalchart/ChartUtil';
import { Loader } from '../loader/Loader.component';
import { ChartTitleText } from '../text/Text.component';
import { formatXAxisLabel } from './utils';
import { ChartTooltipPortal, ChartTooltipItem, ChartTooltipHeader, ChartTooltipItemsContainer, ChartTooltipSeparator, TooltipHeader, } from '../charttooltip/ChartTooltip';
import { LegendShape } from '../chartlegend/ChartLegend';
import { StyledResponsiveContainer } from '../barchartv2/Barchart.component';
import { getRoundReferenceValue, getTicks } from '../barchartv2/utils';
import { IconHelp } from '../iconhelper/IconHelper';
const maxWidthTooltip = { maxWidth: '20rem' };
const LineTemporalChartWrapper = styled.div `
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
`;
const LineTimeSerieChartTooltip = ({ unitLabel, duration, isChartActive, tooltipProps, renderTooltip, hoveredValue, isSymmetrical, chartContainerRef, }) => {
    const { active, payload, label, coordinate } = tooltipProps;
    if (!active || !payload || !payload.length || !label || !isChartActive)
        return null;
    const tooltipContent = renderTooltip ? (renderTooltip(tooltipProps, unitLabel, duration)) : (_jsxs(_Fragment, { children: [_jsx(ChartTooltipHeader, { children: _jsx(TooltipHeader, { duration: duration, value: label }) }), _jsx(ChartTooltipItemsContainer, { children: (() => {
                    // We can't use the default itemSorter method because it's a custom tooltip.
                    // Sort the payload here instead
                    const sortedPayload = [...payload].sort((a, b) => {
                        const aValue = a.value;
                        const bValue = b.value;
                        if (aValue >= 0 && bValue >= 0) {
                            return bValue - aValue; // Higher positive values first
                        }
                        if (aValue < 0 && bValue < 0) {
                            return bValue - aValue; // Lower negative values first
                        }
                        return bValue - aValue; // Positives before negatives
                    });
                    // Find the transition point between positive and negative values
                    const separatorIndex = sortedPayload.findIndex((entry) => entry.value < 0);
                    const hasBothPositiveAndNegative = separatorIndex > 0 && separatorIndex < sortedPayload.length;
                    return sortedPayload.map((entry, index) => {
                        const legendIcon = (_jsx(LegendShape, { color: entry.color, shape: "line", chartColors: { [entry.color]: entry.color } }));
                        const isHovered = entry.name === hoveredValue;
                        const formattedValue = !Number.isFinite(entry.value)
                            ? '-'
                            : `${entry.value.toFixed(2)} ${unitLabel}`;
                        return (_jsxs(React.Fragment, { children: [isSymmetrical &&
                                    hasBothPositiveAndNegative &&
                                    index === separatorIndex && _jsx(ChartTooltipSeparator, {}), _jsx(ChartTooltipItem, { label: entry.name, value: formattedValue, legendIcon: legendIcon, isHovered: isHovered })] }, index));
                    });
                })() })] }));
    return (_jsx(ChartTooltipPortal, { coordinate: coordinate, chartContainerRef: chartContainerRef, isVisible: active && isChartActive, children: tooltipContent }));
};
const isSymmetricalSeries = (series) => {
    return 'above' in series && 'below' in series;
};
export function LineTimeSerieChart({ series, title, height, startingTimeStamp, interval, duration, unitRange, isLoading = false, timeFormat = 'date-time', yAxisType = 'default', yAxisTitle, helpText, syncId, renderTooltip, ...rest }) {
    const theme = useTheme();
    const { getColor, selectedResources } = useChartLegend();
    const chartRef = useRef(null);
    const [isChartActive, setIsChartActive] = useState(false);
    const [hoveredValue, setHoveredValue] = useState(undefined);
    const chartData = useMemo(() => {
        // Guard against empty/undefined series data
        if (!series || (Array.isArray(series) && series.length === 0)) {
            return [];
        }
        // Handle symmetrical series with empty above/below arrays
        if (isSymmetricalSeries(series)) {
            if ((!series.above || series.above.length === 0) &&
                (!series.below || series.below.length === 0)) {
                return [];
            }
        }
        // 1. Add missing data points
        const normalizedSeries = yAxisType === 'symmetrical' && isSymmetricalSeries(series)
            ? {
                above: series.above
                    ? series.above.map((line) => ({
                        ...line,
                        data: addMissingDataPoint(line.data, startingTimeStamp, duration, interval),
                    }))
                    : [],
                // Convert positive values to negative values
                below: series.below
                    ? series.below.map((line) => ({
                        ...line,
                        data: addMissingDataPoint(line.data, startingTimeStamp, duration, interval).map(([timestamp, value]) => [
                            timestamp,
                            value === null ? null : `-${Number(value)}`,
                        ]),
                    }))
                    : [],
            }
            : series.map((line) => ({
                ...line,
                data: addMissingDataPoint(line.data, startingTimeStamp, duration, interval),
            }));
        // 2. Convert directly to Recharts format
        // Initialize an object to hold data points by timestamp
        const dataPointsByTime = {};
        const seriesToProcess = yAxisType === 'symmetrical' && isSymmetricalSeries(normalizedSeries)
            ? [...normalizedSeries.above, ...normalizedSeries.below]
            : normalizedSeries;
        seriesToProcess.forEach((serie) => {
            const label = serie.getTooltipLabel(serie.metricPrefix, serie.resource);
            serie.data.forEach((point) => {
                const timestamp = typeof point[0] === 'number' ? point[0] * 1000 : Number(point[0]);
                const value = point[1];
                // Initialize this timestamp if it doesn't exist
                if (!dataPointsByTime[timestamp]) {
                    dataPointsByTime[timestamp] = { timestamp };
                }
                // Add this metric's value to the data point, and convert the value to a number if it's a string
                dataPointsByTime[timestamp][label] =
                    typeof value === 'string' ? Number(value) : value;
            });
        });
        // Convert object to array for Recharts
        return Object.values(dataPointsByTime).sort((a, b) => a.timestamp - b.timestamp);
    }, [series, startingTimeStamp, duration, interval, yAxisType]);
    // Calculate evenly spaced ticks that avoid the very beginning and end
    const xAxisTicks = useMemo(() => {
        if (!chartData || chartData.length === 0)
            return [];
        const timestamps = chartData.map((d) => d.timestamp);
        const minTimestamp = Math.min(...timestamps);
        const maxTimestamp = Math.max(...timestamps);
        const timeRange = maxTimestamp - minTimestamp;
        // Add padding to avoid labels at the very edges (10% padding on each side)
        const padding = timeRange * 0.1;
        const paddedStart = minTimestamp + padding;
        const paddedEnd = maxTimestamp - padding;
        const paddedRange = paddedEnd - paddedStart;
        // Create 5 evenly spaced ticks within the padded range
        const numTicks = 5;
        const tickInterval = paddedRange / (numTicks - 1);
        const evenlySpacedTicks = Array.from({ length: numTicks }, (_, index) => paddedStart + index * tickInterval);
        return evenlySpacedTicks;
    }, [chartData]);
    // 3. Transform the data base on the valuebase
    const { topValue, unitLabel, rechartsData } = useMemo(() => {
        if (yAxisType === 'percentage')
            return {
                topValue: 100,
                unitLabel: '%',
                rechartsData: chartData,
            };
        const values = chartData.flatMap((dataPoint) => Object.entries(dataPoint)
            .filter(([key]) => key !== 'timestamp')
            .map(([_, value]) => {
            const num = typeof value === 'string' ? Number(value) : (value !== null && value !== void 0 ? value : Infinity);
            return !isNaN(num) && num !== null ? num : null;
        })
            .filter((value) => value !== null));
        // Guard against empty values array
        if (values.length === 0) {
            return {
                topValue: 100, // Default value for empty charts
                unitLabel: '',
                rechartsData: [],
            };
        }
        const top = Math.abs(Math.max(...values));
        const bottom = Math.abs(Math.min(...values));
        const maxValue = Math.max(top, bottom);
        const { valueBase, unitLabel } = getUnitLabel(unitRange !== null && unitRange !== void 0 ? unitRange : [], maxValue);
        // Use round reference value to add extra padding to the top value
        const topValue = getRoundReferenceValue(maxValue / valueBase);
        const rechartsData = chartData.map((dataPoint) => {
            const normalizedDataPoint = { ...dataPoint };
            Object.entries(dataPoint).forEach(([key, value]) => {
                if (key !== 'timestamp' && typeof value === 'number') {
                    normalizedDataPoint[key] = value / valueBase;
                }
            });
            return normalizedDataPoint;
        });
        return { topValue, unitLabel, rechartsData };
    }, [chartData, yAxisType, unitRange]);
    // Group series by resource and create color mapping
    const { colorMapping, groupedSeries } = useMemo(() => {
        const mapping = {};
        // Guard against empty/undefined series
        if (!series) {
            return { colorMapping: mapping, groupedSeries: {} };
        }
        const allSeries = isSymmetricalSeries(series)
            ? [...(series.above || []), ...(series.below || [])]
            : series;
        // Group series by resource
        const groups = allSeries
            .filter((serie) => selectedResources.includes(serie.resource))
            .reduce((acc, serie) => {
            const key = serie.resource;
            if (!acc[key]) {
                acc[key] = [];
            }
            acc[key].push(serie);
            return acc;
        }, {});
        // Get colors from the ChartLegend context
        Object.keys(groups).forEach((resource) => {
            const color = getColor(resource);
            if (color) {
                mapping[resource] = color;
            }
            else {
                console.warn(`Color not defined for resource: ${resource}`);
            }
        });
        return {
            colorMapping: mapping,
            groupedSeries: groups,
        };
    }, [series, getColor, selectedResources]);
    // Format time for display the tick in the x axis
    const formatXAxisLabelCallback = useCallback((timestamp) => formatXAxisLabel(timestamp, duration), [duration]);
    return (_jsxs(LineTemporalChartWrapper, { children: [_jsxs(Stack, { gap: "r4", children: [_jsxs(ChartTitleText, { children: [title, " ", unitLabel && `(${unitLabel})`] }), helpText && (_jsx(IconHelp, { tooltipMessage: helpText, overlayStyle: maxWidthTooltip })), isLoading && _jsx(Loader, {})] }), _jsx("div", { onFocus: () => setIsChartActive(true), onBlur: () => setIsChartActive(false), onFocusCapture: () => setIsChartActive(true), onBlurCapture: () => setIsChartActive(false), children: _jsx(StyledResponsiveContainer, { width: "100%", height: height, children: _jsxs(LineChart, { data: rechartsData, ref: chartRef, margin: { top: 0, right: 0, bottom: 0, left: 0 }, "aria-label": `Time series chart for ${title}`, syncId: syncId, onMouseEnter: () => setIsChartActive(true), onMouseLeave: () => setIsChartActive(false), accessibilityLayer: true, children: [_jsx(CartesianGrid, { vertical: true, horizontal: true, verticalPoints: [0], horizontalPoints: [0], stroke: theme.border, fill: theme.backgroundLevel4, strokeWidth: 1 }), _jsx(XAxis, { dataKey: "timestamp", type: "number", domain: ['dataMin', 'dataMax'], ticks: xAxisTicks, tickFormatter: formatXAxisLabelCallback, tickCount: 5, tick: {
                                    fill: theme.textSecondary,
                                    fontSize: fontSize.smaller,
                                }, axisLine: { stroke: theme.border } }), _jsx(YAxis, { orientation: "right", label: {
                                    value: yAxisTitle,
                                    angle: 90,
                                    dx: 20,
                                    style: {
                                        fill: theme.textSecondary,
                                        fontSize: fontSize.smaller,
                                    },
                                }, domain: yAxisType === 'percentage'
                                    ? [0, 100]
                                    : yAxisType === 'symmetrical'
                                        ? [-topValue, topValue]
                                        : [0, topValue], axisLine: { stroke: theme.border }, tick: {
                                    fill: theme.textSecondary,
                                    fontSize: fontSize.smaller,
                                }, tickFormatter: (value) => new Intl.NumberFormat('fr-FR').format(value), ticks: getTicks(topValue, yAxisType === 'symmetrical'), interval: 0 }), _jsx(Tooltip, { content: (props) => (_jsx(LineTimeSerieChartTooltip, { unitLabel: unitLabel, duration: duration, renderTooltip: renderTooltip, isSymmetrical: yAxisType === 'symmetrical', tooltipProps: props, isChartActive: isChartActive, hoveredValue: hoveredValue, chartContainerRef: chartRef })) }), yAxisType === 'symmetrical' && (_jsx(ReferenceLine, { y: 0, stroke: theme.border })), Object.entries(groupedSeries).map(([resource, resourceSeries]) => resourceSeries.map((serie, serieIndex) => {
                                const label = serie.getTooltipLabel(serie.metricPrefix, serie.resource);
                                return (_jsx(Line, { type: "monotone", dataKey: label, stroke: colorMapping[resource], dot: false, isAnimationActive: false, strokeDasharray: serie.isLineDashed ? '4 4' : undefined, onMouseEnter: () => setHoveredValue(label), onMouseLeave: () => setHoveredValue(undefined) }, `${title}-${resource}-${serieIndex}`));
                            }))] }) }) })] }));
}
