import * as signalR from "@microsoft/signalr";

// Usa variabile ambiente o fallback a window.location per accesso esterno
const getApiUrl = (): string => {
  // Prima controlla la variabile ambiente (deve essere non-vuota)
  const envUrl = import.meta.env.VITE_API_URL;
  if (envUrl && envUrl.trim() !== "") {
    return envUrl;
  }
  // Fallback: usa lo stesso host del frontend ma porta 5000
  const protocol = window.location.protocol;
  const hostname = window.location.hostname;
  return `${protocol}//${hostname}:5000`;
};

class SignalRService {
  private connection: signalR.HubConnection | null = null;
  private listeners: Map<
    string,
    Set<(entityType: string, action: string, data: any) => void>
  > = new Map();
  private isConnecting = false;
  private reconnectAttempts = 0;
  private maxReconnectAttempts = 10;

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

    if (this.isConnecting) {
      return;
    }

    this.isConnecting = true;

    try {
      const apiUrl = getApiUrl(); // Calcola URL al momento della connessione
      console.log(`[SignalR Data] Connecting to ${apiUrl}/hubs/data`);
      this.connection = new signalR.HubConnectionBuilder()
        .withUrl(`${apiUrl}/hubs/data`)
        .withAutomaticReconnect([0, 2000, 5000, 10000, 30000])
        .configureLogging(signalR.LogLevel.Information)
        .build();

      this.connection.on(
        "DataChanged",
        (entityType: string, action: string, data: any) => {
          this.notifyListeners(entityType, action, data);
        },
      );

      this.connection.onreconnecting((error) => {
        console.log("SignalR reconnecting...", error?.message);
      });

      this.connection.onreconnected(() => {
        console.log("SignalR reconnected");
        this.reconnectAttempts = 0;
      });

      this.connection.onclose((error) => {
        console.log("SignalR disconnected", error?.message);
        this.scheduleReconnect();
      });

      await this.connection.start();
      console.log("SignalR connected successfully");
      this.reconnectAttempts = 0;
    } catch (error) {
      console.error("SignalR connection error:", error);
      this.scheduleReconnect();
    } finally {
      this.isConnecting = false;
    }
  }

  private scheduleReconnect(): void {
    if (this.reconnectAttempts < this.maxReconnectAttempts) {
      this.reconnectAttempts++;
      const delay = Math.min(2000 * Math.pow(2, this.reconnectAttempts), 60000);
      console.log(
        `SignalR: Reconnecting in ${delay / 1000}s (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`,
      );
      setTimeout(() => this.connect(), delay);
    } else {
      console.error("SignalR: Max reconnection attempts reached");
    }
  }

  subscribe(
    callback: (entityType: string, action: string, data: any) => void,
  ): () => void {
    const key = "global";
    if (!this.listeners.has(key)) {
      this.listeners.set(key, new Set());
    }
    this.listeners.get(key)!.add(callback);

    // Connect if not connected
    this.connect();

    // Return unsubscribe function
    return () => {
      this.listeners.get(key)?.delete(callback);
    };
  }

  subscribeToEntity(
    entityType: string,
    callback: (action: string, data: any) => void,
  ): () => void {
    if (!this.listeners.has(entityType)) {
      this.listeners.set(entityType, new Set());
    }

    const wrappedCallback = (et: string, action: string, data: any) => {
      if (et === entityType) {
        callback(action, data);
      }
    };

    this.listeners.get(entityType)!.add(wrappedCallback);
    this.connect();

    return () => {
      this.listeners.get(entityType)?.delete(wrappedCallback);
    };
  }

  private notifyListeners(entityType: string, action: string, data: any): void {
    // Notify global listeners
    this.listeners.get("global")?.forEach((callback) => {
      try {
        callback(entityType, action, data);
      } catch (e) {
        console.error("Error in SignalR listener:", e);
      }
    });

    // Notify entity-specific listeners
    this.listeners.get(entityType)?.forEach((callback) => {
      try {
        callback(entityType, action, data);
      } catch (e) {
        console.error("Error in SignalR listener:", e);
      }
    });
  }

  async notifyChange(
    entityType: string,
    action: string,
    data?: any,
  ): Promise<void> {
    if (this.connection?.state === signalR.HubConnectionState.Connected) {
      try {
        await this.connection.invoke(
          "NotifyDataChanged",
          entityType,
          action,
          data,
        );
      } catch (error) {
        console.error("Error sending SignalR notification:", error);
      }
    }
  }

  disconnect(): void {
    this.connection?.stop();
    this.connection = null;
  }
}

export const signalRService = new SignalRService();
