import {
  Body,
  Controller,
  Get,
  HttpException,
  HttpStatus,
  Post,
  Query,
  Req,
  UseGuards,
} from '@nestjs/common';
import {
  ApiBearerAuth,
  ApiOperation,
  ApiResponse,
  ApiTags,
} from '@nestjs/swagger';
import { Request } from 'express';
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
import { ErrorResponse } from '../../common/error/types/types';
import { retrieveAuthorizationToken } from '../../common/http/helper';
import { AccessToken } from '../../common/token';
import {
  ConfirmRequest,
  ConfirmResponse,
  GetRelationsResponse,
  GetUsersResponse,
  SignupRequest,
  SignupResponse,
  PostSortCriteriaRequest,
  PostSortCriteriaResponse,
  GetSortCriteriaRequest,
  GetSortCriteriaResponse,
  PostUpdateUserRequest,
  PostUpdateUserResponse,
} from './types/types';
import { UsersService } from './users.service';
import jwt from 'jsonwebtoken';
import { AuthGuard } from '../../common/guards/auth/authguards';
import {
  isSortDirection,
  isTaskListSortableAttribute,
} from '../../common/types/sort';
import { ADMIN_ROLES } from '../../constants';
import { RoleGuard } from '../../common/guards/role/roleguards';
import { makeContext } from '../../common/log';

@ApiTags('users')
@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @ApiResponse({
    status: HttpStatus.OK,
    type: ConfirmResponse,
    description: '成功時のレスポンス',
  })
  @ApiResponse({
    status: HttpStatus.BAD_REQUEST,
    description: '不正なトークン',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.INTERNAL_SERVER_ERROR,
    description: '想定外のサーバーエラー',
    type: ErrorResponse,
  })
  @ApiOperation({ operationId: 'confirmUser' })
  @Post('confirm')
  async confirmUser(@Body() body: ConfirmRequest): Promise<ConfirmResponse> {
    await this.usersService.confirmUser(body.token);
    return {};
  }

  @ApiResponse({
    status: HttpStatus.OK,
    type: ConfirmResponse,
    description: '成功時のレスポンス',
  })
  @ApiResponse({
    status: HttpStatus.BAD_REQUEST,
    description: '不正なトークン',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.INTERNAL_SERVER_ERROR,
    description: '想定外のサーバーエラー',
    type: ErrorResponse,
  })
  @ApiOperation({ operationId: 'confirmUserAndInitPassword' })
  @Post('confirm/initpassword')
  async confirmUserAndInitPassword(
    @Body() body: ConfirmRequest,
  ): Promise<ConfirmResponse> {
    console.log(body);
    await this.usersService.confirmUserAndInitPassword(body.token);
    return {};
  }

  @ApiResponse({
    status: HttpStatus.OK,
    type: GetUsersResponse,
    description: '成功時のレスポンス',
  })
  @ApiResponse({
    status: HttpStatus.UNAUTHORIZED,
    description: '認証エラー',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.INTERNAL_SERVER_ERROR,
    description: '想定外のサーバーエラー',
    type: ErrorResponse,
  })
  @ApiOperation({ operationId: 'getUsers' })
  @ApiBearerAuth()
  @UseGuards(AuthGuard)
  @UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] }))
  @Get()
  async getUsers(@Req() req: Request): Promise<GetUsersResponse> {
    const accessToken = retrieveAuthorizationToken(req);
    const decodedToken = jwt.decode(accessToken, { json: true }) as AccessToken;

    const users = await this.usersService.getUsers(decodedToken.userId);
    return { users };
  }

  @ApiResponse({
    status: HttpStatus.OK,
    type: SignupResponse,
    description: '成功時のレスポンス',
  })
  @ApiResponse({
    status: HttpStatus.BAD_REQUEST,
    description: '登録済みメールによる再登録、AuthorIDの重複など',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.UNAUTHORIZED,
    description: '認証エラー',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.INTERNAL_SERVER_ERROR,
    description: '想定外のサーバーエラー',
    type: ErrorResponse,
  })
  @ApiOperation({ operationId: 'signup' })
  @ApiBearerAuth()
  @Post('/signup')
  @UseGuards(AuthGuard)
  @UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] }))
  async signup(
    @Req() req: Request,
    @Body() body: SignupRequest,
  ): Promise<SignupResponse> {
    const {
      name,
      role,
      email,
      autoRenew,
      licenseAlert,
      notification,
      authorId,
      typistGroupId,
    } = body;

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

    //ユーザ作成処理
    await this.usersService.createUser(
      payload,
      name,
      role,
      email,
      autoRenew,
      licenseAlert,
      notification,
      authorId,
      typistGroupId,
    );
    return {};
  }

  @ApiResponse({
    status: HttpStatus.OK,
    type: GetRelationsResponse,
    description: '成功時のレスポンス',
  })
  @ApiResponse({
    status: HttpStatus.UNAUTHORIZED,
    description: '認証エラー',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.INTERNAL_SERVER_ERROR,
    description: '想定外のサーバーエラー',
    type: ErrorResponse,
  })
  @ApiOperation({
    operationId: 'getRelations',
    description: 'ログインしているユーザーに関連する各種情報を取得します',
  })
  @ApiBearerAuth()
  @UseGuards(AuthGuard)
  @Get('relations')
  async getRelations(@Req() req: Request): Promise<GetRelationsResponse> {
    const token = retrieveAuthorizationToken(req);
    const { userId } = jwt.decode(token, { json: true }) as AccessToken;

    const context = makeContext(userId);

    return await this.usersService.getRelations(context, userId);
  }

  @ApiResponse({
    status: HttpStatus.OK,
    type: PostSortCriteriaResponse,
    description: '成功時のレスポンス',
  })
  @ApiResponse({
    status: HttpStatus.UNAUTHORIZED,
    description: '認証エラー',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.BAD_REQUEST,
    description: '不正なパラメータ',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.INTERNAL_SERVER_ERROR,
    description: '想定外のサーバーエラー',
    type: ErrorResponse,
  })
  @ApiOperation({
    operationId: 'updateSortCriteria',
    description: 'ログインしているユーザーのタスクソート条件を更新します',
  })
  @ApiBearerAuth()
  @UseGuards(AuthGuard)
  @Post('sort-criteria')
  async updateSortCriteria(
    @Body() body: PostSortCriteriaRequest,
    @Req() req: Request,
  ): Promise<PostSortCriteriaResponse> {
    const { direction, paramName } = body;
    const accessToken = retrieveAuthorizationToken(req);
    const decodedToken = jwt.decode(accessToken, { json: true }) as AccessToken;

    //型チェック
    if (
      !isTaskListSortableAttribute(paramName) ||
      !isSortDirection(direction)
    ) {
      throw new HttpException(
        makeErrorResponse('E010001'),
        HttpStatus.BAD_REQUEST,
      );
    }
    await this.usersService.updateSortCriteria(
      paramName,
      direction,
      decodedToken,
    );
    return {};
  }

  @ApiResponse({
    status: HttpStatus.OK,
    type: GetSortCriteriaResponse,
    description: '成功時のレスポンス',
  })
  @ApiResponse({
    status: HttpStatus.UNAUTHORIZED,
    description: '認証エラー',
    type: ErrorResponse,
  })
  @ApiResponse({
    status: HttpStatus.INTERNAL_SERVER_ERROR,
    description: '想定外のサーバーエラー',
    type: ErrorResponse,
  })
  @ApiOperation({
    operationId: 'getSortCriteria',
    description: 'ログインしているユーザーのタスクソート条件を取得します',
  })
  @ApiBearerAuth()
  @UseGuards(AuthGuard)
  @Get('sort-criteria')
  async getSortCriteria(
    @Query() query: GetSortCriteriaRequest,
    @Req() req: Request,
  ): Promise<GetSortCriteriaResponse> {
    const {} = query;
    const accessToken = retrieveAuthorizationToken(req);
    const decodedToken = jwt.decode(accessToken, { json: true }) as AccessToken;

    const { direction, paramName } = await this.usersService.getSortCriteria(
      decodedToken,
    );
    return { direction, paramName };
  }

  @ApiResponse({
    status: HttpStatus.OK,
    type: PostUpdateUserResponse,
    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: 'updateUser',
    description: 'ユーザーの情報を更新します',
  })
  @ApiBearerAuth()
  @UseGuards(AuthGuard)
  @UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] }))
  @Post('update')
  async updateUser(
    @Body() body: PostUpdateUserRequest,
    @Req() req: Request,
  ): Promise<PostUpdateUserResponse> {
    const accessToken = retrieveAuthorizationToken(req);
    const decodedToken = jwt.decode(accessToken, { json: true }) as AccessToken;

    console.log(body);
    console.log(decodedToken);
    return {};
  }
}
