import {
  Controller,
  Get,
  Post,
  Body,
  Patch,
  Param,
  Delete,
  Res,
  HttpStatus,
  UseGuards,
  Query,
  Headers,
  NotFoundException,
  BadRequestException,
  UsePipes,
  ValidationPipe,
} from '@nestjs/common';
import { AchievementsService } from './achievements.service';
import { CreateAchievementDto } from './dto/create-achievement.dto';
import { UpdateAchievementDto } from './dto/update-achievement.dto';
import {
  errorResponse,
  successResponse,
  validationErrorMessage,
} from 'src/common/errors/response-config';
import { lan } from 'src/lan';
import { Response as ExpressResponse } from 'express';
import { saveError } from 'src/api-logs/api-error-log';
import { InjectRepository } from '@nestjs/typeorm';
import { ApiLog } from 'src/api-logs/entities/api-log.entity';
import { Repository } from 'typeorm';
import { AuthMiddleware } from 'src/common/middleware/auth.middleware';
import { CreateUserAchievementDto } from './dto/create-user-achievement.dto';

@Controller('achievements')
@UseGuards(AuthMiddleware)
export class AchievementsController {
  constructor(
    private readonly achievementsService: AchievementsService,
    @InjectRepository(ApiLog)
    private readonly apiLogRepository: Repository<ApiLog>,
  ) {}

  @Post()
  @UsePipes(
    new ValidationPipe({
      exceptionFactory: validationErrorMessage,
      transform: true,
      transformOptions: {
        enableImplicitConversion: true,
      },
    }),
  )
  async create(
    @Body() createAchievementDto: CreateAchievementDto,
    @Res() res: ExpressResponse,
  ) {
    try {
      await this.achievementsService.create(createAchievementDto);
      res.send(successResponse(201, lan('common.request_submitted')));
    } catch (error) {
      if (error instanceof BadRequestException) {
        res
          .status(HttpStatus.BAD_REQUEST)
          .send(errorResponse(400, error.message));
      } else {
        res
          .status(HttpStatus.INTERNAL_SERVER_ERROR)
          .send(errorResponse(500, lan('common.internal_server_error'), error));
      }

      saveError(error, this.apiLogRepository);
    }
  }

  @Get()
  async findAll(
    @Query('take') take: number,
    @Query('skip') skip: number,
    @Query('key') key: string,
    @Query('order') order: string,
    @Res() res: ExpressResponse,
    @Headers() headers: any,
  ) {
    try {
      const allAchievement = await this.achievementsService.findAll(
        take,
        skip,
        key,
        order,
        headers,
      );

      res
        .status(HttpStatus.OK)
        .send(
          successResponse(
            200,
            lan('common.data_retrieved_successfully'),
            allAchievement,
          ),
        );
    } catch (error) {
      res
        .status(HttpStatus.INTERNAL_SERVER_ERROR)
        .send(errorResponse(500, lan('common.internal_server_error'), error));

      saveError(error, this.apiLogRepository);
    }
  }

  @Get('user')
  async findAllAchievement(
    @Query('take') take: number,
    @Query('skip') skip: number,
    @Query('key') key: string,
    @Query('order') order: string,
    @Query('search') search: string,
    @Query('recent') recent: boolean,
    @Query('province') province: string,
    @Res() res: ExpressResponse,
    @Headers() headers: any,
  ) {
    try {
      const allAchievement = await this.achievementsService.findAllAchievement(
        take,
        skip,
        key,
        order,
        search,
        recent,
        province,
      );

      res
        .status(HttpStatus.OK)
        .send(
          successResponse(
            200,
            lan('common.data_retrieved_successfully'),
            allAchievement,
          ),
        );
    } catch (error) {
      res
        .status(HttpStatus.INTERNAL_SERVER_ERROR)
        .send(errorResponse(500, lan('common.internal_server_error'), error));

      saveError(error, this.apiLogRepository);
    }
  }

  @Get(':id')
  async findOne(@Param('id') id: string, @Res() res: ExpressResponse) {
    try {
      const data = await this.achievementsService.findOne(id);
      res
        .status(HttpStatus.OK)
        .send(
          successResponse(200, lan('common.data_retrieved_successfully'), data),
        );
    } catch (error) {
      if (
        error instanceof NotFoundException ||
        error instanceof BadRequestException
      ) {
        res
          .status(HttpStatus.NOT_FOUND)
          .send(errorResponse(404, error.message));
      } else {
        res
          .status(HttpStatus.INTERNAL_SERVER_ERROR)
          .send(errorResponse(500, lan('common.internal_server_error'), error));
      }

      saveError(error, this.apiLogRepository);
    }
  }

  @Get('user/record')
  async findAllUserAchievement(
    @Query('take') take: number,
    @Query('skip') skip: number,
    @Query('user_id') userId: string,
    @Query('date') date: string,
    @Res() res: ExpressResponse,
  ) {
    try {
      const allAchievement =
        await this.achievementsService.findAllUserAchievement(
          take,
          skip,
          userId,
          date,
        );

      res
        .status(HttpStatus.OK)
        .send(
          successResponse(
            200,
            lan('common.data_retrieved_successfully'),
            allAchievement,
          ),
        );
    } catch (error) {
      saveError(error, this.apiLogRepository);
      if (error instanceof NotFoundException) {
        res
          .status(HttpStatus.NOT_FOUND)
          .send(errorResponse(404, error.message));
      } else {
        res
          .status(HttpStatus.INTERNAL_SERVER_ERROR)
          .send(errorResponse(500, lan('common.internal_server_error'), error));
      }
    }
  }

  @Post('add')
  @UsePipes(
    new ValidationPipe({
      exceptionFactory: validationErrorMessage,
      transform: true,
      transformOptions: {
        enableImplicitConversion: true,
      },
    }),
  )
  async createUserAchievement(
    @Body() createUserAchievementDto: CreateUserAchievementDto,
    @Res() res: ExpressResponse,
  ) {
    try {
      await this.achievementsService.checkAndAddUserAchievement(
        createUserAchievementDto,
      );
      res.send(successResponse(201, lan('common.request_submitted')));
    } catch (error) {
      res
        .status(HttpStatus.INTERNAL_SERVER_ERROR)
        .send(errorResponse(500, lan('common.internal_server_error'), error));

      saveError(error, this.apiLogRepository);
    }
  }

  @Patch(':id')
  @UsePipes(
    new ValidationPipe({
      exceptionFactory: validationErrorMessage,
      transform: true,
      transformOptions: {
        enableImplicitConversion: true,
      },
    }),
  )
  async update(
    @Param('id') id: string,
    @Body() updateAchievementDto: UpdateAchievementDto,
    @Res() res: ExpressResponse,
  ) {
    try {
      const data = await this.achievementsService.update(
        id,
        updateAchievementDto,
      );
      res
        .status(HttpStatus.OK)
        .send(successResponse(200, lan('common.data_updated'))),
        data;
    } catch (error) {
      if (
        error instanceof NotFoundException ||
        error instanceof BadRequestException
      ) {
        res
          .status(HttpStatus.BAD_REQUEST)
          .send(errorResponse(404, error.message));
      } else {
        res
          .status(HttpStatus.INTERNAL_SERVER_ERROR)
          .send(errorResponse(500, lan('common.internal_server_error'), error));
      }

      saveError(error, this.apiLogRepository);
    }
  }

  @Delete(':id')
  async remove(@Param('id') id: string, @Res() res: ExpressResponse) {
    try {
      await this.achievementsService.remove(id);
      res
        .status(HttpStatus.OK)
        .send(successResponse(200, lan('common.data_deleted')));
    } catch (error) {
      if (
        error instanceof NotFoundException ||
        error instanceof BadRequestException
      ) {
        res
          .status(HttpStatus.BAD_REQUEST)
          .send(errorResponse(404, error.message));
      } else {
        res
          .status(HttpStatus.INTERNAL_SERVER_ERROR)
          .send(errorResponse(500, lan('common.internal_server_error'), error));
      }

      saveError(error, this.apiLogRepository);
    }
  }
}
