import React, { useEffect, useState, useRef, useCallback } from 'react';
import { io } from 'socket.io-client';
import LineChartComponent from './Components/LineChartComponent';
import { TimeRangeSelector } from './Components/TimeRangeSelector';
import { ConnectionStatusIndicator } from './Components/ConnectionStatusIndicator';
import { CurrentRangeDisplay } from './Components/CurrentRangeDisplay';
import { TIME_RANGES, COLORS } from './Components/constants';
import axios from 'axios';

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 socketRef = 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 <= 60) return 1;       // 1 мин
        if (range <= 300) return 5;      // 5 мин
        if (range <= 1800) return 15;    // 30 мин
        if (range <= 3600) return 30;    // 1 час
        if (range <= 10800) return 60;   // 3 часа
        if (range <= 21600) return 120;  // 6 часов
        if (range <= 43200) return 300;  // 12 часов
        if (range <= 86400) return 600;  // 24 часа
        return 1800;                     // > 24 часов
    }, []);

    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);

        if (socketRef.current?.connected) {
            socketRef.current.emit('get-metrics', {
                metric: metricName,
                start,
                end,
                step,
                _t: Date.now()
            });
        }
    }, [metricName, selectedRange.value, isSelectingRange]);

    const processMetricsData = useCallback((response) => {
        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;

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

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

                // Унифицированная конвертация timestamp
                let timestamp;
                if (typeof item.timestamp === 'number') {
                    // Определяем, в секундах или миллисекундах пришел timestamp
                    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: value,
                    timestamp: timestamp
                });
            });

            // Сортируем и ограничиваем данные
            Object.keys(newData).forEach(instance => {
                newData[instance] = newData[instance]
                    .sort((a, b) => a.timestamp - b.timestamp)
                    .slice(-1000);
            });
            return newData;
        });
    }, [metricName, selectedRange.value, formatTime, useCustomRange, startDate, endDate]);

    const setupWebSocket = useCallback(() => {
        if (socketRef.current) {
            // Если соединение уже существует, возвращаем его
            if (socketRef.current.connected) return socketRef.current;
            // Если соединение в процессе переподключения, тоже возвращаем
            if (socketRef.current.reconnecting) return socketRef.current;
        }

        const socket = io('http://192.168.2.39:3000/api/metrics-ws', {
            transports: ['websocket'],
            reconnection: true,
            reconnectionAttempts: Infinity,
            reconnectionDelay: 1000,
            reconnectionDelayMax: 5000,
        });

        socketRef.current = socket;

        socket.on('connect', () => {
            console.log('WebSocket connected');
            setConnectionStatus('connected');
            fetchData();
        });

        socket.on('disconnect', (reason) => {
            console.log('WebSocket disconnected:', reason);
            setConnectionStatus('disconnected');
            if (reason === 'io server disconnect') socket.connect();
        });

        socket.on('connect_error', (error) => {
            console.error('WebSocket connection error:', error);
            setConnectionStatus('error');
            setTimeout(() => socket.connect(), 1000);
        });

        socket.on('metrics-data', (response) => {
            console.log('Received raw metrics data:', response);
            processMetricsData(response);
        });

        socket.on('metrics-error', (error) => {
            console.error('Metrics error:', error);
            setConnectionStatus('error');
        });

        return socket;
    }, []);

    const fetchCustomRangeData = useCallback(async () => {
        const start = Math.floor(startDate.getTime() / 1000);
        const end = Math.floor(endDate.getTime() / 1000);
        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) {
                // Преобразуем данные перед передачей в processMetricsData
                const processedData = response.data.map(item => ({
                    ...item,
                    timestamp: item.timestamp, // оставляем в секундах - processMetricsData конвертирует
                    value: item.value.toString()
                }));

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


    const handleRangeChange = useCallback((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));

        setSelectedRange(range);
        setUseCustomRange(false);
        setChartData(null);
        setSelectedGraphRange(null);
        setFilteredData(null);

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

        // Переподключение сокета
        if (!socketRef.current?.connected) {
            socketRef.current?.connect();
        }
    }, []);

    const handleCustomRangeChange = useCallback(() => {
        // Отключаем WebSocket соединение
        if (socketRef.current?.connected) {
            socketRef.current.disconnect();
            setConnectionStatus('disconnected');
        }

        // Очищаем интервал обновления
        if (intervalRef.current) {
            clearInterval(intervalRef.current);
            intervalRef.current = null;
        }

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

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

        if (useCustomRange) {
            fetchCustomRangeData();
        } else {
            if (!socketRef.current?.connected) {
                socketRef.current?.connect();
            }
            fetchData();
        }

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

    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 (socketRef.current?.connected) {
            socketRef.current.disconnect();
        }
        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]);

    useEffect(() => {
        const socket = setupWebSocket();
        return () => {
            clearInterval(intervalRef.current);
            socket.disconnect();
        };
    }, [setupWebSocket]);

    // Обновим useEffect для кастомного диапазона
    useEffect(() => {
        if (useCustomRange && !isSelectingRange) {
            // Очищаем предыдущий таймер
            if (debounceRef.current) {
                clearTimeout(debounceRef.current);
            }

            // Устанавливаем новый таймер с задержкой 500 мс
            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 <div style={{ padding: '20px', textAlign: 'center' }}>Loading data...</div>;
    }

    if (Object.keys(chartData).length === 0) {
        return <div style={{ padding: '20px', textAlign: 'center' }}>No data available</div>;
    }

    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);

