import React, { useEffect, useState, useRef, useCallback } from 'react';
import { webSocketManager } from './WebSocketManager';
import LineChartComponent from './Components/LineChartComponent';
import { TimeRangeSelector } from './Components/TimeRangeSelector';
import { ConnectionStatusIndicator } from './Components/ConnectionStatusIndicator';
import { CurrentRangeDisplay } from './Components/CurrentRangeDisplay';
import { TIME_RANGES, COLORS, SECOND, MINUTE, HOUR, DAY } from './Components/constants';
import axios from 'axios';
import Skeleton from '@mui/material/Skeleton';
import Box from '@mui/material/Box';


// Компонент Skeleton для графика
const ChartSkeleton = () => (
    <Box sx={{
        backgroundColor: '#fff',
        borderRadius: '8px',
        padding: '20px',
        marginBottom: '20px',
        position: 'relative'
    }}>
        <Box sx={{ position: 'absolute', right: '20px', top: '20px' }}>
            <Skeleton variant="circular" width={16} height={16} />
        </Box>

        <Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 2 }}>
            <Skeleton variant="text" width="40%" height={30} />
            <Skeleton variant="text" width="30%" height={30} />
        </Box>

        <Skeleton variant="rectangular" width="100%" height={300} />

        <Box sx={{ display: 'flex', justifyContent: 'center', mt: 2, gap: 2 }}>
            {[1, 2, 3, 4].map((_, i) => (
                <Skeleton key={i} variant="rounded" width={80} height={36} />
            ))}
        </Box>
    </Box>
);

const PrometheusChart = ({ metricName }) => {
    const [chartData, setChartData] = useState(null);
    const [selectedRange, setSelectedRange] = useState(TIME_RANGES[0]);
    const [startDate, setStartDate] = useState(new Date());
    const [endDate, setEndDate] = useState(new Date());
    const [useCustomRange, setUseCustomRange] = useState(false);
    const [connectionStatus, setConnectionStatus] = useState('disconnected');
    const [selectedGraphRange, setSelectedGraphRange] = useState(null);
    const [filteredData, setFilteredData] = useState(null);
    const [isSelectingRange, setIsSelectingRange] = useState(false);
    const [lastCustomRange, setLastCustomRange] = useState(null);
    const intervalRef = useRef(null);
    const debounceRef = useRef(null);

    const formatTime = useCallback((timestamp, rangeSeconds) => {
        const ts = typeof timestamp === 'number' ? timestamp : Date.now();
        const date = new Date(ts);

        // Определяем формат в зависимости от диапазона
        const showFullDate = rangeSeconds > 86400; // больше суток

        const timeOptions = {
            hour: '2-digit',
            minute: '2-digit',
            second: '2-digit',
            hour12: false
        };

        const dateOptions = showFullDate ? {
            month: '2-digit',
            day: '2-digit',
            ...timeOptions
        } : timeOptions;

        return {
            display: date.toLocaleString('ru-RU', dateOptions),
            fullDisplay: date.toLocaleString('ru-RU', {
                year: 'numeric',
                month: '2-digit',
                day: '2-digit',
                hour: '2-digit',
                minute: '2-digit',
                second: '2-digit',
                hour12: false
            }),
            timestamp: ts
        };
    }, []);

    const calculateStep = useCallback((start, end) => {
        const range = end - start;
        if (range <= MINUTE) return 1;           // 1 мин
        if (range <= MINUTE * 5) return 5;      // 5 мин
        if (range <= HOUR / 2) return 15;      // 30 мин
        if (range <= HOUR) return 30;         // 1 час
        if (range <= HOUR * 3) return 60;    // 3 часа
        if (range <= HOUR * 6) return 120;  // 6 часов
        if (range <= DAY / 2) return 300;  // 12 часов
        if (range <= DAY) return 600;     // 24 часа
        return 1800;                     // > 24 часов
    }, []);


    const processMetricsData = useCallback((response, replace = false) => {
        console.log('Processing metrics data:', response);
        if (response.metric !== metricName) return;

        const dataArray = Array.isArray(response.data) ? response.data : [response.data];
        if (!dataArray.length) return;

        const newData = {};
        const rangeSeconds = useCustomRange
            ? (endDate.getTime() - startDate.getTime()) / 1000
            : selectedRange.value;

        dataArray.forEach(item => {
            const instance = item.instance || 'default';
            if (!newData[instance]) newData[instance] = [];

            let timestamp;
            if (typeof item.timestamp === 'number') {
                timestamp = item.timestamp > 1e12 ? item.timestamp : item.timestamp * 1000;
            } else {
                timestamp = Date.now();
            }

            const value = parseFloat(item.value);
            const formattedTime = formatTime(timestamp, rangeSeconds);

            newData[instance].push({
                time: formattedTime.display,
                fullTime: formattedTime.fullDisplay,
                value,
                timestamp
            });
        });

        Object.keys(newData).forEach(instance => {
            newData[instance] = newData[instance]
                .sort((a, b) => a.timestamp - b.timestamp)
                .slice(-1000);
        });

        if (replace) {
            setChartData(newData); // Заменяем полностью
        } else {
            setChartData(prev => {
                const merged = { ...(prev || {}) };
                Object.keys(newData).forEach(instance => {
                    if (!merged[instance]) merged[instance] = [];
                    merged[instance] = [...merged[instance], ...newData[instance]]
                        .sort((a, b) => a.timestamp - b.timestamp)
                        .slice(-1000);
                });
                return merged;
            });
        }
    }, [metricName, selectedRange.value, formatTime, useCustomRange, startDate, endDate]);


    const fetchData = useCallback(() => {
        if (isSelectingRange) return;

        const now = Math.floor(Date.now() / 1000);
        const start = now - selectedRange.value;
        const end = now;
        const step = calculateStep(start, end);

        webSocketManager.getMetricsRange(metricName, start, end, step)
            .then(data => {
                processMetricsData({ metric: metricName, data });
            })
            .catch(error => {
                console.error('Error fetching metrics:', error);
            });
    }, [metricName, selectedRange.value, isSelectingRange, calculateStep, processMetricsData]);

    const fetchCustomRangeData = useCallback(async () => {
        // Добавляем проверку на валидность дат
        if (!startDate || !endDate || startDate >= endDate) {
            console.error('Invalid date range');
            return;
        }

        const start = Math.floor(startDate.getTime() / 1000);
        const end = Math.ceil(endDate.getTime() / 1000); // Используем Math.ceil для конечной даты
        const rangeSeconds = end - start;

        try {
            const response = await axios.get(`${import.meta.env.VITE_BACK_URL}/api/metrics`, {
                params: {
                    metric: metricName,
                    start,
                    end,
                    step: calculateStep(start, end)
                }
            });

            if (response.data?.length) {
                // Добавляем нормализацию timestamp
                const processedData = response.data.map(item => ({
                    ...item,
                    timestamp: item.timestamp > 1e12 ? item.timestamp : item.timestamp * 1000,
                    value: parseFloat(item.value)
                }));

                processMetricsData({
                    metric: metricName,
                    data: processedData
                }, true);
            }
        } catch (error) {
            console.error('Ошибка при получении кастомных данных:', error);
        }
    }, [metricName, startDate, endDate, calculateStep, processMetricsData]);


    const handleRangeChange = useCallback(async (event) => {
        if (intervalRef.current) {
            clearInterval(intervalRef.current);
            intervalRef.current = null;
        }

        const selectedValue = event.target.value;
        const range = TIME_RANGES.find(r => r.value === parseInt(selectedValue, 10));

        // Полный сброс состояния перед загрузкой новых данных
        setChartData(null);
        setSelectedRange(range);
        setUseCustomRange(false);
        setSelectedGraphRange(null);
        setFilteredData(null);

        const now = new Date();
        setEndDate(now);
        setStartDate(new Date(now.getTime() - range.value * 1000));

        // Ждем завершения обновления состояния перед загрузкой
        await new Promise(resolve => setTimeout(resolve, 0));
        fetchData();
    }, [fetchData]);

    const handleCustomRangeChange = useCallback(() => {
        if (intervalRef.current) {
            clearInterval(intervalRef.current);
            intervalRef.current = null;
        }

        setUseCustomRange(true);
        setChartData(null);
        setSelectedGraphRange(null);
        setFilteredData(null);
        fetchCustomRangeData();
    }, [fetchCustomRangeData]);



    const interpolateData = useCallback((data, targetPointCount) => {
        if (!data || data.length < 2) return data;
        if (data.length >= targetPointCount) return data;

        const interpolated = [];
        const step = (data.length - 1) / (targetPointCount - 1);

        for (let i = 0; i < targetPointCount; i++) {
            const index = i * step;
            const lowerIndex = Math.floor(index);
            const upperIndex = Math.ceil(index);

            if (lowerIndex === upperIndex) {
                interpolated.push(data[lowerIndex]);
                continue;
            }

            const fraction = index - lowerIndex;
            const interpolatedPoint = {};

            Object.keys(data[lowerIndex]).forEach(key => {
                if (key === 'timestamp') {
                    interpolatedPoint[key] = data[lowerIndex][key] +
                        fraction * (data[upperIndex][key] - data[lowerIndex][key]);

                    // Добавляем отображаемое время
                    const { display, fullDisplay } = formatTime(interpolatedPoint[key],
                        (endDate - startDate) / 1000);
                    interpolatedPoint.time = display;
                    interpolatedPoint.fullTime = fullDisplay;
                } else if (typeof data[lowerIndex][key] === 'number') {
                    interpolatedPoint[key] = data[lowerIndex][key] +
                        fraction * (data[upperIndex][key] - data[lowerIndex][key]);
                } else {
                    interpolatedPoint[key] = data[lowerIndex][key];
                }
            });

            interpolated.push(interpolatedPoint);
        }

        return interpolated;
    }, []);

    const handleRangeSelect = useCallback((range) => {
        setLastCustomRange(range);
        if (!range || !chartData) return;

        setIsSelectingRange(true);
        setSelectedGraphRange(range);

        if (intervalRef.current) {
            clearInterval(intervalRef.current);
            intervalRef.current = null;
        }

        // Получаем все точки и сортируем по времени
        const allPoints = Object.values(chartData).flat();
        const sortedPoints = allPoints.sort((a, b) => a.timestamp - b.timestamp);

        // Вычисляем абсолютные индексы
        const startIndex = Math.floor(range.startIndex * (sortedPoints.length - 1));
        const endIndex = Math.floor(range.endIndex * (sortedPoints.length - 1));

        // Фильтруем точки по выбранному диапазону
        const filtered = sortedPoints.slice(startIndex, endIndex + 1);

        // Применяем интерполяцию только если точек меньше 100
        const interpolated = filtered.length < 100 ?
            interpolateData(filtered, Math.min(100, filtered.length * 3)) :
            filtered;

        setFilteredData(interpolated);
        setIsSelectingRange(false);
    }, [chartData, interpolateData, formatTime]);

    const handleResetZoom = useCallback(() => {
        setSelectedGraphRange(null);
        setFilteredData(null);
        setIsSelectingRange(false);

        if (useCustomRange) {
            fetchCustomRangeData();
        } else {
            fetchData();
        }

        if (lastCustomRange) {
            handleRangeSelect(lastCustomRange);
        }
    }, [fetchData, fetchCustomRangeData, useCustomRange, lastCustomRange, handleRangeSelect]);

    useEffect(() => {
        // Обработчик данных с сервера
        const handleMetricsData = (data) => {
            if (!useCustomRange) {
                processMetricsData({ metric: metricName, data });
            }
        };

        // Подписываемся на обновления метрики
        const unsubscribe = webSocketManager.subscribe(metricName, handleMetricsData);

        // Подписываемся на изменения статуса соединения
        const unsubscribeStatus = webSocketManager.onConnectionStatusChange(setConnectionStatus);

        return () => {
            // Отписываемся при размонтировании компонента
            unsubscribe();
            unsubscribeStatus();

            // Очищаем интервал обновления
            if (intervalRef.current) {
                clearInterval(intervalRef.current);
            }
        };
    }, [metricName, useCustomRange, processMetricsData]);


    useEffect(() => {
        if (useCustomRange && !isSelectingRange) {
            if (debounceRef.current) {
                clearTimeout(debounceRef.current);
            }

            debounceRef.current = setTimeout(() => {
                fetchCustomRangeData();
            }, 500);
        }

        return () => {
            if (debounceRef.current) {
                clearTimeout(debounceRef.current);
            }
        };
    }, [useCustomRange, isSelectingRange, startDate, endDate, fetchCustomRangeData]);

    useEffect(() => {
        if (useCustomRange || isSelectingRange) return;

        const fetchDataWrapper = () => {
            try {
                fetchData();
            } catch (error) {
                console.error('Error in interval fetch:', error);
            }
        };

        if (intervalRef.current) {
            clearInterval(intervalRef.current);
        }


        fetchDataWrapper();
        intervalRef.current = setInterval(fetchDataWrapper, selectedRange.interval);

        return () => {
            if (intervalRef.current) {
                clearInterval(intervalRef.current);
            }
        };
    }, [fetchData, selectedRange.interval, useCustomRange, isSelectingRange]);

    useEffect(() => {
        if (!selectedGraphRange || !chartData) {
            setFilteredData(null);
            return;
        }

        const allPoints = Object.values(chartData).flat();
        const sortedPoints = allPoints.sort((a, b) => a.timestamp - b.timestamp);

        const startIndex = Math.floor(selectedGraphRange.startIndex * (sortedPoints.length - 1));
        const endIndex = Math.floor(selectedGraphRange.endIndex * (sortedPoints.length - 1));

        const filtered = sortedPoints.slice(startIndex, endIndex + 1);
        const interpolated = filtered.length > 100 ?
            interpolateData(filtered, 100) :
            filtered;

        setFilteredData(interpolated);
    }, [selectedGraphRange, chartData, interpolateData]);

    if (chartData === null) {
        return <ChartSkeleton />;
    }

    if (Object.keys(chartData).length === 0) {
        return (
            <Box sx={{
                backgroundColor: '#fff',
                borderRadius: '8px',
                padding: '20px',
                marginBottom: '20px',
                textAlign: 'center'
            }}>
                No data available
            </Box>
        );
    }

    return (
        <div style={{
            backgroundColor: '#fff',
            borderRadius: '8px',
            padding: '20px',
            marginBottom: '20px',
            position: 'relative'
        }}>
            <ConnectionStatusIndicator connectionStatus={connectionStatus} />

            <TimeRangeSelector
                selectedRange={selectedRange}
                handleRangeChange={handleRangeChange}
                startDate={startDate}
                setStartDate={setStartDate}
                endDate={endDate}
                setEndDate={setEndDate}
                useCustomRange={useCustomRange}
                handleCustomRangeChange={handleCustomRangeChange}
                handleResetZoom={handleResetZoom}
            />

            <CurrentRangeDisplay
                useCustomRange={useCustomRange}
                startDate={startDate}
                endDate={endDate}
                selectedRange={selectedRange}
            />

            <LineChartComponent
                chartData={chartData}
                metricName={metricName}
                colors={COLORS}
                onRangeSelect={handleRangeSelect}
                filteredData={filteredData}
            />
        </div>
    );
};

export default React.memo(PrometheusChart);