import {
  WebSocketGateway,
  WebSocketServer,
  SubscribeMessage,
  MessageBody,
  ConnectedSocket,
  OnGatewayConnection,
  OnGatewayDisconnect,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import { MessagesService } from './messages.service';

@WebSocketGateway({ cors: { origin: '*' }, namespace: '/chat' })
export class MessagesGateway implements OnGatewayConnection, OnGatewayDisconnect {
  @WebSocketServer() server: Server;

  constructor(
    private messages: MessagesService,
    private jwt: JwtService,
    private config: ConfigService,
  ) {}

  async handleConnection(client: Socket) {
    try {
      const token = client.handshake.auth.token ?? client.handshake.headers.authorization?.replace('Bearer ', '');
      const payload = this.jwt.verify(token, { secret: this.config.get('JWT_SECRET') });
      client.data.userId = payload.sub;
    } catch {
      client.disconnect();
    }
  }

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

  @SubscribeMessage('join-group')
  async handleJoinGroup(@MessageBody() groupId: string, @ConnectedSocket() client: Socket) {
    const allowed = await this.messages.isMember(groupId, client.data.userId);
    if (!allowed) return client.emit('error', 'Access denied');
    client.join(groupId);
    client.emit('joined', { groupId });
  }

  @SubscribeMessage('leave-group')
  handleLeaveGroup(@MessageBody() groupId: string, @ConnectedSocket() client: Socket) {
    client.leave(groupId);
  }

  @SubscribeMessage('send-message')
  async handleMessage(
    @MessageBody() data: { groupId: string; body: string; type?: string; mediaUrl?: string },
    @ConnectedSocket() client: Socket,
  ) {
    const allowed = await this.messages.isMember(data.groupId, client.data.userId);
    if (!allowed) return client.emit('error', 'Access denied');

    const message = await this.messages.create(
      data.groupId,
      client.data.userId,
      data.body,
      data.type,
      data.mediaUrl,
    );

    this.server.to(data.groupId).emit('new-message', message);
    return message;
  }

  @SubscribeMessage('typing')
  handleTyping(
    @MessageBody() data: { groupId: string; isTyping: boolean },
    @ConnectedSocket() client: Socket,
  ) {
    client.to(data.groupId).emit('user-typing', {
      userId: client.data.userId,
      isTyping: data.isTyping,
    });
  }

  @SubscribeMessage('mark-read')
  async handleMarkRead(
    @MessageBody() messageId: string,
    @ConnectedSocket() client: Socket,
  ) {
    return this.messages.markRead(messageId, client.data.userId);
  }
}
