import { Body, Controller, Delete, Get, Headers, Param, Patch, Post, Query } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import { firstValueFrom } from 'rxjs';
import { map } from 'rxjs/operators';
import { CoreGrpcService } from '../core-grpc.service';
import { getAuthorizedUserId } from '../document-access';
import {
  AddChatRoomMemberDto,
  CreateChatRoomDto,
  CreateE2EChatRoomDto,
  EditChatMessageDto,
  ForwardMessagesDto,
  MarkRoomReadDto,
  SendChatMessageDto,
  SetMessagePinnedDto,
  SetRoomNotificationsMutedDto,
  ToggleMessageReactionDto,
  UpdateChatRoomDto,
  VotePollDto
} from '../dto/chat.dto';

@ApiTags('Чат')
@ApiBearerAuth()
@Controller('chat')
export class ChatController {
  constructor(
    private readonly core: CoreGrpcService,
    private readonly jwt: JwtService
  ) {}

  private async auth(authorization?: string) {
    return getAuthorizedUserId(this.jwt, this.core, authorization);
  }

  @Get('groups/:groupId/rooms')
  @ApiOperation({ summary: 'Список чатов семьи' })
  async listRooms(@Headers('authorization') authorization: string | undefined, @Param('groupId') groupId: string) {
    const userId = await this.auth(authorization);
    return firstValueFrom(
      this.core.chat.ListRooms({ userId, groupId }).pipe(
        map((response) => {
          const payload = response as { rooms?: unknown[] };
          return { rooms: payload.rooms ?? [] };
        })
      )
    );
  }

  @Post('groups/:groupId/rooms')
  @ApiOperation({ summary: 'Создать групповой чат' })
  async createRoom(
    @Headers('authorization') authorization: string | undefined,
    @Param('groupId') groupId: string,
    @Body() dto: CreateChatRoomDto
  ) {
    const userId = await this.auth(authorization);
    return firstValueFrom(
      this.core.chat.CreateRoom({ userId, groupId, name: dto.name, memberUserIds: dto.memberUserIds ?? [] })
    );
  }

  @Post('groups/:groupId/e2e-rooms')
  @ApiOperation({ summary: 'Создать секретный E2E чат' })
  async createE2ERoom(
    @Headers('authorization') authorization: string | undefined,
    @Param('groupId') groupId: string,
    @Body() dto: CreateE2EChatRoomDto
  ) {
    const userId = await this.auth(authorization);
    return firstValueFrom(this.core.chat.CreateE2ERoom({ userId, groupId, peerUserId: dto.peerUserId }));
  }

  @Patch('rooms/:roomId')
  @ApiOperation({ summary: 'Настройки чата' })
  async updateRoom(
    @Headers('authorization') authorization: string | undefined,
    @Param('roomId') roomId: string,
    @Body() dto: UpdateChatRoomDto
  ) {
    const userId = await this.auth(authorization);
    return firstValueFrom(
      this.core.chat.UpdateRoomSettings({
        userId,
        roomId,
        name: dto.name,
        notificationsMuted: dto.notificationsMuted,
        pinned: dto.pinned
      })
    );
  }

  @Post('rooms/:roomId/members')
  @ApiOperation({ summary: 'Добавить участника в чат' })
  async addMember(
    @Headers('authorization') authorization: string | undefined,
    @Param('roomId') roomId: string,
    @Body() dto: AddChatRoomMemberDto
  ) {
    const userId = await this.auth(authorization);
    return firstValueFrom(this.core.chat.AddRoomMember({ userId, roomId, memberUserId: dto.memberUserId }));
  }

  @Delete('rooms/:roomId/members/:memberUserId')
  @ApiOperation({ summary: 'Удалить участника из чата' })
  async removeMember(
    @Headers('authorization') authorization: string | undefined,
    @Param('roomId') roomId: string,
    @Param('memberUserId') memberUserId: string
  ) {
    const userId = await this.auth(authorization);
    return firstValueFrom(this.core.chat.RemoveRoomMember({ userId, roomId, memberUserId }));
  }

  @Get('rooms/:roomId/messages')
  @ApiOperation({ summary: 'Сообщения чата' })
  async listMessages(
    @Headers('authorization') authorization: string | undefined,
    @Param('roomId') roomId: string,
    @Query('beforeMessageId') beforeMessageId?: string,
    @Query('limit') limit?: string
  ) {
    const userId = await this.auth(authorization);
    return firstValueFrom(
      this.core.chat.ListMessages({
        userId,
        roomId,
        beforeMessageId,
        limit: limit ? Number(limit) : 50
      }).pipe(
        map((response) => {
          const payload = response as { messages?: unknown[] };
          return { messages: payload.messages ?? [] };
        })
      )
    );
  }

  @Post('rooms/:roomId/messages')
  @ApiOperation({ summary: 'Отправить сообщение' })
  async sendMessage(
    @Headers('authorization') authorization: string | undefined,
    @Param('roomId') roomId: string,
    @Body() dto: SendChatMessageDto
  ) {
    const userId = await this.auth(authorization);
    return firstValueFrom(
      this.core.chat.SendMessage({
        userId,
        roomId,
        type: dto.type,
        content: dto.content,
        replyToId: dto.replyToId,
        storageKey: dto.storageKey,
        mimeType: dto.mimeType,
        metadataJson: dto.metadataJson,
        poll: dto.poll,
        isEncrypted: dto.isEncrypted
      })
    );
  }

  @Post('messages/:messageId/vote')
  @ApiOperation({ summary: 'Голос в опросе' })
  async votePoll(
    @Headers('authorization') authorization: string | undefined,
    @Param('messageId') messageId: string,
    @Body() dto: VotePollDto
  ) {
    const userId = await this.auth(authorization);
    return firstValueFrom(this.core.chat.VotePoll({ userId, messageId, optionIds: dto.optionIds }));
  }

  @Post('rooms/:roomId/mute')
  @ApiOperation({ summary: 'Выключить/включить уведомления чата' })
  async setMuted(
    @Headers('authorization') authorization: string | undefined,
    @Param('roomId') roomId: string,
    @Body() dto: SetRoomNotificationsMutedDto
  ) {
    const userId = await this.auth(authorization);
    return firstValueFrom(this.core.chat.SetRoomNotificationsMuted({ userId, roomId, muted: dto.muted }));
  }

  @Patch('messages/:messageId')
  @ApiOperation({ summary: 'Редактировать сообщение' })
  async editMessage(
    @Headers('authorization') authorization: string | undefined,
    @Param('messageId') messageId: string,
    @Body() dto: EditChatMessageDto
  ) {
    const userId = await this.auth(authorization);
    return firstValueFrom(this.core.chat.EditMessage({ userId, messageId, content: dto.content }));
  }

  @Delete('messages/:messageId')
  @ApiOperation({ summary: 'Удалить сообщение' })
  async deleteMessage(@Headers('authorization') authorization: string | undefined, @Param('messageId') messageId: string) {
    const userId = await this.auth(authorization);
    return firstValueFrom(this.core.chat.DeleteMessage({ userId, messageId }));
  }

  @Post('messages/:messageId/pin')
  @ApiOperation({ summary: 'Закрепить или открепить сообщение' })
  async setMessagePinned(
    @Headers('authorization') authorization: string | undefined,
    @Param('messageId') messageId: string,
    @Body() dto: SetMessagePinnedDto
  ) {
    const userId = await this.auth(authorization);
    return firstValueFrom(this.core.chat.SetMessagePinned({ userId, messageId, pinned: dto.pinned }));
  }

  @Post('messages/:messageId/reactions')
  @ApiOperation({ summary: 'Поставить или снять реакцию' })
  async toggleReaction(
    @Headers('authorization') authorization: string | undefined,
    @Param('messageId') messageId: string,
    @Body() dto: ToggleMessageReactionDto
  ) {
    const userId = await this.auth(authorization);
    return firstValueFrom(this.core.chat.ToggleMessageReaction({ userId, messageId, emoji: dto.emoji }));
  }

  @Post('rooms/:roomId/forward')
  @ApiOperation({ summary: 'Переслать сообщения в другой чат' })
  async forwardMessages(
    @Headers('authorization') authorization: string | undefined,
    @Param('roomId') roomId: string,
    @Body() dto: ForwardMessagesDto
  ) {
    const userId = await this.auth(authorization);
    return firstValueFrom(
      this.core.chat.ForwardMessages({ userId, targetRoomId: roomId, messageIds: dto.messageIds }).pipe(
        map((response) => {
          const payload = response as { messages?: unknown[] };
          return { messages: payload.messages ?? [] };
        })
      )
    );
  }

  @Post('rooms/:roomId/read')
  @ApiOperation({ summary: 'Отметить сообщения чата прочитанными' })
  async markRoomRead(
    @Headers('authorization') authorization: string | undefined,
    @Param('roomId') roomId: string,
    @Body() dto: MarkRoomReadDto
  ) {
    const userId = await this.auth(authorization);
    return firstValueFrom(
      this.core.chat.MarkRoomRead({ userId, roomId, lastMessageId: dto.lastMessageId })
    );
  }

  @Post('rooms/:roomId/typing')
  @ApiOperation({ summary: 'Сообщить о наборе текста в чате' })
  async reportTyping(
    @Headers('authorization') authorization: string | undefined,
    @Param('roomId') roomId: string
  ) {
    const userId = await this.auth(authorization);
    return firstValueFrom(this.core.chat.ReportTyping({ userId, roomId }));
  }

  @Delete('rooms/:roomId')
  @ApiOperation({ summary: 'Удалить чат' })
  async deleteRoom(@Headers('authorization') authorization: string | undefined, @Param('roomId') roomId: string) {
    const userId = await this.auth(authorization);
    return firstValueFrom(this.core.chat.DeleteRoom({ userId, roomId }));
  }
}
