import {
  WebSocketGateway,
  SubscribeMessage,
  MessageBody,
  WebSocketServer,
  ConnectedSocket,
  OnGatewayConnection,
  OnGatewayDisconnect,
} from '@nestjs/websockets';
import { UseGuards } from '@nestjs/common';
import { Server, Socket } from 'socket.io';
import { JwtService } from '@nestjs/jwt';
import { WsJwtGuard } from '../auth/guards';
import { WhiteboardService } from './whiteboard.service';
import {
  WhiteboardDrawDto,
  WhiteboardClearDto,
  WhiteboardJoinDto,
  WhiteboardCursorDto,
} from './dto/whiteboard-draw.dto';
import { Logger } from '@nestjs/common';

@WebSocketGateway({
  cors: {
    origin: [
      'http://localhost:5173',
      'http://127.0.0.1:5173',
      'http://localhost:5500',
      'http://127.0.0.1:5500',
      'http://localhost:3000',
    ],
    credentials: true,
  },
  namespace: '/whiteboard',
})
export class WhiteboardGateway implements OnGatewayConnection, OnGatewayDisconnect {
  @WebSocketServer()
  server: Server;

  private readonly logger = new Logger(WhiteboardGateway.name);
  private userSockets: Map<string, { whiteboardId: string; userId: string }> = new Map();

  constructor(
    private readonly jwtService: JwtService,
    private readonly whiteboardService: WhiteboardService,
  ) {}

  handleConnection(client: Socket) {
    try {
      const token = this.extractTokenFromHandshake(client);

      if (!token) {
        this.logger.log(`Client ${client.id} rejected: No token provided`);
        client.emit('error', { message: 'Unauthorized: No token provided' });
        client.disconnect();
        return;
      }

      const payload = this.jwtService.verify(token, {
        secret: process.env.JWT_SECRET || 'your-super-secret-jwt-key-change-this-in-production',
      });

      // User-Daten zum Socket hinzufügen
      client.data.user = payload;

      this.logger.log(`Client connected: ${client.id}, User: ${payload.username}`);
    } catch (error) {
      this.logger.log(`Client ${client.id} rejected: Invalid token`);
      client.emit('error', { message: 'Unauthorized: Invalid token' });
      client.disconnect();
    }
  }

  private extractTokenFromHandshake(client: Socket): string | null {
    // Token aus Cookie extrahieren
    const cookies = client.handshake.headers.cookie;
    if (cookies) {
      const cookieArray = cookies.split(';');
      for (const cookie of cookieArray) {
        const [name, value] = cookie.trim().split('=');
        if (name === 'access_token') {
          return value;
        }
      }
    }

    // Fallback: Token aus auth object
    const token = client.handshake.auth?.token;
    if (token) {
      return token;
    }

    // Fallback: Token aus query parameter
    const queryToken = client.handshake.query?.token as string;
    if (queryToken) {
      return queryToken;
    }

    return null;
  }

  handleDisconnect(client: Socket) {
    this.logger.log(`Client disconnected: ${client.id}`);
    const userData = this.userSockets.get(client.id);

    if (userData) {
      this.whiteboardService.leaveWhiteboard(userData.whiteboardId, userData.userId);

      // Benachrichtige andere Benutzer
      this.server.to(userData.whiteboardId).emit('user-left', {
        userId: userData.userId,
        activeUsers: this.whiteboardService.getActiveUsers(userData.whiteboardId),
      });

      this.userSockets.delete(client.id);
    }
  }

  @UseGuards(WsJwtGuard)
  @SubscribeMessage('join-whiteboard')
  handleJoinWhiteboard(
    @MessageBody() data: WhiteboardJoinDto,
    @ConnectedSocket() client: Socket,
  ) {
    const { whiteboardId, userId } = data;

    this.logger.log(`User ${userId} joining whiteboard ${whiteboardId}`);

    // Verlasse altes Whiteboard falls vorhanden
    const oldData = this.userSockets.get(client.id);
    if (oldData) {
      client.leave(oldData.whiteboardId);
      this.whiteboardService.leaveWhiteboard(oldData.whiteboardId, oldData.userId);
    }

    // Trete neuem Whiteboard bei
    client.join(whiteboardId);
    this.whiteboardService.joinWhiteboard(whiteboardId, userId);
    this.userSockets.set(client.id, { whiteboardId, userId });

    // Sende aktuelle Whiteboard-Daten an den neuen Benutzer
    const currentData = this.whiteboardService.getWhiteboardData(whiteboardId);
    client.emit('whiteboard-state', currentData);

    // Benachrichtige andere Benutzer über den neuen Teilnehmer
    const activeUsers = this.whiteboardService.getActiveUsers(whiteboardId);
    this.server.to(whiteboardId).emit('user-joined', {
      userId,
      activeUsers,
    });

    return {
      success: true,
      activeUsers,
    };
  }

  @UseGuards(WsJwtGuard)
  @SubscribeMessage('draw')
  handleDraw(
    @MessageBody() data: WhiteboardDrawDto,
    @ConnectedSocket() client: Socket,
  ) {
    const { whiteboardId } = data;

    // Speichere die Zeichnung
    const drawingData = this.whiteboardService.addDrawing(whiteboardId, data);

    // Sende die Zeichnung an alle anderen Benutzer im Whiteboard (außer dem Sender)
    client.to(whiteboardId).emit('drawing', drawingData);

    return { success: true };
  }

  @UseGuards(WsJwtGuard)
  @SubscribeMessage('clear-whiteboard')
  handleClearWhiteboard(
    @MessageBody() data: WhiteboardClearDto,
    @ConnectedSocket() client: Socket,
  ) {
    const { whiteboardId, userId } = data;

    this.logger.log(`User ${userId} clearing whiteboard ${whiteboardId}`);

    // Lösche das Whiteboard
    this.whiteboardService.clearWhiteboard(whiteboardId);

    // Benachrichtige alle Benutzer
    this.server.to(whiteboardId).emit('whiteboard-cleared', { userId });

    return { success: true };
  }

  @UseGuards(WsJwtGuard)
  @SubscribeMessage('cursor-move')
  handleCursorMove(
    @MessageBody() data: WhiteboardCursorDto,
    @ConnectedSocket() client: Socket,
  ) {
    const { whiteboardId, userId, x, y } = data;

    // Sende Cursor-Position an alle anderen Benutzer
    client.to(whiteboardId).emit('cursor-position', {
      userId,
      x,
      y,
    });
  }

  @UseGuards(WsJwtGuard)
  @SubscribeMessage('undo')
  handleUndo(
    @MessageBody() data: { whiteboardId: string; userId: string },
    @ConnectedSocket() client: Socket,
  ) {
    const { whiteboardId, userId } = data;

    this.logger.log(`User ${userId} requesting undo on whiteboard ${whiteboardId}`);

    // Benachrichtige alle Benutzer über Undo
    this.server.to(whiteboardId).emit('undo-requested', { userId });

    return { success: true };
  }

  @UseGuards(WsJwtGuard)
  @SubscribeMessage('get-active-users')
  handleGetActiveUsers(
    @MessageBody() data: { whiteboardId: string },
  ) {
    const activeUsers = this.whiteboardService.getActiveUsers(data.whiteboardId);
    return { activeUsers };
  }
}
