import * as signalR from '@microsoft/signalr';
import { PublishingProgressUpdate, BuildStatusUpdate } from '../types';

export class SignalRService {
  private connection: signalR.HubConnection | null = null;
  private listeners: Map<string, Set<(data: any) => void>> = new Map();

  async start(): Promise<void> {
    if (this.connection?.state === signalR.HubConnectionState.Connected) {
      return;
    }

    this.connection = new signalR.HubConnectionBuilder()
      .withUrl('/hubs/publishing', {
        accessTokenFactory: () => localStorage.getItem('authToken') || '',
      })
      .withAutomaticReconnect()
      .build();

    this.setupEventHandlers();

    try {
      await this.connection.start();
      console.log('SignalR connected successfully');
    } catch (error) {
      console.error('SignalR connection failed:', error);
      throw error;
    }
  }

  async stop(): Promise<void> {
    if (this.connection) {
      await this.connection.stop();
      this.connection = null;
      this.listeners.clear();
    }
  }

  private setupEventHandlers(): void {
    if (!this.connection) return;

    // Publishing progress updates
    this.connection.on('PublishingProgress', (update: PublishingProgressUpdate) => {
      this.notifyListeners('PublishingProgress', update);
    });

    // Build status updates
    this.connection.on('BuildStatusUpdate', (update: BuildStatusUpdate) => {
      this.notifyListeners('BuildStatusUpdate', update);
    });

    // Publication completed
    this.connection.on('PublicationCompleted', (publicationId: number) => {
      this.notifyListeners('PublicationCompleted', publicationId);
    });

    // Publication failed
    this.connection.on('PublicationFailed', (data: { publicationId: number; error: string }) => {
      this.notifyListeners('PublicationFailed', data);
    });
  }

  private notifyListeners(event: string, data: any): void {
    const eventListeners = this.listeners.get(event);
    if (eventListeners) {
      eventListeners.forEach(callback => callback(data));
    }
  }

  subscribe<T>(event: string, callback: (data: T) => void): () => void {
    if (!this.listeners.has(event)) {
      this.listeners.set(event, new Set());
    }
    
    const eventListeners = this.listeners.get(event)!;
    eventListeners.add(callback);

    // Return unsubscribe function
    return () => {
      eventListeners.delete(callback);
      if (eventListeners.size === 0) {
        this.listeners.delete(event);
      }
    };
  }

  async joinPublicationGroup(publicationId: number): Promise<void> {
    if (this.connection?.state === signalR.HubConnectionState.Connected) {
      await this.connection.invoke('JoinPublicationGroup', publicationId.toString());
    }
  }

  async leavePublicationGroup(publicationId: number): Promise<void> {
    if (this.connection?.state === signalR.HubConnectionState.Connected) {
      await this.connection.invoke('LeavePublicationGroup', publicationId.toString());
    }
  }

  get connectionState(): signalR.HubConnectionState {
    return this.connection?.state || signalR.HubConnectionState.Disconnected;
  }

  get isConnected(): boolean {
    return this.connection?.state === signalR.HubConnectionState.Connected;
  }
}

export const signalRService = new SignalRService();