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 { CarService } from './car.service';
import { CreateCarDto } from './dto/create-car.dto';
import { CreateMakerBrandDto } from './dto/create-maker-brand.dto';
import { CreateMakerDto } from './dto/create-maker.dto';
import { MakerBrandProductDto } from './dto/maker-brand-product.dto';
import { MakerBrandSortDto } from './dto/maker-brand-sort.dto';
import { MakerSortDto } from './dto/maker-sort.dto';
import { UpdateCarDto } from './dto/update-car.dto';
import { UpdateMakerBrandDto } from './dto/update-maker-brand.dto';
import { UpdateMakerDto } from './dto/update-maker.dto';
import { MakerBrandEntity } from './entities/maker-brand.entity';
import { MakerEntity } from './entities/maker.entity';

@ApiTags('Car')
@Controller('cars')
export class CarController {
  constructor(
    private carService: CarService,
    private error: ErrorService,
    private logService: LogService,
  ) {}

  /**
   * -------------------------------------------------------
   */
  @Get('maker-brands/all/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.GET_MAKER_BRAND)
  async all() {
    const data = await this.carService.getMakerBrandFull();
    return successfulResult([], data);
  }

  /**
   * -------------------------------------------------------
   * GET /maker-brands/admin
   */
  @Get('maker-brands/admin')
  @ApiQuery({ name: 'product_id_for_mapping', required: false })
  @ApiQuery({
    name: 'name',
    required: false,
    description: 'e.g. like:%abc%',
  })
  @ApiQuery({
    name: 'maker_id',
    required: false,
    description: 'e.g. like:%abc%',
  })
  @ApiQuery({
    name: 'wage',
    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: 'deleted', 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_MAKER_BRAND)
  async makerBrandsList(
    @Page() page,
    @Limit() limit,
    @Sort() sorts,
    @Filter([
      ['date', 'createdAt', 'DATE'],
      ['maker_id', 'makerId'],
      'name',
      'deleted',
    ])
    filters,
    @Query('product_id_for_mapping') productIdForMapping: string,
    @Query('wage') wage: number,
  ) {
    const data = await this.carService.getAllMakerBrands(
      page,
      limit,
      filters,
      sorts,
      productIdForMapping,
      wage,
    );
    return successfulResult([], data);
  }

  /**
   * -------------------------------------------------------
   * GET /makers/admin
   */
  @Get('makers/admin')
  @ApiQuery({ name: 'name', required: false, description: 'e.g. like:%abc%' })
  @ApiQuery({ name: 'is_internal', 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: 'deleted', 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_MAKER)
  async makersList(
    @Page() page,
    @Limit() limit,
    @Sort() sorts,
    @Filter([
      ['date', 'createdAt', 'DATE'],
      'name',
      ['is_internal', 'isInternal'],
      'deleted',
    ])
    filters,
  ) {
    const data = await this.carService.getAllMakers(
      page,
      limit,
      filters,
      sorts,
    );
    return successfulResult([], data);
  }

  /**
   * -------------------------------------------------------
   * GET /cars/admin
   * GET /cars?user_id=djd4-hfhf-ssd3-sd
   */
  @Get('admin')
  @ApiQuery({ name: 'user_id', required: false })
  @ApiQuery({ name: 'deleted', required: false })
  @ApiQuery({ name: 'maker_brand_id', required: false })
  @ApiQuery({ name: 'vin_code', required: false })
  @ApiQuery({ name: 'engin_code', required: false })
  @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_CAR)
  async carsListForAdmin(
    @Page() page,
    @Limit() limit,
    @Sort() sorts,
    @Filter([
      ['date', 'createdAt', 'DATE'],
      ['user_id', 'userId'],
      ['maker_brand_id', 'makerBrandId'],
      ['vin_code', 'vinCode'],
      ['engine_code', 'engineCode'],
      'deleted',
    ])
    filters,
  ) {
    const data = await this.carService.carsList(page, limit, filters, sorts);
    return successfulResult([], data);
  }

  /**
   * -------------------------------------------------------
   * GET /cars/me
   */
  @Get('me')
  @ApiQuery({ name: 'maker_brand_id', required: false })
  @ApiQuery({ name: 'vin_code', required: false })
  @ApiQuery({ name: 'engin_code', required: false })
  @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)
  async carsListForUser(
    @Page() page,
    @Limit() limit,
    @Sort() sorts,
    @Filter([
      ['date', 'createdAt', 'DATE'],
      ['maker_brand_id', 'makerBrandId'],
      ['vin_code', 'vinCode'],
      ['engine_code', 'engineCode'],
    ])
    filters,
    @User('id') userId,
  ) {
    const data = await this.carService.carsList(
      page,
      limit,
      filters,
      sorts,
      userId,
    );
    return successfulResult([], data);
  }

  /**
   * -------------------------------------------------------
   * GET /makers/1/admin
   */
  @Get('makers/:id/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.GET_MAKER)
  async getMakerById(@Param('id') id: number) {
    const data = await this.carService.getMakerById(id);
    return successfulResult([], data);
  }

  /**
   * -------------------------------------------------------
   * GET /maker-brands/1/admin
   */
  @Get('maker-brands/:id/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.GET_MAKER_BRAND)
  async getMakerBrandById(@Param('id') id: number) {
    const data = await this.carService.getMakerBrandById(id);
    return successfulResult([], data);
  }

  /**
   * -------------------------------------------------------
   * PUT /maker-Brands/sort/admin
   */
  @Put('maker-brands/sort/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.SORT_MAKER_BRAND)
  async makerBrandSort(
    @Body() dto: MakerBrandSortDto,
    @User('id') operatorUserId: string,
  ) {
    const result = await sort(MakerBrandEntity.name, dto.id, dto.action);

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

    // log
    const makerBrand = await this.carService.getMakerBrandById(dto.id, false);

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

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

  /**
   * -------------------------------------------------------
   * PUT /makers/sort/admin
   */
  @Put('makers/sort/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.SORT_MAKER)
  async makerSort(
    @Body() dto: MakerSortDto,
    @User('id') operatorUserId: string,
  ) {
    const result = await sort(MakerEntity.name, dto.id, dto.action);

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

    // log
    const maker = await this.carService.getMakerById(dto.id);

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

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

  /**
   * -------------------------------------------------------
   * PUT /maker-Brands/1/admin
   */
  @Put('maker-brands/:id/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.UPDATE_MAKER_BRAND)
  @ApiConsumes('multipart/form-data')
  @UseInterceptors(FileInterceptor('image', multerOptions('brands')))
  async updateMakerBrand(
    @Param('id') id: number,
    @Body() dto: UpdateMakerBrandDto,
    @UploadedFile() image,
    @User('id') operatorUserId: string,
  ) {
    await this.carService.updateMakerBrand(id, dto, image, operatorUserId);
    return successfulResult(['مشخصات برند خودرو با موفقیت آپدیت شد']);
  }

  /**
   * -------------------------------------------------------
   * PUT /makers/1/admin
   */
  @Put('makers/:id/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.UPDATE_MAKER)
  @ApiConsumes('multipart/form-data')
  @UseInterceptors(FileInterceptor('image', multerOptions('companies')))
  async updateMaker(
    @Param('id') id: number,
    @Body() dto: UpdateMakerDto,
    @UploadedFile() image,
    @User('id') operatorUserId: string,
  ) {
    await this.carService.updateMaker(id, dto, image, operatorUserId);
    return successfulResult(['مشخصات خودروساز، با موفقیت آپدیت شد']);
  }

  /**
   * -------------------------------------------------------
   * GET /cars/1/admin
   */
  @Get(':id/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.GET_CAR)
  async getCarByIdForAdmin(@Param('id') id: number) {
    const data = await this.carService.getCarById(id);
    return successfulResult([], data);
  }

  /**
   * -------------------------------------------------------
   * GET /cars/1/me
   */
  @Get(':id/me')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard)
  async getCarByIdForUser(@Param('id') id: number, @User('id') userId) {
    const data = await this.carService.getCarById(id, userId);
    return successfulResult([], data);
  }

  /**
   * -------------------------------------------------------
   * POST /maker-brands/admin
   */
  @Post('maker-brands/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.ADD_MAKER_BRAND)
  @HttpCode(HttpStatus.CREATED)
  @ApiConsumes('multipart/form-data')
  @UseInterceptors(FileInterceptor('image', multerOptions('brands')))
  async addMakerBrand(
    @Body() dto: CreateMakerBrandDto,
    @UploadedFile() image,
    @User('id') operatorUserId: string,
  ) {
    const data = await this.carService.addMakerBrand(
      dto,
      image,
      operatorUserId,
    );
    return successfulResult(['برند جدید خودرو، باموفقیت اضافه شد'], data);
  }

  /**
   * -------------------------------------------------------
   * POST /makers/admin
   */
  @Post('makers/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.ADD_MAKER)
  @HttpCode(HttpStatus.CREATED)
  @ApiConsumes('multipart/form-data')
  @UseInterceptors(FileInterceptor('image', multerOptions('companies')))
  async addMaker(
    @Body() dto: CreateMakerDto,
    @UploadedFile() image,
    @User('id') operatorUserId: string,
  ) {
    const data = await this.carService.addMaker(dto, image, operatorUserId);
    return successfulResult(['خودروساز جدید، باموفقیت اضافه شد'], data);
  }

  /**
   * -------------------------------------------------------
   * DELETE /maker-brands/1/admin
   */
  @Delete('maker-brands/:id/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.DELETE_MAKER_BRAND)
  async deleteMakerBrand(
    @Param('id') id: number,
    @User('id') operatorUserId: string,
  ) {
    await this.carService.deleteMakerBrand(id, operatorUserId);
    return successfulResult(['برند خودروی موردنظر، با موفقیت حذف شد']);
  }

  /**
   * -------------------------------------------------------
   * DELETE /makers/1/admin
   */
  @Delete('makers/:id/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.DELETE_MAKER)
  async deleteMaker(
    @Param('id') id: number,
    @User('id') operatorUserId: string,
  ) {
    await this.carService.deleteMaker(id, operatorUserId);
    return successfulResult(['خودروساز موردنظر، با موفقیت حذف شد']);
  }

  /**
   * -------------------------------------------------------
   * DELETE /cars/1/admin
   */
  @Delete(':id/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.DELETE_CAR)
  async deleteCar(@Param('id') id: number, @User('id') operatorUserId: string) {
    await this.carService.deleteCar(id, operatorUserId);
    return successfulResult(['خودروی موردنظر، با موفقیت حذف شد']);
  }

  /**
   * -------------------------------------------------------
   * POST /cars/admin
   */
  @Post('admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.ADD_CAR)
  @HttpCode(HttpStatus.CREATED)
  async addCar(@Body() dto: CreateCarDto, @User('id') operatorUserId: string) {
    const data = await this.carService.addCar(dto, operatorUserId);
    return successfulResult(
      ['خودروی جدید برای کاربر موردنظر باموفقیت اضافه شد'],
      data,
    );
  }

  /**
   * -------------------------------------------------------
   * PUT /cars/1/admin
   */
  @Put(':id/admin')
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.UPDATE_CAR)
  async updateCar(
    @Param('id') id: number,
    @Body() dto: UpdateCarDto,
    @User('id') operatorUserId: string,
  ) {
    await this.carService.updateCar(id, dto, operatorUserId);
    return successfulResult(['خودروی کاربر موردنظر، باموفقیت آپدیت شد']);
  }

  /**
   * -------------------------------------------------------
   * PUT /cars/maker-brand/products/mapping/:id/admin
   */
  @Put('maker-brand/products/mapping/:id/admin')
  @ApiParam({ name: 'id', description: 'Maker Brand ID' })
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard, PermissionsGuard)
  @Permissions(PermissionsType.LINK_MAKER_BRAND_TO_PRODUCT)
  async makerBrandMapping(
    @Param('id') makerBrandId: number,
    @Body() dto: MakerBrandProductDto,
    @User('id') operatorUserId: string,
  ) {
    await this.carService.makerBrandProductMapping(
      makerBrandId,
      dto,
      operatorUserId,
    );
    return successfulResult(['تغییرات با موفقیت اعمال شد']);
  }
}
