import {
  WebSocketGateway,
  WebSocketServer,
  // SubscribeMessage,
  OnGatewayConnection,
  OnGatewayDisconnect,
  // MessageBody,
  // ConnectedSocket,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
import { Logger, Inject, forwardRef } from '@nestjs/common';
import { NotificationService } from './services/notification.service';
import { WsAdminAuthGuard, type AuthenticatedSocket } from './guards/ws-admin-auth.guard';
import { ModuleRef } from '@nestjs/core';
import { ExecutionContext } from '@nestjs/common';
import { IInAppNotificationPayload } from './interfaces/notification.interface';

@WebSocketGateway({
  cors: {
    origin: true,
    credentials: true,
  },
  namespace: '/notifications',
  transports: ['websocket', 'polling'],
})
export class NotificationsGateway implements OnGatewayConnection, OnGatewayDisconnect {
  @WebSocketServer()
  server!: Server;

  private readonly logger = new Logger(NotificationsGateway.name);
  private readonly pingIntervals = new Map<string, NodeJS.Timeout>();

  constructor(
    @Inject(forwardRef(() => NotificationService))
    private readonly notificationService: NotificationService,
    private readonly moduleRef: ModuleRef,
  ) {}

  async handleConnection(client: Socket) {
    try {
      await this.authenticateConnection(client);
      const authenticatedClient = client as AuthenticatedSocket;
      this.logger.log(`Admin connected: ${authenticatedClient.adminId}`);
      this.handleJoinRoom(authenticatedClient);

      // Start ping interval for this client
      // const intervalId = setInterval(() => {
      //   void authenticatedClient.emit('ping', { message: 'ping', timestamp: new Date().toISOString() });
      // }, 2000);

      // this.pingIntervals.set(client.id, intervalId);
    } catch (error) {
      this.logger.error(
        `Connection authentication failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
      );
      void client.emit('error', { message: 'Authentication failed' });
      client.disconnect();
    }
  }

  handleDisconnect(client: Socket) {
    this.logger.log(`Client disconnected: ${client.id}`);
    this.handleLeaveRoom(client);

    // Clear ping interval for this client
    // const intervalId = this.pingIntervals.get(client.id);
    // if (intervalId) {
    //   clearInterval(intervalId);
    //   this.pingIntervals.delete(client.id);
    // }
  }

  sendInAppNotification(repipient: { adminId: string; restaurantId: string }, payload: IInAppNotificationPayload) {
    const room = this.getRoom(repipient.adminId, repipient.restaurantId);

    this.logger.log(`Sending in app notification to admin: ${repipient.adminId}`);
    this.server.to(room).emit('notifications', payload, async () => {
      // 3. ACK (delivered)
      // await this.notificationService.markAsDelivered(payload.notificationId);
    });
    this.logger.log(`In app notification sent to admin: ${repipient.adminId}`);
  }

  private getRoom(adminId: string, restaurantId: string) {
    return `restaurant:${restaurantId}-admin:${adminId}`;
  }

  handleLeaveRoom(client: AuthenticatedSocket) {
    const room = this.getRoom(client.adminId!, client.restId!);
    void client.leave(room);
    this.logger.log(`Admin ${client.adminId} (Client ${client.id}) left room: ${room}`);
    void client.emit('left', { room, message: 'Successfully Admin left room' });
  }

  handleJoinRoom(client: AuthenticatedSocket) {
    const room = this.getRoom(client.adminId!, client.restId!);
    void client.join(room);
    this.logger.log(`Admin ${client.adminId} (Client ${client.id}) joined room: ${room}`);
    void client.emit('joined', { room, message: 'Successfully Admin joined room' });
  }

  private async authenticateConnection(client: Socket): Promise<void> {
    const guard = this.moduleRef.get(WsAdminAuthGuard);
    const context = {
      switchToWs: () => ({ getClient: () => client }),
      getClass: () => NotificationsGateway,
      getHandler: () => this.handleConnection,
    } as unknown as ExecutionContext;

    const canActivate = await guard.canActivate(context);
    if (!canActivate) {
      throw new Error('Authentication failed');
    }
  }
}
