import {
  Controller,
  Get,
  Post,
  Body,
  Patch,
  Param,
  Delete,
  UseInterceptors,
  UseGuards,
  UsePipes,
  ValidationPipe,
  UploadedFile,
  Res,
  HttpStatus,
  Query,
  NotFoundException,
  BadRequestException,
  Headers,
  UploadedFiles,
  Search,
} from '@nestjs/common';
import { GroupsService } from './groups.service';
import { CreateGroupDto } from './dto/create-group.dto';
import { UpdateGroupDto } from './dto/update-group.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { ApiLog } from 'src/api-logs/entities/api-log.entity';
import {
  FileFieldsInterceptor,
  FileInterceptor,
} from '@nestjs/platform-express';
import { imageConfig } from 'src/common/file upload/multer-config';
import { AuthMiddleware } from 'src/common/middleware/auth.middleware';
import {
  errorResponse,
  successResponse,
  validationErrorMessage,
} from 'src/common/errors/response-config';
import { Response as ExpressResponse } from 'express';
import { limits } from 'src/common/file upload/multer-config-helper';
import { lan } from 'src/lan';
import { saveError } from 'src/api-logs/api-error-log';
import { DataSource, Repository } from 'typeorm';
import { FileValidationInterceptor } from 'src/common/file upload/ValidationInterceptor';
import { AppUsersService } from 'src/app_users/app_users.service';

@Controller('groups')
export class GroupsController {
  constructor(
    private readonly groupsService: GroupsService,
    @InjectRepository(ApiLog)
    private readonly apiLogRepository: Repository<ApiLog>,
    private readonly connection: DataSource,
    private readonly appUserService: AppUsersService,
  ) {}

  @Post()
  @UseGuards(AuthMiddleware)
  @UseInterceptors(
    FileFieldsInterceptor([{ name: 'group_icon', maxCount: 1 }], imageConfig),
    FileValidationInterceptor,
  )
  @UsePipes(
    new ValidationPipe({
      exceptionFactory: validationErrorMessage,
      transform: true,
      transformOptions: {
        enableImplicitConversion: true,
      },
    }),
  )
  async create(
    @Body() createGroupDto: CreateGroupDto,
    @UploadedFiles() group_icon, // use UploadedFiles instead of UploadedFile
    @Res() res: ExpressResponse,
  ) {
    try {
      const icon = group_icon['group_icon'];

      if (icon && icon.size > limits.fileSize['image']) {
        return res.send(errorResponse(413, lan('common.limit_has_exceeded')));
      }

      if (icon) {
        const attachmentUrl = `uploads/group-icons/${icon[0].filename}`;
        createGroupDto.icon = attachmentUrl;
      }

      const data = await this.groupsService.create(createGroupDto);

      if (data.owner_role === 'user') {
        await this.appUserService.associateUserWithGroup(
          data.owner_id,
          data.id,
        );
      }

      res.send(successResponse(201, lan('common.request_submitted')));
    } catch (error) {
      saveError(error, this.apiLogRepository);
      if (
        error instanceof BadRequestException ||
        error instanceof NotFoundException
      ) {
        res
          .status(HttpStatus.NOT_FOUND)
          .send(errorResponse(400, error.message));
      } else {
        res
          .status(HttpStatus.INTERNAL_SERVER_ERROR)
          .send(errorResponse(500, lan('common.internal_server_error'), error));
      }
    }
  }

  @Get()
  @UseGuards(AuthMiddleware)
  async findAll(
    @Query('take') take: number,
    @Query('skip') skip: number,
    @Query('search') search: string,
    @Res() res: ExpressResponse,
    @Headers() headers: any,
  ) {
    try {
      const awardRecognitions = await this.groupsService.findAllGroups(
        take,
        skip,
        search,
        headers,
      );

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

      saveError(error, this.apiLogRepository);
    }
  }

  @Get(':id')
  @UseGuards(AuthMiddleware)
  async findOne(@Param('id') id: string, @Res() res: ExpressResponse) {
    try {
      const awardRecognition = await this.groupsService.findGroup(id);

      if (awardRecognition) {
        res
          .status(HttpStatus.OK)
          .send(
            successResponse(
              200,
              lan('common.data_retrieved_successfully'),
              awardRecognition,
            ),
          );
      }
    } 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);
    }
  }

  @Get('user/:id')
  @UseGuards(AuthMiddleware)
  async findAllGroupUser(
    @Param('id') id: string,
    @Query('take') take: number,
    @Query('skip') skip: number,
    @Query('search') search: string,
    @Res() res: ExpressResponse,
  ) {
    try {
      const awardRecognition: any = await this.groupsService.findAllGroupUser(
        id,
        take,
        skip,
        search,
      );

      if (awardRecognition) {
        res
          .status(HttpStatus.OK)
          .send(
            successResponse(
              200,
              lan('common.data_retrieved_successfully'),
              awardRecognition,
            ),
          );
      }
    } 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);
    }
  }

  @Patch(':id')
  @UseInterceptors(FileInterceptor('group_icon', imageConfig))
  @UseGuards(AuthMiddleware)
  async update(
    @Param('id') id: string,
    @UploadedFile() group_icon,
    @Body() updateGroupDto: UpdateGroupDto,
    @Res() res: ExpressResponse,
  ) {
    const queryRunner = this.connection.createQueryRunner();

    try {
      await queryRunner.connect();
      await queryRunner.startTransaction();

      if (group_icon && group_icon.size > limits.fileSize['image']) {
        return res.send(errorResponse(413, lan('common.limit_has_exceeded')));
      }

      if (group_icon) {
        const attachmentUrl = `uploads/group-icons/${group_icon.filename}`;
        updateGroupDto.icon = attachmentUrl;
      }

      await this.groupsService.updateGroup(id, updateGroupDto);

      res
        .status(HttpStatus.OK)
        .send(successResponse(200, lan('common.data_updated')));
    } 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);
    } finally {
      await queryRunner.release();
    }
  }

  @Delete(':id')
  @UseGuards(AuthMiddleware)
  async remove(@Param('id') id: string, @Res() res: ExpressResponse) {
    try {
      await this.groupsService.removeGroup(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);
    }
  }
}
