import {
  Body,
  Controller,
  Delete,
  Get,
  HttpCode,
  HttpStatus,
  Param,
  Post,
  Put,
  Query,
  UploadedFile,
  UseGuards,
  UseInterceptors,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import {
  ApiBearerAuth,
  ApiConsumes,
  ApiParam,
  ApiQuery,
  ApiTags,
} from '@nestjs/swagger';
import { ErrorService } from 'src/error/error.service';
import { User } from 'src/utils/decorators/user.decorator';
import { multerOptions } from 'src/utils/multer.options';
import { sort } from 'src/utils/sort';
import { successfulResult } from '../../utils';
import { Filter } from '../../utils/decorators/filter.decorator';
import { Limit } from '../../utils/decorators/limit.decorator';
import { Page } from '../../utils/decorators/page.decorator';
import { Sort } from '../../utils/decorators/sort.decorator';
import { JwtAuthGuard } from '../auth/jwt/jwt.guard';
import { Permissions } from '../auth/permission/permissions.decorator';
import { PermissionsGuard } from '../auth/permission/permissions.guard';
import { LogAction, LogType } from '../log/log.interface';
import { LogService } from '../log/log.service';
import { PermissionsType } from '../role/entities/permission.entity';
import { CreateLinkAttributeToProductDto } from './dto/create-link-attribute-to-product.dto';
import { CreateNotifListDto } from './dto/create-notif-list.dto';
import { CreateProductPictureDto } from './dto/create-product-picture.dto';
import { CreateProductDto } from './dto/create-product.dto';
import { CreateQuantityUnitDto } from './dto/create-quantity-unit.dto';
import { ProductMakerBrandDto } from './dto/product-maker-brand.dto';
import { ProductSortDto } from './dto/product-sort.dto';
import { UpdateProductDto } from './dto/update-product.dto';
import { UpdateQuantityUnitDto } from './dto/update-quantity-unit.dto';
import { ProductEntity } from './entities/product.entity';
import { ProductService } from './product.service';
import { Access } from '../../utils/decorators/access.decorator';

@ApiTags('Product')
@Controller('products')
export class ProductController {
  constructor(
    private productService: ProductService,
    private error: ErrorService,
    private logService: LogService,
  ) {}

  /**
   * -------------------------------------------------------
   * GET /products/statistic/admin
   */
  @Get('statistic/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.GET_ALL_PRODUCT_STATISTICS)
  async statistic() {
    const data = await this.productService.statistic();
    return successfulResult([], data);
  }

  /**
   * -------------------------------------------------------
   * show sales of products
   * GET /products/statistic/sales/admin
   */
  @Get('statistic/sales/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(
    PermissionsType.GET_ALL_PRODUCT_STATISTICS,
    PermissionsType.GET_PRODUCT_STATISTICS,
  )
  @ApiQuery({
    name: 'date',
    required: false,
    description: 'e.g. gte:2022-04-16[and]lte:2022-04-16',
  })
  async statisticSales(
    @Filter([['date', 'order.createdAt', 'DATE']])
    filters,
    @User('id') userId: number,
    @Access(PermissionsType.GET_ALL_ORDER_STATISTICS) accessAll: boolean,
  ) {
    const data = await this.productService.statisticSales(
      filters,
      userId,
      accessAll,
    );
    return successfulResult([], data);
  }

  /**
   * -------------------------------------------------------
   */
  @Get('all/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.GET_PRODUCT)
  @ApiQuery({ name: 'only_creator', required: false, description: 'e.g. 1' })
  async all(
    @Query('only_creator') onlyCreator: string,
    @User('id') userId: number,
  ) {
    const data = await this.productService.getFull(userId, onlyCreator === '1');
    return successfulResult([], data);
  }

  /**
   * -------------------------------------------------------
   * products/1/pictures/admin
   */
  @Get(':id/pictures/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.GET_PRODUCT_PICTURE)
  async getPictureByProductId(@Param('id') productId: string) {
    const data = await this.productService.getPictureByProductId(productId);
    return successfulResult([], data);
  }

  /**
   * -------------------------------------------------------
   * Get all quantity units
   * GET /products/quantity-unit/admin
   */
  @Get('quantity-unit/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.GET_PRODUCT_QUANTITY_UNIT)
  async getQuantityUnits() {
    const data = await this.productService.getQuantityUnits();
    return successfulResult([], data);
  }

  /**
   * -------------------------------------------------------
   * GET /products/quantity-unit/1/admin
   */
  @Get('quantity-unit/:id/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.GET_PRODUCT_QUANTITY_UNIT)
  async getQuantityUnitById(@Param('id') id: number) {
    const data = await this.productService.getQuantityUnitById(id);
    return successfulResult([], data);
  }

  /**
   * -------------------------------------------------------
   * POST /products/quantity-unit/admin
   */
  @Post('quantity-unit/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.ADD_PRODUCT_QUANTITY_UNIT)
  @HttpCode(HttpStatus.CREATED)
  async addQuantityUnit(
    @Body() dto: CreateQuantityUnitDto,
    @User('id') operatorUserId: string,
  ) {
    const data = await this.productService.addQuantityUnit(dto, operatorUserId);
    return successfulResult(['واحد اندازه گیری جدید، باموفقیت اضافه شد'], data);
  }

  /**
   * -------------------------------------------------------
   * PUT /products/sort/admin
   */
  @Put('sort/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.SORT_PRODUCT)
  async sort(@Body() dto: ProductSortDto, @User('id') operatorUserId: string) {
    const result = await sort(ProductEntity.name, dto.id, dto.action);

    if (!result) {
      return this.error.unprocessableEntity(['داده جایگزینی یافت نشد']);
    }

    // log
    const product = await this.productService.getRawById(dto.id);

    await this.logService.add({
      type: LogType.product,
      action: LogAction.update,
      operatorUserId,
      message: `مرتب سازی محصول ${product.name} انجام شد.`,
      affectedId: dto.id,
    });

    return successfulResult(['داده مورد نظر با موفقیت مرتب شد']);
  }

  /**
   * -------------------------------------------------------
   * PUT /products/quantity-unit/1/admin
   */
  @Put('quantity-unit/:id/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.UPDATE_PRODUCT_QUANTITY_UNIT)
  async updateQuantityUnit(
    @Param('id') id: number,
    @Body() dto: UpdateQuantityUnitDto,
    @User('id') operatorUserId: string,
  ) {
    await this.productService.updateQuantityUnit(id, dto, operatorUserId);
    return successfulResult([' واحد اندازه گیری موردنظر، باموفقیت آپدیت شد']);
  }

  /**
   * -------------------------------------------------------
   * DELETE /products/quantity-unit/1/admin
   */
  @Delete('quantity-unit/:id/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.DELETE_PRODUCT_QUANTITY_UNIT)
  async deleteQuantityUnit(@Param('id') id: number) {
    await this.productService.deleteQuantityUnit(id);
    return successfulResult(['واحد اندازه گیری موردنظر، با موفقیت حذف شد']);
  }

  /**
   * -------------------------------------------------------
   * GET /products/waitings/admin
   */
  @Get('waitings/admin')
  @ApiQuery({ name: 'name', required: false, description: 'e.g. like:%abc%' })
  @ApiQuery({ name: 'category_id', required: false, description: 'e.g. 1' })
  @ApiQuery({ name: 'product_id', required: false, description: 'e.g. 1' })
  @ApiQuery({ name: 'sort', required: false, description: 'e.g. id:desc' })
  @ApiQuery({ name: 'limit', required: false, description: 'Default: 20' })
  @ApiQuery({ name: 'page', required: false, description: 'Default: 1' })
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.GET_ALL_PRODUCT)
  async waitingList(
    @Page() page,
    @Limit() limit,
    @Sort() sorts,
    @Filter([
      ['category_id', 'categoryId'],
      ['product_id', 'productId'],
      'name',
    ])
    filters,
  ) {
    const data = await this.productService.waitingList(
      page,
      limit,
      filters,
      sorts,
    );
    return successfulResult([], data);
  }

  /**
   * -------------------------------------------------------
   * Get all products
   * GET /products/admin
   */
  @Get('admin')
  @ApiQuery({ name: 'product_id_for_mapping', required: false })
  @ApiQuery({ name: 'wage', required: false, description: 'e.g. 1' })
  @ApiQuery({ name: 'maker_brand_id', required: false, description: 'e.g. 1' })
  @ApiQuery({ name: 'name', required: false, description: 'e.g. like:%abc%' })
  @ApiQuery({ name: 'category_id', required: false, description: 'e.g. 1' })
  @ApiQuery({ name: 'product_id', required: false, description: 'e.g. 1' })
  @ApiQuery({ name: 'quantity', required: false, description: 'e.g. gt:0' })
  @ApiQuery({ name: 'published', required: false, description: 'e.g. 1' })
  @ApiQuery({ name: 'deleted', required: false, description: 'e.g. 1' })
  @ApiQuery({ name: 'is_service', required: false, description: 'e.g. 1' })
  @ApiQuery({
    name: 'has_notif_quantity',
    required: false,
    description: 'e.g. 1',
  })
  @ApiQuery({
    name: 'date',
    required: false,
    description: 'e.g. gte:2022-04-16[and]lte:2022-04-16',
  })
  @ApiQuery({ name: 'sort', required: false, description: 'e.g. id:desc' })
  @ApiQuery({ name: 'limit', required: false, description: 'Default: 20' })
  @ApiQuery({ name: 'page', required: false, description: 'Default: 1' })
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.GET_PRODUCT, PermissionsType.GET_ALL_PRODUCT)
  async list(
    @Page() page,
    @Limit() limit,
    @Sort() sorts,
    @Filter([
      ['date', 'createdAt', 'DATE'],
      ['category_id', 'categoryId'],
      ['product_id', 'productId'],
      ['quantity', 'stockQuantity'],
      ['is_service', 'category.isService'],
      'name',
      'published',
      'deleted',
    ])
    filters,
    @Query('maker_brand_id') makerBrandId,
    @Query('has_notif_quantity') hasNotifQuantity,
    @Query('product_id_for_mapping') productIdForMapping: string,
    @Query('wage') wage: number,
    @User('id') userId: number,
    @Access(PermissionsType.GET_ALL_PRODUCT) accessAll: boolean,
  ) {
    const data = await this.productService.getAll(
      page,
      limit,
      filters,
      sorts,
      makerBrandId,
      hasNotifQuantity === '1',
      productIdForMapping,
      wage,
      userId,
      accessAll,
    );
    return successfulResult([], data);
  }

  /**
   * -------------------------------------------------------
   * Get all products for mapping
   * GET /products/mapping/admin
   */
  @Get('mapping/admin')
  @ApiQuery({ name: 'maker_brand_id_for_mapping', required: false })
  @ApiQuery({ name: 'wage', required: false, description: 'e.g. 1' })
  @ApiQuery({ name: 'name', required: false, description: 'e.g. like:%abc%' })
  @ApiQuery({ name: 'category_id', required: false, description: 'e.g. 1' })
  @ApiQuery({ name: 'product_id', required: false, description: 'e.g. 1' })
  @ApiQuery({ name: 'sort', required: false, description: 'e.g. id:desc' })
  @ApiQuery({ name: 'limit', required: false, description: 'Default: 20' })
  @ApiQuery({ name: 'page', required: false, description: 'Default: 1' })
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.GET_PRODUCT)
  async getAllForMapping(
    @Page() page,
    @Limit() limit,
    @Sort() sorts,
    @Filter([
      ['category_id', 'categoryId'],
      ['product_id', 'productId'],
      'name',
    ])
    filters,
    @Query('maker_brand_id_for_mapping') makerBrandIdForMapping: string,
    @Query('wage') wage: number,
  ) {
    const data = await this.productService.getAllForMapping(
      page,
      limit,
      filters,
      sorts,
      makerBrandIdForMapping,
      wage,
    );
    return successfulResult([], data);
  }

  /**
   * -------------------------------------------------------
   * GET by Id
   */
  @Get(':id/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.GET_PRODUCT)
  async getById(@Param('id') id: string) {
    const data = await this.productService.getById(id);
    return successfulResult([], data);
  }

  /**
   * -------------------------------------------------------
   * GET count pending requests by productId
   */
  @Get('notify-quantity/:productId/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.COUNT_WAITING_CUSTOMER_FOR_PRODUCT_QUANTITY)
  async getPendingNotifCountByProductId(@Param('productId') productId: string) {
    const data = await this.productService.getPendingNotifCountByProductId(
      productId,
    );
    return successfulResult([], data);
  }

  /**
   * -------------------------------------------------------
   * POST notif list for product quantity
   */
  @Post('notify-quantity/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.NOTIFY_WAITING_CUSTOMER_FOR_PRODUCT_QUANTITY)
  async sendNotifForList(
    @Body() dto: CreateNotifListDto,
    @User('id') operatorUserId: string,
  ) {
    const data = await this.productService.sendNotifForList(
      dto,
      operatorUserId,
    );
    return successfulResult(
      ['اطلاع رسانی به لیست مورد نظر با موفقیت انجام شد'],
      data,
    );
  }

  /**
   * -------------------------------------------------------
   * POST /products/admin
   */
  @Post('admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.ADD_PRODUCT, PermissionsType.ADD_ALL_PRODUCT)
  @ApiConsumes('multipart/form-data')
  @UseInterceptors(FileInterceptor('image', multerOptions('products')))
  @HttpCode(HttpStatus.CREATED)
  async addProduct(
    @Body() dto: CreateProductDto,
    @UploadedFile() image,
    @User('id') operatorUserId: string,
    @Access(PermissionsType.ADD_ALL_PRODUCT) accessAll: boolean,
  ) {
    const data = await this.productService.addProduct(
      dto,
      image,
      operatorUserId,
      accessAll,
    );

    let message = 'محصول جدید با موفقیت ثبت شد';
    if (!accessAll) {
      message = 'محصول جدید پس از تایید مدیر انتشار خواهد یافت';
    }

    return successfulResult([message], data);
  }

  /**
   * -------------------------------------------------------
   * POST /products/picture/admin
   */
  @Post('picture/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.ADD_PRODUCT_PICTURE)
  @ApiConsumes('multipart/form-data')
  @UseInterceptors(FileInterceptor('image', multerOptions('products')))
  @HttpCode(HttpStatus.CREATED)
  async addProductPicture(
    @Body() dto: CreateProductPictureDto,
    @UploadedFile() image,
    @User('id') operatorUserId: string,
  ) {
    await this.productService.addProductPicture(dto, image, operatorUserId);
    return successfulResult(['عکس موردنظر، برای این محصول، اضافه شد']);
  }

  /**
   * -------------------------------------------------------
   * DELETE /products/picture/admin?picture_id=1&product_id=2
   */
  @Delete('picture/admin')
  @ApiQuery({ name: 'picture_id', required: true, description: 'e.g. 1' })
  @ApiQuery({ name: 'product_id', required: true, description: 'e.g. 1' })
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.DELETE_PRODUCT_PICTURE)
  async deleteProductPicture(
    @Query('picture_id') pictureId,
    @Query('product_id') productId,
    @User('id') operatorUserId: string,
  ) {
    await this.productService.deleteProductPicture(
      pictureId,
      productId,
      operatorUserId,
    );
    return successfulResult(['عکس موردنظر، حذف شد']);
  }

  /**
   * -------------------------------------------------------
   * PUT /products/maker-brands/mapping/:id/admin
   */
  @Put('maker-brands/mapping/:id/admin')
  @ApiParam({ name: 'id', description: 'Product ID' })
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(
    PermissionsType.LINK_PRODUCT_TO_MAKER_BRAND,
    PermissionsType.LINK_ALL_PRODUCT_TO_MAKER_BRAND,
  )
  async makerBrandMapping(
    @Param('id') productId: string,
    @Body() dto: ProductMakerBrandDto,
    @User('id') operatorUserId: string,
    @Access(PermissionsType.LINK_ALL_PRODUCT_TO_MAKER_BRAND) accessAll: boolean,
  ) {
    await this.productService.makerBrandMapping(
      productId,
      dto,
      operatorUserId,
      accessAll,
    );
    return successfulResult(['تغییرات با موفقیت اعمال شد']);
  }

  /**
   * -------------------------------------------------------
   * PUT /products/:id/waiting/admin
   */
  @Put(':id/waiting/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.UPDATE_ALL_PRODUCT)
  async confirmWaiting(@Param('id') id: string) {
    await this.productService.confirmWaiting(id);

    return successfulResult(['محصول موردنظر تعیین وضعیت شد']);
  }

  /**
   * -------------------------------------------------------
   * PUT /products/:id/admin
   */
  @Put(':id/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(
    PermissionsType.UPDATE_PRODUCT,
    PermissionsType.UPDATE_ALL_PRODUCT,
  )
  @ApiConsumes('multipart/form-data')
  @UseInterceptors(FileInterceptor('image', multerOptions('products')))
  async update(
    @Param('id') id: string,
    @Body() dto: UpdateProductDto,
    @UploadedFile() image,
    @User('id') operatorUserId: string,
    @Access(PermissionsType.UPDATE_ALL_PRODUCT) accessAll: boolean,
  ) {
    await this.productService.updateById(
      id,
      dto,
      image,
      operatorUserId,
      accessAll,
    );

    let message = 'تغییرات با موفقیت اعمال شد';
    if (!accessAll) {
      message = 'تغییرات پس از تایید مدیر اعمال خواهد شد';
    }

    return successfulResult([message]);
  }

  /**
   * -------------------------------------------------------
   * POST /products/attribute/mapping/admin
   */
  @Post('attribute/mapping/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.ADD_PRODUCT_ATTRIBUTE)
  @HttpCode(HttpStatus.CREATED)
  async linkAttributeToProduct(
    @Body() dto: CreateLinkAttributeToProductDto,
    @User('id') operatorUserId: string,
  ) {
    const data = await this.productService.linkAttributeToProduct(
      dto,
      operatorUserId,
    );
    return successfulResult(
      ['ویژگی موردنظر به این محصول، باموفقیت اضافه شد'],
      data,
    );
  }

  /**
   * -------------------------------------------------------
   * DELETE /products/attribute/mapping/admin
   */
  @Delete('attribute/mapping/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.DELETE_PRODUCT_ATTRIBUTE)
  async unlinkAttributeToProduct(
    @Query('product_id') productId: string,
    @Query('option_id') optionId: number,
    @User('id') operatorUserId: string,
  ) {
    await this.productService.unlinkAttributeToProduct(
      productId,
      optionId,
      operatorUserId,
    );
    return successfulResult(['ویژگی موردنظر از این محصول، باموفقیت حذف شد']);
  }

  /**
   * -------------------------------------------------------
   * DELETE /products/1/admin
   */
  @Delete(':id/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(
    PermissionsType.DELETE_PRODUCT,
    PermissionsType.DELETE_ALL_PRODUCT,
  )
  async deleteProduct(
    @Param('id') id: number,
    @User('id') operatorUserId: string,
    @Access(PermissionsType.DELETE_ALL_PRODUCT) accessAll: boolean,
  ) {
    await this.productService.deleteProduct(id, operatorUserId, accessAll);

    let message = 'محصول موردنظر، با موفقیت حذف شد';
    if (!accessAll) {
      message = 'محصول موردنظر پس از تایید مدیر حذف خواهد شد';
    }

    return successfulResult([message]);
  }
}
