import { Body, Controller, Delete, Get, Headers, Param, Patch, Post, Query } from '@nestjs/common';
import { ApiBearerAuth, ApiBody, ApiOperation, ApiTags } from '@nestjs/swagger';
import { JwtService } from '@nestjs/jwt';
import { firstValueFrom } from 'rxjs';
import { CoreGrpcService } from '../core-grpc.service';
import { CreateBotDto, SetBotWebAppDto, SubmitBotCallbackDto, SubmitBotMessageDto, UpdateBotDto, UpdateBotProfileDto, ValidateWebAppInitDataDto } from '../dto/bot.dto';
import { getAuthorizedUserId } from '../document-access';

@ApiTags('BotFather')
@ApiBearerAuth()
@Controller('bots')
export class BotController {
  constructor(
    private readonly core: CoreGrpcService,
    private readonly jwt: JwtService
  ) {}

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

  @Get()
  @ApiOperation({ summary: 'Список моих ботов', description: 'Возвращает Telegram-ботов текущего пользователя.' })
  listMyBots(@Headers('authorization') authorization?: string) {
    return this.auth(authorization).then((userId) => firstValueFrom(this.core.bot.ListMyBots({ ownerId: userId })));
  }

  @Post()
  @ApiOperation({ summary: 'Создать бота', description: 'Регистрирует нового бота и возвращает токен Bot API.' })
  @ApiBody({ type: CreateBotDto })
  createBot(@Headers('authorization') authorization: string | undefined, @Body() dto: CreateBotDto) {
    return this.auth(authorization).then((ownerId) =>
      firstValueFrom(this.core.bot.CreateBot({ ownerId, name: dto.name, username: dto.username }))
    );
  }

  @Get('by-username/:botRef/messages')
  @ApiOperation({ summary: 'История чата с ботом', description: 'Возвращает сообщения пользователя с ботом в хронологическом порядке.' })
  listBotMessages(@Headers('authorization') authorization: string | undefined, @Param('botRef') botRef: string, @Query('roomId') roomId?: string) {
    return this.auth(authorization).then((userId) =>
      firstValueFrom(this.core.bot.ListBotChatMessages({ userId, botRef, roomId }))
    );
  }

  @Post('by-username/:botRef/messages')
  @ApiOperation({
    summary: 'Написать боту',
    description: 'Публикует inbound-событие в RabbitMQ для доставки боту через webhook или getUpdates.'
  })
  @ApiBody({ type: SubmitBotMessageDto })
  submitMessage(
    @Headers('authorization') authorization: string | undefined,
    @Param('botRef') botRef: string,
    @Body() dto: SubmitBotMessageDto
  ) {
    return this.auth(authorization).then((senderUserId) =>
      firstValueFrom(this.core.bot.SubmitBotInboundMessage({ senderUserId, botRef, text: dto.text, roomId: dto.roomId }))
    );
  }

  @Post('by-username/:botRef/callback')
  @ApiOperation({
    summary: 'Нажатие inline-кнопки',
    description: 'Публикует callback_query Update для webhook или getUpdates.'
  })
  @ApiBody({ type: SubmitBotCallbackDto })
  submitCallback(
    @Headers('authorization') authorization: string | undefined,
    @Param('botRef') botRef: string,
    @Body() dto: SubmitBotCallbackDto
  ) {
    return this.auth(authorization).then((senderUserId) =>
      firstValueFrom(
        this.core.bot.SubmitBotCallbackQuery({
          senderUserId,
          botRef,
          messageId: dto.messageId,
          callbackData: dto.callbackData
        })
      )
    );
  }

  @Post('web-app/validate')
  @ApiOperation({
    summary: 'Проверить initData Mini App',
    description: 'Валидирует Telegram Web App initData по HMAC-SHA256, как в официальном Bot API.'
  })
  @ApiBody({ type: ValidateWebAppInitDataDto })
  validateWebApp(@Body() dto: ValidateWebAppInitDataDto) {
    return firstValueFrom(this.core.bot.ValidateWebAppInitData(dto));
  }

  @Get(':botId')
  @ApiOperation({ summary: 'Получить бота', description: 'Возвращает настройки бота, принадлежащего пользователю.' })
  getBot(@Headers('authorization') authorization: string | undefined, @Param('botId') botId: string) {
    return this.auth(authorization).then((requesterId) =>
      firstValueFrom(this.core.bot.GetBot({ requesterId, botId, isSuperAdmin: false }))
    );
  }

  @Patch(':botId')
  @ApiOperation({ summary: 'Обновить бота', description: 'Изменяет имя или username бота.' })
  @ApiBody({ type: UpdateBotDto })
  updateBot(
    @Headers('authorization') authorization: string | undefined,
    @Param('botId') botId: string,
    @Body() dto: UpdateBotDto
  ) {
    return this.auth(authorization).then((requesterId) =>
      firstValueFrom(this.core.bot.UpdateBot({ requesterId, botId, ...dto, isSuperAdmin: false }))
    );
  }

  @Delete(':botId')
  @ApiOperation({ summary: 'Удалить бота', description: 'Удаляет бота и все связанные чаты/сообщения.' })
  deleteBot(@Headers('authorization') authorization: string | undefined, @Param('botId') botId: string) {
    return this.auth(authorization).then((requesterId) =>
      firstValueFrom(this.core.bot.DeleteBot({ requesterId, botId, isSuperAdmin: false }))
    );
  }

  @Post(':botId/revoke-token')
  @ApiOperation({ summary: 'Перевыпустить токен', description: 'Инвалидирует старый токен и возвращает новый.' })
  revokeToken(@Headers('authorization') authorization: string | undefined, @Param('botId') botId: string) {
    return this.auth(authorization).then((requesterId) =>
      firstValueFrom(this.core.bot.RevokeBotToken({ requesterId, botId, isSuperAdmin: false }))
    );
  }

  @Patch(':botId/profile')
  @ApiOperation({
    summary: 'Профиль бота',
    description: 'Обновляет description, aboutText, botPicUrl и глобальную кнопку меню (Web App).'
  })
  @ApiBody({ type: UpdateBotProfileDto })
  updateBotProfile(
    @Headers('authorization') authorization: string | undefined,
    @Param('botId') botId: string,
    @Body() dto: UpdateBotProfileDto
  ) {
    return this.auth(authorization).then((requesterId) =>
      firstValueFrom(this.core.bot.UpdateBotProfile({ requesterId, botId, ...dto, isSuperAdmin: false }))
    );
  }

  @Patch(':botId/web-app')
  @ApiOperation({ summary: 'Настроить Mini App', description: 'Привязывает URL Web App к боту.' })
  @ApiBody({ type: SetBotWebAppDto })
  setWebApp(
    @Headers('authorization') authorization: string | undefined,
    @Param('botId') botId: string,
    @Body() dto: SetBotWebAppDto
  ) {
    return this.auth(authorization).then((requesterId) =>
      firstValueFrom(this.core.bot.SetBotWebApp({ requesterId, botId, webAppUrl: dto.webAppUrl, isSuperAdmin: false }))
    );
  }
}
