import {
  OnGatewayConnection,
  OnGatewayInit,
  WebSocketGateway,
  WebSocketServer,
} from '@nestjs/websockets';
import { Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { JwtService } from '@nestjs/jwt';
import { Server, Socket } from 'socket.io';
import { RealtimeEmitter, userOrgRoom } from './realtime.emitter';

type AccessPayload = {
  sub?: string;
  organizationId?: string;
  type?: string;
};

@WebSocketGateway({
  namespace: '/realtime',
  cors: {
    origin: process.env.FRONTEND_URL || 'http://localhost:3001',
    credentials: true,
  },
})
export class RealtimeGateway implements OnGatewayInit, OnGatewayConnection {
  private readonly logger = new Logger(RealtimeGateway.name);

  @WebSocketServer()
  server!: Server;

  constructor(
    private readonly jwt: JwtService,
    private readonly config: ConfigService,
    private readonly emitter: RealtimeEmitter,
  ) {}

  afterInit(server: Server) {
    this.emitter.setServer(server);
  }

  async handleConnection(client: Socket) {
    try {
      const token = this.readAccessToken(client);
      if (!token) {
        client.disconnect(true);
        return;
      }

      const payload = await this.jwt.verifyAsync<AccessPayload>(token, {
        secret: this.config.get<string>('jwt.secret'),
      });

      if (!payload?.sub || !payload.organizationId || payload.type !== 'access') {
        client.disconnect(true);
        return;
      }

      const room = userOrgRoom(payload.sub, payload.organizationId);
      await client.join(room);
      client.data.userId = payload.sub;
      client.data.organizationId = payload.organizationId;
    } catch (error) {
      this.logger.debug(`Realtime auth failed: ${String(error)}`);
      client.disconnect(true);
    }
  }

  private readAccessToken(client: Socket): string | null {
    const cookieHeader = client.handshake.headers.cookie;
    if (!cookieHeader) return null;
    const parts = cookieHeader.split(';');
    for (const part of parts) {
      const [rawKey, ...rest] = part.trim().split('=');
      if (rawKey === 'accessToken') {
        return decodeURIComponent(rest.join('='));
      }
    }
    return null;
  }
}
