import {
  Body,
  Controller,
  Get,
  HttpException,
  HttpStatus,
  Logger,
  Param,
  Post,
  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 {
  GetWorkflowsResponse,
  CreateWorkflowsRequest,
  CreateWorkflowsResponse,
  UpdateWorkflowResponse,
  UpdateWorkflowRequest,
  UpdateWorkflowRequestParam,
  DeleteWorkflowRequestParam,
  DeleteWorkflowResponse,
} from './types/types';
import { AuthGuard } from '../../common/guards/auth/authguards';
import { RoleGuard } from '../../common/guards/role/roleguards';
import { ADMIN_ROLES } from '../../constants';
import { retrieveAuthorizationToken } from '../../common/http/helper';
import { Request } from 'express';
import { makeContext, retrieveRequestId, retrieveIp } from '../../common/log';
import { WorkflowsService } from './workflows.service';
import { makeErrorResponse } from '../../common/error/makeErrorResponse';

@ApiTags('workflows')
@Controller('workflows')
export class WorkflowsController {
  private readonly logger = new Logger(WorkflowsController.name);
  constructor(private readonly workflowsService: WorkflowsService) {}

  @ApiResponse({
    status: HttpStatus.OK,
    type: GetWorkflowsResponse,
    description: '成功時のレスポンス',
  })
  @ApiResponse({
    status: HttpStatus.UNAUTHORIZED,
    description: '認証エラー',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.INTERNAL_SERVER_ERROR,
    description: '想定外のサーバーエラー',
    type: ErrorResponse,
  })
  @ApiOperation({
    operationId: 'getWorkflows',
    description: 'アカウント内のワークフローの一覧を取得します',
  })
  @ApiBearerAuth()
  @UseGuards(AuthGuard)
  @UseGuards(
    RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN], delegation: true }),
  )
  @Get()
  async getWorkflows(@Req() req: Request): Promise<GetWorkflowsResponse> {
    const accessToken = retrieveAuthorizationToken(req);
    if (!accessToken) {
      throw new HttpException(
        makeErrorResponse('E000107'),
        HttpStatus.UNAUTHORIZED,
      );
    }
    const ip = retrieveIp(req);
    if (!ip) {
      throw new HttpException(
        makeErrorResponse('E000401'),
        HttpStatus.UNAUTHORIZED,
      );
    }

    const requestId = retrieveRequestId(req);
    if (!requestId) {
      throw new HttpException(
        makeErrorResponse('E000501'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
    const decodedAccessToken = jwt.decode(accessToken, { json: true });
    if (!decodedAccessToken) {
      throw new HttpException(
        makeErrorResponse('E000101'),
        HttpStatus.UNAUTHORIZED,
      );
    }
    const { userId } = decodedAccessToken as AccessToken;

    const context = makeContext(userId, requestId);
    this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);

    const workflows = await this.workflowsService.getWorkflows(context, userId);

    return { workflows };
  }

  @ApiResponse({
    status: HttpStatus.OK,
    type: CreateWorkflowsResponse,
    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: 'createWorkflows',
    description: 'アカウント内にワークフローを新規作成します',
  })
  @ApiBearerAuth()
  @UseGuards(AuthGuard)
  @UseGuards(
    RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN], delegation: true }),
  )
  @Post()
  async createWorkflows(
    @Req() req: Request,
    @Body() body: CreateWorkflowsRequest,
  ): Promise<CreateWorkflowsResponse> {
    const { authorId, worktypeId, templateId, typists } = body;

    const accessToken = retrieveAuthorizationToken(req);
    if (!accessToken) {
      throw new HttpException(
        makeErrorResponse('E000107'),
        HttpStatus.UNAUTHORIZED,
      );
    }
    const ip = retrieveIp(req);
    if (!ip) {
      throw new HttpException(
        makeErrorResponse('E000401'),
        HttpStatus.UNAUTHORIZED,
      );
    }

    const requestId = retrieveRequestId(req);
    if (!requestId) {
      throw new HttpException(
        makeErrorResponse('E000501'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
    const decodedAccessToken = jwt.decode(accessToken, { json: true });
    if (!decodedAccessToken) {
      throw new HttpException(
        makeErrorResponse('E000101'),
        HttpStatus.UNAUTHORIZED,
      );
    }
    const { userId } = decodedAccessToken as AccessToken;

    const context = makeContext(userId, requestId);
    this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
    await this.workflowsService.createWorkflow(
      context,
      userId,
      authorId,
      typists,
      worktypeId,
      templateId,
    );

    return {};
  }

  @ApiResponse({
    status: HttpStatus.OK,
    type: UpdateWorkflowResponse,
    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: 'updateWorkflow',
    description: 'アカウント内のワークフローを編集します',
  })
  @ApiBearerAuth()
  @UseGuards(AuthGuard)
  @UseGuards(
    RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN], delegation: true }),
  )
  @Post('/:workflowId')
  async updateWorkflow(
    @Req() req: Request,
    @Param() param: UpdateWorkflowRequestParam,
    @Body() body: UpdateWorkflowRequest,
  ): Promise<UpdateWorkflowResponse> {
    const { authorId, worktypeId, templateId, typists } = body;
    const { workflowId } = param;

    const accessToken = retrieveAuthorizationToken(req);
    if (!accessToken) {
      throw new HttpException(
        makeErrorResponse('E000107'),
        HttpStatus.UNAUTHORIZED,
      );
    }
    const ip = retrieveIp(req);
    if (!ip) {
      throw new HttpException(
        makeErrorResponse('E000401'),
        HttpStatus.UNAUTHORIZED,
      );
    }

    const requestId = retrieveRequestId(req);
    if (!requestId) {
      throw new HttpException(
        makeErrorResponse('E000501'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
    const decodedAccessToken = jwt.decode(accessToken, { json: true });
    if (!decodedAccessToken) {
      throw new HttpException(
        makeErrorResponse('E000101'),
        HttpStatus.UNAUTHORIZED,
      );
    }
    const { userId } = decodedAccessToken as AccessToken;

    const context = makeContext(userId, requestId);
    this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
    await this.workflowsService.updateWorkflow(
      context,
      userId,
      workflowId,
      authorId,
      typists,
      worktypeId,
      templateId,
    );

    return {};
  }

  @ApiResponse({
    status: HttpStatus.OK,
    type: DeleteWorkflowResponse,
    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: 'deleteWorkflow',
    description: 'アカウント内のワークフローを削除します',
  })
  @ApiBearerAuth()
  @UseGuards(AuthGuard)
  @UseGuards(
    RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN], delegation: true }),
  )
  @Post('/:workflowId/delete')
  async deleteWorkflow(
    @Req() req: Request,
    @Param() param: DeleteWorkflowRequestParam,
  ): Promise<DeleteWorkflowResponse> {
    const { workflowId } = param;

    const accessToken = retrieveAuthorizationToken(req);
    if (!accessToken) {
      throw new HttpException(
        makeErrorResponse('E000107'),
        HttpStatus.UNAUTHORIZED,
      );
    }
    const ip = retrieveIp(req);
    if (!ip) {
      throw new HttpException(
        makeErrorResponse('E000401'),
        HttpStatus.UNAUTHORIZED,
      );
    }

    const requestId = retrieveRequestId(req);
    if (!requestId) {
      throw new HttpException(
        makeErrorResponse('E000501'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
    const decodedAccessToken = jwt.decode(accessToken, { json: true });
    if (!decodedAccessToken) {
      throw new HttpException(
        makeErrorResponse('E000101'),
        HttpStatus.UNAUTHORIZED,
      );
    }
    const { userId } = decodedAccessToken as AccessToken;

    const context = makeContext(userId, requestId);
    this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
    await this.workflowsService.deleteWorkflow(context, userId, workflowId);
    return {};
  }
}
