import {
  Body,
  ClassSerializerInterceptor,
  Controller,
  Delete,
  Get,
  HttpStatus,
  Param,
  ParseFilePipeBuilder,
  Patch,
  Post,
  Query,
  UploadedFile,
  UseInterceptors,
} from '@nestjs/common';
import { RecipesService } from './recipes.service';
import {
  RetrieveRecipesQueryParamsDto,
  RetrieveRecipesResponseDto,
} from './dtos/retrieve-recipes.dto';
import { IsPublic } from '../../shared/decorators/auth/is-public.decorator';
import { IUser, User } from '../../shared/decorators/auth/user.decorator';
import { ApiBody, ApiConsumes, ApiTags } from '@nestjs/swagger';
import {
  ApiDefaultResponseStatus,
  ApiResponseStatuses,
} from '../../shared/decorators/swagger/api-response-statuses.decorator';
import {
  RetrieveRecipeParamsDto,
  RetrieveRecipeResponseDto,
} from './dtos/retrieve-recipe.dto';
import {
  RetrieveIngredientsParamsDto,
  RetrieveIngredientsResponseDto,
} from './dtos/retrieve-ingredients.dto';
import {
  RetrieveNutrientsParamsDto,
  RetrieveNutrientsResponseDto,
} from './dtos/retrieve-nutrients.dto';
import { CreateRecipeDto } from './dtos/create-recipe.dto';
import { DeleteRecipeParamsDto } from './dtos/delete-recipe.dto';
import { AddLikeToRecipeDto } from './dtos/add-like-to-recipe.dto';
import { RemoveLikeFromRecipeParamsDto } from './dtos/remove-like-from-recipe.dto';
import { RetrieveRecommendationsResponseDto } from './dtos/retrieve-recommendations.dto';
import { FileInterceptor } from '@nestjs/platform-express';
import { UploadRecipePictureParamsDto } from './dtos/upload-recipe-picture.dto';
import { DeleteRecipePictureParamsDto } from './dtos/delete-recipe-picture.dto';
import {
  RetrieveRecipesTagsQueryParamsDto,
  RetrieveRecipesTagsResponseDto,
} from './dtos/retrieve-recipes-tags.dto';
import {
  RetrieveRecipeTagParamsDto,
  RetrieveRecipeTagResponseDto,
} from './dtos/retrieve-recipe-tag.dto';
import {
  UpdateRecipeDto,
  UpdateRecipeParamsDto,
} from './dtos/update-recipe.dto';
import { SimpleNumberDto } from '../../shared/dtos/simple-number.dto';

@ApiTags('Recipes')
@Controller('recipes')
export class RecipesController {
  constructor(private readonly _recipesService: RecipesService) {}

  @Get()
  @IsPublic()
  @ApiDefaultResponseStatus(200, RetrieveRecipesResponseDto)
  @ApiResponseStatuses(400, 401, 502)
  @UseInterceptors(ClassSerializerInterceptor)
  async retrieveRecipes(
    @Query() queryParams: RetrieveRecipesQueryParamsDto,
    @User() user?: IUser,
  ): Promise<RetrieveRecipesResponseDto> {
    return this._recipesService.retrieveRecipes(queryParams, user);
  }

  @Get('count')
  @IsPublic()
  @ApiDefaultResponseStatus(200, SimpleNumberDto)
  @ApiResponseStatuses(502)
  async retrieveRecipesCount(): Promise<SimpleNumberDto> {
    return this._recipesService.retrieveRecipesCount();
  }

  @Get('recommendations')
  @IsPublic()
  @ApiDefaultResponseStatus(200, RetrieveRecommendationsResponseDto)
  @ApiResponseStatuses(502)
  @UseInterceptors(ClassSerializerInterceptor)
  async retrieveRecommendations(
    @User() user?: IUser,
  ): Promise<RetrieveRecommendationsResponseDto> {
    return this._recipesService.retrieveRecommendations(user);
  }

  @Get('tags')
  @IsPublic()
  @ApiDefaultResponseStatus(200, RetrieveRecipesTagsResponseDto)
  @ApiResponseStatuses(400, 502)
  async retrieveRecipesTags(
    @Query() queryParams: RetrieveRecipesTagsQueryParamsDto,
  ): Promise<RetrieveRecipesTagsResponseDto> {
    return this._recipesService.retrieveRecipesTags(queryParams);
  }

  @Get('tags/:id')
  @IsPublic()
  @ApiDefaultResponseStatus(200, RetrieveRecipeTagResponseDto)
  @ApiResponseStatuses(400, 404, 502)
  async retrieveRecipeTag(
    @Param() params: RetrieveRecipeTagParamsDto,
  ): Promise<RetrieveRecipeTagResponseDto> {
    return this._recipesService.retrieveRecipeTag(params);
  }

  @Get(':id')
  @IsPublic()
  @ApiDefaultResponseStatus(200, RetrieveRecipeResponseDto)
  @ApiResponseStatuses(400, 404, 502)
  @UseInterceptors(ClassSerializerInterceptor)
  async retrieveRecipe(
    @Param() params: RetrieveRecipeParamsDto,
    @User() user?: IUser,
  ): Promise<RetrieveRecipeResponseDto> {
    return this._recipesService.retrieveRecipe(params, user);
  }

  @Get(':id/ingredients')
  @IsPublic()
  @ApiDefaultResponseStatus(200, RetrieveIngredientsResponseDto)
  @ApiResponseStatuses(400, 404, 502)
  @UseInterceptors(ClassSerializerInterceptor)
  async retrieveIngredients(
    @Param() params: RetrieveIngredientsParamsDto,
    @User() user: IUser,
  ): Promise<RetrieveIngredientsResponseDto> {
    return this._recipesService.retrieveIngredients(params, user);
  }

  @Get(':id/nutrients')
  @IsPublic()
  @ApiDefaultResponseStatus(200, RetrieveNutrientsResponseDto)
  @ApiResponseStatuses(400, 404, 502)
  async retrieveNutrients(
    @Param() params: RetrieveNutrientsParamsDto,
    @User() user?: IUser,
  ): Promise<RetrieveNutrientsResponseDto> {
    return this._recipesService.retrieveNutrients(params, user);
  }

  @Post()
  @ApiDefaultResponseStatus(201, RetrieveRecipeResponseDto)
  @ApiResponseStatuses(400, 401, 409, 500, 502)
  async createRecipe(
    @Body() recipe: CreateRecipeDto,
    @User() user: IUser,
  ): Promise<RetrieveRecipeResponseDto> {
    return this._recipesService.createRecipe(recipe, user);
  }

  @Post(':id/like')
  @ApiDefaultResponseStatus(201)
  @ApiResponseStatuses(400, 401, 403, 409, 502)
  async addLikeToRecipe(
    @Param() params: AddLikeToRecipeDto,
    @User() user: IUser,
  ) {
    return this._recipesService.addLikeToRecipe(params, user);
  }

  @Post(':id/upload/picture')
  @ApiDefaultResponseStatus(200)
  @ApiResponseStatuses(400, 401, 404, 403, 500, 502)
  @ApiConsumes('multipart/form-data')
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        image: {
          type: 'file',
        },
      },
      required: ['image'],
    },
  })
  @UseInterceptors(FileInterceptor('image'))
  async uploadRecipePicture(
    @Param() params: UploadRecipePictureParamsDto,
    @User() user: IUser,
    @UploadedFile(
      new ParseFilePipeBuilder()
        .addMaxSizeValidator({
          maxSize: 8000000,
        })
        .build({
          errorHttpStatusCode: HttpStatus.BAD_REQUEST,
        }),
    )
    image: Express.Multer.File,
  ) {
    return this._recipesService.uploadRecipePicture(params, user, image);
  }

  @Patch(':id')
  @ApiDefaultResponseStatus(200, RetrieveRecipeResponseDto)
  @ApiResponseStatuses(400, 401, 403, 404, 409, 502)
  @UseInterceptors(ClassSerializerInterceptor)
  async updateRecipe(
    @Param() params: UpdateRecipeParamsDto,
    @Body() recipe: UpdateRecipeDto,
    @User() user: IUser,
  ): Promise<RetrieveRecipeResponseDto> {
    return this._recipesService.updateRecipe(params, recipe, user);
  }

  @Delete(':id')
  @ApiDefaultResponseStatus(200, RetrieveRecipeResponseDto)
  @ApiResponseStatuses(400, 401, 404, 502)
  async deleteRecipe(
    @Param() params: DeleteRecipeParamsDto,
    @User() user: IUser,
  ): Promise<RetrieveRecipeResponseDto> {
    return this._recipesService.deleteRecipe(params, user);
  }

  @Delete(':id/like')
  @ApiDefaultResponseStatus(200)
  @ApiResponseStatuses(400, 401, 502)
  async removeLikeFromRecipe(
    @Param() params: RemoveLikeFromRecipeParamsDto,
    @User() user: IUser,
  ) {
    return this._recipesService.removeLikeFromRecipe(params, user);
  }

  @Delete(':id/upload/picture')
  @ApiDefaultResponseStatus(200)
  @ApiResponseStatuses(401, 403, 404, 502)
  async deleteRecipePicture(
    @Param() params: DeleteRecipePictureParamsDto,
    @User() user: IUser,
  ) {
    return this._recipesService.deleteRecipePicture(params, user);
  }
}
