import { Injectable, inject } from '@angular/core';
import { Subject } from 'rxjs';
import { ChallengeEventService } from './challenge-event.service';
import { Challenge } from '../models/challenge.models';

/**
 * Service to handle WebSocket connections for challenge events
 * Listens for incoming challenge notifications and emits them to ChallengeEventService
 */
@Injectable({ providedIn: 'root' })
export class ChallengeWebSocketService {
    private readonly challengeEventService = inject(ChallengeEventService);
    private ws: WebSocket | null = null;
    private reconnectAttempts = 0;
    private readonly maxReconnectAttempts = 5;
    private readonly reconnectDelay = 3000;

    /**
     * Initialize WebSocket connection for challenge events
     */
    connect(): void {
        if (this.ws) {
            return; // Already connected
        }

        const wsProtocol = window.location.protocol === 'https:' ? 'wss' : 'ws';
        const wsUrl = `${wsProtocol}://${window.location.host}/ws/challenges`;

        try {
            this.ws = new WebSocket(wsUrl);

            this.ws.onopen = () => {
                console.log('Challenge WebSocket connected');
                this.reconnectAttempts = 0;
            };

            this.ws.onmessage = (event) => {
                this.handleMessage(event.data);
            };

            this.ws.onerror = (error) => {
                console.error('Challenge WebSocket error:', error);
            };

            this.ws.onclose = () => {
                console.log('Challenge WebSocket disconnected');
                this.ws = null;
                this.attemptReconnect();
            };
        } catch (error) {
            console.error('Failed to create WebSocket:', error);
            this.attemptReconnect();
        }
    }

    /**
     * Close the WebSocket connection
     */
    disconnect(): void {
        if (this.ws) {
            this.ws.close();
            this.ws = null;
        }
    }

    /**
     * Send a message through WebSocket
     */
    send(message: any): void {
        if (this.ws && this.ws.readyState === WebSocket.OPEN) {
            this.ws.send(JSON.stringify(message));
        }
    }

    /**
     * Handle incoming WebSocket messages
     */
    private handleMessage(data: string): void {
        try {
            const message = JSON.parse(data);

            if (!message.type) {
                return;
            }

            switch (message.type) {
                case 'challenge.received':
                    if (message.challenge) {
                        this.challengeEventService.onChallengeReceived(message.challenge as Challenge);
                    }
                    break;

                case 'challenge.accepted':
                    if (message.challenge) {
                        this.challengeEventService.onChallengeAccepted(message.challenge as Challenge);
                    }
                    break;

                case 'challenge.declined':
                    if (message.challengeId) {
                        this.challengeEventService.removeChallenge(message.challengeId);
                    }
                    break;

                case 'challenge.expired':
                    if (message.challengeId) {
                        this.challengeEventService.removeChallenge(message.challengeId);
                    }
                    break;

                default:
                    console.debug('Unknown challenge message type:', message.type);
            }
        } catch (error) {
            console.error('Failed to parse WebSocket message:', error);
        }
    }

    /**
     * Attempt to reconnect to WebSocket
     */
    private attemptReconnect(): void {
        if (this.reconnectAttempts >= this.maxReconnectAttempts) {
            console.error('Max WebSocket reconnection attempts reached');
            return;
        }

        this.reconnectAttempts++;
        console.log(`Attempting WebSocket reconnect (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`);

        setTimeout(() => {
            this.connect();
        }, this.reconnectDelay);
    }
}
