import {
  Body,
  Controller,
  Get,
  Headers,
  HttpStatus,
  Param,
  ParseIntPipe,
  Post,
  Query,
  Req,
  UseGuards,
} from '@nestjs/common';
import {
  ApiResponse,
  ApiOperation,
  ApiTags,
  ApiBearerAuth,
  ApiParam,
} from '@nestjs/swagger';
import { ErrorResponse } from '../../common/error/types/types';
import { Request } from 'express';
import { TasksService } from './tasks.service';
import {
  AudioNextRequest,
  AudioNextResponse,
  ChangeStatusRequest,
  ChangeStatusResponse,
  PostCheckoutPermissionRequest,
  PostCheckoutPermissionResponse,
  TasksRequest,
  TasksResponse,
} from './types/types';
import {
  isSortDirection,
  isTaskListSortableAttribute,
} from '../../common/types/sort';
import jwt from 'jsonwebtoken';
import { retrieveAuthorizationToken } from '../../common/http/helper';
import { AccessToken } from '../../common/token';
import { AuthGuard } from '../../common/guards/auth/authguards';
import { RoleGuard } from '../../common/guards/role/roleguards';
import { ADMIN_ROLES, USER_ROLES } from '../../constants';
import { Roles } from '../../common/types/role';
import { makeContext } from '../../common/log';

@ApiTags('tasks')
@Controller('tasks')
export class TasksController {
  constructor(private readonly taskService: TasksService) {}

  @ApiResponse({
    status: HttpStatus.OK,
    type: TasksResponse,
    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: 'getTasks',
    description: '音声ファイル・文字起こしタスク情報をページ指定して取得します',
  })
  @ApiBearerAuth()
  @UseGuards(AuthGuard)
  @Get()
  async getTasks(
    @Req() req,
    @Query() body: TasksRequest,
  ): Promise<TasksResponse> {
    const accessToken = retrieveAuthorizationToken(req);
    const decodedToken = jwt.decode(accessToken, { json: true }) as AccessToken;

    const context = makeContext(decodedToken.userId);

    const { limit, offset, status } = body;
    const paramName = isTaskListSortableAttribute(body.paramName)
      ? body.paramName
      : undefined;
    const direction = isSortDirection(body.direction)
      ? body.direction
      : undefined;

    const { tasks, total } = await this.taskService.getTasks(
      context,
      decodedToken,
      offset,
      limit,
      status?.split(',') ?? [],
      paramName,
      direction,
    );
    return { tasks, total, limit, offset };
  }

  @Get('next')
  @ApiResponse({
    status: HttpStatus.OK,
    type: AudioNextResponse,
    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: 'getNextAudioFile',
    description:
      '指定した文字起こしタスクの次のタスクに紐づく音声ファイルIDを取得します',
  })
  @ApiBearerAuth()
  async getNextAudioFile(
    @Headers() headers,
    @Query() body: AudioNextRequest,
  ): Promise<AudioNextResponse> {
    const { endedFileId } = body;
    console.log(endedFileId);

    return { nextFileId: 1234 };
  }

  @Post(':audioFileId/checkout')
  @ApiResponse({
    status: HttpStatus.OK,
    type: ChangeStatusResponse,
    description: '成功時のレスポンス',
  })
  @ApiResponse({
    status: HttpStatus.BAD_REQUEST,
    description: '不正なパラメータ',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.NOT_FOUND,
    description: '指定したIDの音声ファイルが存在しない場合',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.UNAUTHORIZED,
    description: '認証エラー',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.INTERNAL_SERVER_ERROR,
    description: '想定外のサーバーエラー',
    type: ErrorResponse,
  })
  @ApiOperation({
    operationId: 'checkout',
    description:
      '指定した文字起こしタスクをチェックアウトします（ステータスをInprogressにします）',
  })
  @ApiBearerAuth()
  @UseGuards(AuthGuard)
  @UseGuards(
    RoleGuard.requireds({
      roles: [USER_ROLES.AUTHOR, USER_ROLES.TYPIST],
    }),
  )
  async checkout(
    @Req() req: Request,
    @Param() param: ChangeStatusRequest,
  ): Promise<ChangeStatusResponse> {
    // AuthGuardでチェック済みなのでここでのアクセストークンチェックはしない
    const accessToken = retrieveAuthorizationToken(req);
    const { role, userId } = jwt.decode(accessToken, {
      json: true,
    }) as AccessToken;

    // RoleGuardでroleの文字列に想定外の文字列や重複がないことは担保されているためここでは型変換のみ行う
    const roles = role.split(' ') as Roles[];

    const context = makeContext(userId);

    await this.taskService.checkout(context, param.audioFileId, roles, userId);
    return {};
  }

  @Post(':audioFileId/checkin')
  @ApiResponse({
    status: HttpStatus.OK,
    type: ChangeStatusResponse,
    description: '成功時のレスポンス',
  })
  @ApiResponse({
    status: HttpStatus.BAD_REQUEST,
    description: '不正なパラメータ',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.NOT_FOUND,
    description: '指定したIDの音声ファイルが存在しない場合',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.UNAUTHORIZED,
    description: '認証エラー',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.INTERNAL_SERVER_ERROR,
    description: '想定外のサーバーエラー',
    type: ErrorResponse,
  })
  @ApiOperation({
    operationId: 'checkin',
    description:
      '指定した文字起こしタスクをチェックインします（ステータスをFinishedにします）',
  })
  @ApiBearerAuth()
  @UseGuards(AuthGuard)
  @UseGuards(
    RoleGuard.requireds({
      roles: [USER_ROLES.TYPIST],
    }),
  )
  async checkin(
    @Req() req: Request,
    @Param() params: ChangeStatusRequest,
  ): Promise<ChangeStatusResponse> {
    const { audioFileId } = params;
    // AuthGuardでチェック済みなのでここでのアクセストークンチェックはしない
    const accessToken = retrieveAuthorizationToken(req);
    const { userId } = jwt.decode(accessToken, {
      json: true,
    }) as AccessToken;

    const context = makeContext(userId);

    await this.taskService.checkin(context, audioFileId, userId);
    return {};
  }

  @Post(':audioFileId/cancel')
  @ApiResponse({
    status: HttpStatus.OK,
    type: ChangeStatusResponse,
    description: '成功時のレスポンス',
  })
  @ApiResponse({
    status: HttpStatus.BAD_REQUEST,
    description: '不正なパラメータ',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.NOT_FOUND,
    description: '指定したIDの音声ファイルが存在しない場合',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.UNAUTHORIZED,
    description: '認証エラー',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.INTERNAL_SERVER_ERROR,
    description: '想定外のサーバーエラー',
    type: ErrorResponse,
  })
  @ApiOperation({
    operationId: 'cancel',
    description:
      '指定した文字起こしタスクをキャンセルします（ステータスをUploadedにします）',
  })
  @UseGuards(AuthGuard)
  @UseGuards(
    RoleGuard.requireds({
      roles: [ADMIN_ROLES.ADMIN, USER_ROLES.TYPIST],
    }),
  )
  @ApiBearerAuth()
  async cancel(
    @Req() req: Request,
    @Param() params: ChangeStatusRequest,
  ): Promise<ChangeStatusResponse> {
    const { audioFileId } = params;
    // AuthGuardでチェック済みなのでここでのアクセストークンチェックはしない
    const accessToken = retrieveAuthorizationToken(req);
    const { userId, role } = jwt.decode(accessToken, {
      json: true,
    }) as AccessToken;
    // RoleGuardでroleの文字列に想定外の文字列や重複がないことは担保されているためここでは型変換のみ行う
    const roles = role.split(' ') as Roles[];

    const context = makeContext(userId);

    await this.taskService.cancel(context, audioFileId, userId, roles);
    return {};
  }

  @Post(':audioFileId/suspend')
  @ApiResponse({
    status: HttpStatus.OK,
    type: ChangeStatusResponse,
    description: '成功時のレスポンス',
  })
  @ApiResponse({
    status: HttpStatus.BAD_REQUEST,
    description: '不正なパラメータ',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.NOT_FOUND,
    description: '指定したIDの音声ファイルが存在しない場合',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.UNAUTHORIZED,
    description: '認証エラー',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.INTERNAL_SERVER_ERROR,
    description: '想定外のサーバーエラー',
    type: ErrorResponse,
  })
  @ApiOperation({
    operationId: 'suspend',
    description:
      '指定した文字起こしタスクを一時中断します（ステータスをPendingにします）',
  })
  @ApiBearerAuth()
  @UseGuards(AuthGuard)
  @UseGuards(
    RoleGuard.requireds({
      roles: [USER_ROLES.TYPIST],
    }),
  )
  async suspend(
    @Req() req: Request,
    @Param() params: ChangeStatusRequest,
  ): Promise<ChangeStatusResponse> {
    const { audioFileId } = params;
    // AuthGuardでチェック済みなのでここでのアクセストークンチェックはしない
    const accessToken = retrieveAuthorizationToken(req);
    const { userId } = jwt.decode(accessToken, {
      json: true,
    }) as AccessToken;

    const context = makeContext(userId);

    await this.taskService.suspend(context, audioFileId, userId);
    return {};
  }

  @Post(':audioFileId/send-back')
  @ApiResponse({
    status: HttpStatus.OK,
    type: ChangeStatusResponse,
    description: '成功時のレスポンス',
  })
  @ApiResponse({
    status: HttpStatus.BAD_REQUEST,
    description: '不正なパラメータ',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.NOT_FOUND,
    description: '指定したIDの音声ファイルが存在しない場合',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.UNAUTHORIZED,
    description: '認証エラー',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.INTERNAL_SERVER_ERROR,
    description: '想定外のサーバーエラー',
    type: ErrorResponse,
  })
  @ApiOperation({
    operationId: 'sendBack',
    description:
      '指定した文字起こしタスクを差し戻します（ステータスをPendingにします）',
  })
  @ApiBearerAuth()
  async sendBack(
    @Headers() headers,
    @Param() params: ChangeStatusRequest,
  ): Promise<ChangeStatusResponse> {
    const { audioFileId } = params;
    console.log(audioFileId);

    return {};
  }

  @Post(':audioFileId/backup')
  @ApiResponse({
    status: HttpStatus.OK,
    type: ChangeStatusResponse,
    description: '成功時のレスポンス',
  })
  @ApiResponse({
    status: HttpStatus.BAD_REQUEST,
    description: '不正なパラメータ',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.NOT_FOUND,
    description: '指定したIDの音声ファイルが存在しない場合',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.UNAUTHORIZED,
    description: '認証エラー',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.INTERNAL_SERVER_ERROR,
    description: '想定外のサーバーエラー',
    type: ErrorResponse,
  })
  @ApiOperation({
    operationId: 'backup',
    description:
      '指定した文字起こしタスクをバックアップします（ステータスをBackupにします）',
  })
  @ApiBearerAuth()
  async backup(
    @Headers() headers,
    @Param() params: ChangeStatusRequest,
  ): Promise<ChangeStatusResponse> {
    const { audioFileId } = params;
    console.log(audioFileId);

    return {};
  }

  @Post(':audioFileId/checkout-permission')
  @ApiResponse({
    status: HttpStatus.OK,
    type: PostCheckoutPermissionResponse,
    description: '成功時のレスポンス',
  })
  @ApiResponse({
    status: HttpStatus.BAD_REQUEST,
    description:
      '不正なパラメータ（タスクのステータス不正、指定ユーザー不正など）',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.NOT_FOUND,
    description: '指定したIDの音声ファイルが存在しない',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.UNAUTHORIZED,
    description: '認証エラー',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.INTERNAL_SERVER_ERROR,
    description: '想定外のサーバーエラー',
    type: ErrorResponse,
  })
  @ApiOperation({
    operationId: 'changeCheckoutPermission',
    description: '指定した文字起こしタスクのチェックアウト候補を変更します。',
  })
  @ApiParam({
    name: 'audioFileId',
    required: true,
    description: 'ODMS Cloud上の音声ファイルID',
  })
  @ApiBearerAuth()
  @UseGuards(AuthGuard)
  @UseGuards(
    RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN, USER_ROLES.AUTHOR] }),
  )
  async changeCheckoutPermission(
    @Req() req: Request,
    //TODO [Task2243] checkoutやcheckinと同じパスパラメータなので記述方法を統一したい
    @Param(`audioFileId`, ParseIntPipe)
    audioFileId: number,
    @Body() body: PostCheckoutPermissionRequest,
  ): Promise<PostCheckoutPermissionResponse> {
    const { assignees } = body;
    const accessToken = retrieveAuthorizationToken(req);

    const { role, userId } = jwt.decode(accessToken, {
      json: true,
    }) as AccessToken;
    // RoleGuardでroleの文字列に想定外の文字列や重複がないことは担保されているためここでは型変換のみ行う
    const roles = role.split(' ') as Roles[];

    const context = makeContext(userId);

    await this.taskService.changeCheckoutPermission(
      context,
      audioFileId,
      assignees,
      userId,
      roles,
    );

    return {};
  }
}
