import {
  Body,
  Controller,
  Get,
  HttpStatus,
  Post,
  Query,
  Req,
  UseGuards,
} from '@nestjs/common';
import {
  ApiBearerAuth,
  ApiOperation,
  ApiResponse,
  ApiTags,
} from '@nestjs/swagger';
import jwt from 'jsonwebtoken';
import { AccessToken } from '../../common/token';
import { ErrorResponse } from '../../common/error/types/types';
import { FilesService } from './files.service';
import {
  AudioDownloadLocationRequest,
  AudioDownloadLocationResponse,
  AudioUploadFinishedRequest,
  AudioUploadFinishedResponse,
  AudioUploadLocationRequest,
  AudioUploadLocationResponse,
  TemplateDownloadLocationRequest,
  TemplateDownloadLocationResponse,
} from './types/types';
import { AuthGuard } from '../../common/guards/auth/authguards';
import { RoleGuard } from '../../common/guards/role/roleguards';
import { USER_ROLES } from '../../constants';
import { retrieveAuthorizationToken } from '../../common/http/helper';
import { Request } from 'express';
import { makeContext } from '../../common/log';

@ApiTags('files')
@Controller('files')
export class FilesController {
  constructor(private readonly filesService: FilesService) {}

  @ApiResponse({
    status: HttpStatus.OK,
    type: AudioUploadFinishedResponse,
    description: '成功時のレスポンス',
  })
  @ApiResponse({
    status: HttpStatus.BAD_REQUEST,
    description: '不正なパラメータ',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.UNAUTHORIZED,
    description: '認証エラー',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.INTERNAL_SERVER_ERROR,
    description: '想定外のサーバーエラー',
    type: ErrorResponse,
  })
  @ApiOperation({
    operationId: 'uploadFinished',
    description:
      'アップロードが完了した音声ファイルの情報を登録し、文字起こしタスクを生成します',
  })
  @ApiBearerAuth()
  @UseGuards(AuthGuard)
  @UseGuards(RoleGuard.requireds({ roles: [USER_ROLES.AUTHOR] }))
  @Post('audio/upload-finished')
  async uploadFinished(
    @Req() req: Request,
    @Body() body: AudioUploadFinishedRequest,
  ): Promise<AudioUploadFinishedResponse> {
    const token = retrieveAuthorizationToken(req);
    const accessToken = jwt.decode(token, { json: true }) as AccessToken;

    const context = makeContext(accessToken.userId);

    const {
      url,
      authorId,
      fileName,
      duration,
      createdDate,
      finishedDate,
      uploadedDate,
      fileSize,
      priority,
      audioFormat,
      comment,
      workType,
      optionItemList,
      isEncrypted,
    } = body;

    const res = await this.filesService.uploadFinished(
      context,
      accessToken.userId,
      url,
      authorId,
      fileName,
      duration,
      createdDate,
      finishedDate,
      uploadedDate,
      fileSize,
      priority,
      audioFormat,
      comment,
      workType,
      optionItemList,
      isEncrypted,
    );

    return { jobNumber: res.jobNumber };
  }

  @Get('audio/upload-location')
  @ApiResponse({
    status: HttpStatus.OK,
    type: AudioUploadLocationResponse,
    description: '成功時のレスポンス',
  })
  @ApiResponse({
    status: HttpStatus.UNAUTHORIZED,
    description: '認証エラー',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.INTERNAL_SERVER_ERROR,
    description: '想定外のサーバーエラー',
    type: ErrorResponse,
  })
  @ApiOperation({
    operationId: 'uploadLocation',
    description:
      'ログイン中ユーザー用のBlob Storage上の音声ファイルのアップロード先アクセスURLを取得します',
  })
  @ApiBearerAuth()
  @UseGuards(AuthGuard)
  @UseGuards(RoleGuard.requireds({ roles: [USER_ROLES.AUTHOR] }))
  async uploadLocation(
    @Req() req: Request,
    // クエリパラメータ AudioUploadLocationRequest は空であるため内部で使用しない。
    // 使用しないことを宣言するために先頭にプレフィックス_（アンダースコア）をつけている
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    @Query() _query: AudioUploadLocationRequest,
  ): Promise<AudioUploadLocationResponse> {
    const token = retrieveAuthorizationToken(req);
    const accessToken = jwt.decode(token, { json: true }) as AccessToken;

    const context = makeContext(accessToken.userId);

    const url = await this.filesService.publishUploadSas(context, accessToken);
    return { url };
  }

  @Get('audio/download-location')
  @ApiResponse({
    status: HttpStatus.OK,
    type: AudioDownloadLocationResponse,
    description: '成功時のレスポンス',
  })
  @ApiResponse({
    status: HttpStatus.BAD_REQUEST,
    description: '不正なパラメータ',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.UNAUTHORIZED,
    description: '認証エラー',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.INTERNAL_SERVER_ERROR,
    description: '想定外のサーバーエラー',
    type: ErrorResponse,
  })
  @ApiOperation({
    operationId: 'downloadLocation',
    description:
      '指定した音声ファイルのBlob Storage上のダウンロード先アクセスURLを取得します',
  })
  @ApiBearerAuth()
  @UseGuards(AuthGuard)
  @UseGuards(
    RoleGuard.requireds({ roles: [USER_ROLES.AUTHOR, USER_ROLES.TYPIST] }),
  )
  async downloadLocation(
    @Req() req: Request,
    @Query() body: AudioDownloadLocationRequest,
  ): Promise<AudioDownloadLocationResponse> {
    const { audioFileId } = body;

    const token = retrieveAuthorizationToken(req);
    const accessToken = jwt.decode(token, { json: true }) as AccessToken;

    const context = makeContext(accessToken.userId);

    const url = await this.filesService.publishAudioFileDownloadSas(
      context,
      accessToken.userId,
      audioFileId,
    );

    return { url };
  }

  @Get('template/download-location')
  @ApiResponse({
    status: HttpStatus.OK,
    type: TemplateDownloadLocationResponse,
    description: '成功時のレスポンス',
  })
  @ApiResponse({
    status: HttpStatus.BAD_REQUEST,
    description: '不正なパラメータ',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.UNAUTHORIZED,
    description: '認証エラー',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.INTERNAL_SERVER_ERROR,
    description: '想定外のサーバーエラー',
    type: ErrorResponse,
  })
  @ApiOperation({
    operationId: 'downloadTemplateLocation',
    description:
      '指定した音声ファイルに対応したテンプレートファイルのBlob Storage上のダウンロード先アクセスURLを取得します',
  })
  @ApiBearerAuth()
  @UseGuards(AuthGuard)
  @UseGuards(
    RoleGuard.requireds({ roles: [USER_ROLES.AUTHOR, USER_ROLES.TYPIST] }),
  )
  async downloadTemplateLocation(
    @Req() req: Request,
    @Query() body: TemplateDownloadLocationRequest,
  ): Promise<TemplateDownloadLocationResponse> {
    const { audioFileId } = body;

    const token = retrieveAuthorizationToken(req);
    const accessToken = jwt.decode(token, { json: true }) as AccessToken;

    const context = makeContext(accessToken.userId);

    const url = await this.filesService.publishTemplateFileDownloadSas(
      context,
      accessToken.userId,
      audioFileId,
    );

    return { url };
  }
}
