import { Logger, OnModuleDestroy } from '@nestjs/common';
import {
  OnGatewayConnection,
  OnGatewayDisconnect,
  WebSocketGateway,
} from '@nestjs/websockets';
import type { Socket } from 'socket.io';
import { MobileSystemNotificationsService } from '../system-notifications/mobile-system-notifications.service';
import {
  MobileSession,
  MobileTransportService,
} from './mobile-transport.service';

@WebSocketGateway({
  namespace: '/mobile-notifications',
  transports: ['websocket'],
  cors: {
    origin: [
      'https://mis-stage.datacolabx.com',
      'https://mis-pro.datacolabx.com',
    ],
  },
})
export class MobileNotificationsGateway
  implements OnGatewayConnection, OnGatewayDisconnect, OnModuleDestroy
{
  private readonly logger = new Logger(MobileNotificationsGateway.name);
  private readonly clients = new Map<
    Socket,
    { identity: MobileSession; snapshot?: string }
  >();
  private timer?: ReturnType<typeof setTimeout>;
  private stopped = false;
  constructor(
    private readonly transport: MobileTransportService,
    private readonly notifications: MobileSystemNotificationsService,
  ) {}

  async handleConnection(socket: Socket) {
    if (process.env.MOBILE_REALTIME_ENABLED !== 'true')
      return socket.disconnect(true);
    try {
      const identity = await this.transport.authenticateTicket(
        socket.handshake.auth?.ticket,
      );
      if (!socket.connected) return;
      this.clients.set(socket, { identity });
      socket.emit('ready');
      if (!this.timer) this.schedule();
    } catch {
      socket.disconnect(true);
    }
  }
  handleDisconnect(socket: Socket) {
    this.clients.delete(socket);
  }
  private schedule() {
    if (this.stopped) return;
    this.timer = setTimeout(() => {
      void this.poll();
    }, 5000);
    this.timer.unref();
  }
  private async poll() {
    try {
      for (const [socket, client] of this.clients) {
        try {
          if (!(await this.transport.sessionIsActive(client.identity))) {
            this.clients.delete(socket);
            socket.disconnect(true);
            continue;
          }
          const page = await this.notifications.findAllForCurrentUser(
            client.identity.userId,
            { page: 1, limit: 100 },
          );
          const snapshot = JSON.stringify([
            page.meta,
            page.data.map((n) => [n.id, n.updatedAt]),
          ]);
          if (snapshot !== client.snapshot) {
            client.snapshot = snapshot;
            // Refresh only. FCM is the single source of phone alerts, avoiding duplicates.
            socket.emit('notifications.changed');
          }
        } catch {
          this.logger.warn('Mobile realtime refresh failed; retrying.');
        }
      }
    } finally {
      this.timer = undefined;
      if (this.clients.size) this.schedule();
    }
  }
  onModuleDestroy() {
    this.stopped = true;
    clearTimeout(this.timer);
    for (const socket of this.clients.keys()) socket.disconnect(true);
    this.clients.clear();
  }
}
