import { io, Socket } from 'socket.io-client';
import { EventBus } from './EventBus';

export class WebSocketService {
	private socket: Socket | null = null;
	private eventBus: EventBus;
	private apiBaseUrl: string;
	//private receivedEvents: Set<string> = new Set(); // Для отслеживания полученных событий
	//private reconnectAttempts = 0;
	private maxReconnectAttempts = 10;
	private pingIntervalId: ReturnType<typeof setInterval> | null = null;

	constructor(eventBus: EventBus, apiBaseUrl: string) {
		this.eventBus = eventBus;
		this.apiBaseUrl = apiBaseUrl;
	}

	/**
	 * Подключается к WebSocket серверу
	 */
	connect(): void {
		if (this.socket?.connected) {
			console.log('WebSocket уже подключен');
			return;
		}

		// Извлекаем базовый URL без /api
		const wsUrl = this.apiBaseUrl.replace('/api', '');
		console.log('🔌 Подключение к WebSocket:', wsUrl);

		this.socket = io(wsUrl, {
			transports: ['websocket', 'polling'],
			reconnection: true,
			reconnectionDelay: 1000,
			reconnectionDelayMax: 5000,
			reconnectionAttempts: this.maxReconnectAttempts,
			timeout: 20000,
			forceNew: false,
			upgrade: true,
			// Добавляем дополнительные опции для стабильности
			autoConnect: true,
			rememberUpgrade: true,
			rejectUnauthorized: false
		});

		this.setupEventHandlers();
		this.startProactivePing(); // Запускаем проактивный пинг
	}

	/**
	 * Настраивает обработчики WebSocket событий
	 */
	private setupEventHandlers(): void {
		if (!this.socket) return;

		this.socket.on('connect', () => {
			console.log('✅ WebSocket подключен');
		});

		this.socket.on('disconnect', (reason) => {
			console.log('❌ WebSocket отключен:', reason);
		});

		this.socket.on('connect_error', (error) => {
			console.error('Ошибка подключения WebSocket:', error);
		});

		// ----------------------------------------------- Nodes Update Events ---------------------------------------------------------------

		this.socket.on(
			'node_type_changed',
			(data: { graph_id: string; node_id: string; node_type: string }, callback?: (response: { status: string }) => void) => {
				console.log('📡 WebSocket получено: node_type_changed', data);
				this.eventBus.emit('ws-node-type-changed', {
					graphId: data.graph_id,
					nodeId: data.node_id,
					nodeType: data.node_type
				});
				if (callback) {
					callback({ status: 'ok' }); // Отправляем подтверждение
				}
			}
		);

		this.socket.on(
			'graph_title_updated',
			(data: { graph_id: string; title: string }, callback?: (response: { status: string }) => void) => {
				console.log('📡 WebSocket получено: graph_title_updated', data);
				this.eventBus.emit('ws-graph-title-updated', data);
				if (callback) {
					callback({ status: 'ok' }); // Отправляем подтверждение
				}
			}
		);

		this.socket.on(
			'node_title_updated',
			(data: { graph_id: string; node_id: string; title: string }, callback?: (response: { status: string }) => void) => {
				console.log('📡 WebSocket получено: node_title_updated', data);

				this.eventBus.emit('ws-node-title-updated', data);
				if (callback) {
					callback({ status: 'ok' }); // Отправляем подтверждение
				}
			}
		);
	}

	/**
	 * Отключается от WebSocket сервера
	 */
	disconnect(): void {
		if (this.socket) {
			this.socket.disconnect();
			this.socket = null;
			console.log('WebSocket отключен');
		}
		this.stopProactivePing(); // Останавливаем проактивный пинг
	}

	/**
	 * Проверяет статус подключения
	 */
	isConnected(): boolean {
		return this.socket?.connected ?? false;
	}

	/**
	 * Запускает проактивный пинг для проверки соединения.
	 */
    private startProactivePing(): void {
        this.stopProactivePing(); // Убедимся, что предыдущий интервал остановлен
        this.pingIntervalId = setInterval(() => {
            if (this.socket && this.socket.connected) {
                console.log('Client sending proactive ping...');
                let ackTimeout = setTimeout(() => {
                    console.warn('Client: No pong response received for proactive ping within timeout. Forcing reconnect.');
                    this.disconnect();
                    this.connect();
                }, 3000); // Таймаут ожидания ответа на пинг от сервера (3 секунды)

                // Используем callback для получения подтверждения от сервера
                this.socket.emit('client_proactive_ping', (response: { status: string }) => {
                    clearTimeout(ackTimeout);
                    if (response && response.status === 'pong') {
                        console.log('Client received proactive pong.');
                    } else {
                        console.warn('Client: Invalid pong response or no response for proactive ping. Forcing reconnect.');
                        this.disconnect();
                        this.connect();
                    }
                });
            } else if (this.socket && !this.socket.connected) { // Здесь убираем '&& !this.socket.connecting'
                // Если сокет не подключен, Socket.IO должен сам управлять переподключением,
                // так как reconnection: true. Ручной вызов connect() может быть избыточным.
                console.log('Client proactive ping: Socket not connected. Relying on Socket.IO\'s auto-reconnect logic.');
                // Если вы все же хотите агрессивно вызвать this.connect() при каждом обнаружении отключения,
                // оставьте следующую строку:
                this.connect();
            }
        }, 5000); // Проверять каждые 5 секунд
    }

	/**
	 * Останавливает проактивный пинг.
	 */
	private stopProactivePing(): void {
		if (this.pingIntervalId) {
			clearInterval(this.pingIntervalId);
			this.pingIntervalId = null;
			console.log('Proactive ping stopped.');
		}
	}
}
