import {
  Controller,
  Get,
  Post,
  Body,
  Patch,
  Param,
  UsePipes,
  Res,
  UseInterceptors,
  UploadedFile,
  ValidationPipe,
  HttpStatus,
  NotFoundException,
  UnauthorizedException,
  UseGuards,
  BadRequestException,
} from '@nestjs/common';
import { Response as ExpressResponse } from 'express';
import { FileInterceptor } from '@nestjs/platform-express';
import { profilePictureConfig } from 'src/common/file upload/multer-config';
import {
  errorResponse,
  successResponse,
  validationErrorMessage,
} from 'src/common/errors/response-config';
import { InjectRepository } from '@nestjs/typeorm';
import { ApiLog } from 'src/api-logs/entities/api-log.entity';
import { DataSource, Repository } from 'typeorm';
import { saveError } from 'src/api-logs/api-error-log';
import { LoginDto } from './dto/login.dto';
import { lan } from 'src/lan';
import { AuthMiddleware } from 'src/common/middleware/auth.middleware';
import { AuthService } from './auth.service';
import { ChangePasswordDto } from './dto/change-password.dto';
import { UpdateAuthDto } from './dto/update-auth.dto';

@Controller('')
export class AuthController {
  constructor(
    private readonly authService: AuthService,
    @InjectRepository(ApiLog)
    private readonly apiLogRepository: Repository<ApiLog>,
    private readonly connection: DataSource,
  ) {}

  @Post('login')
  @UsePipes(
    new ValidationPipe({
      exceptionFactory: validationErrorMessage,
      transform: true,
      transformOptions: {
        enableImplicitConversion: true,
      },
    }),
  )
  async login(@Body() loginDto: LoginDto, @Res() res: ExpressResponse) {
    try {
      const user = await this.authService.login(loginDto);

      res
        .status(HttpStatus.OK)
        .send(successResponse(200, lan('login.success'), user));
    } catch (error) {
      if (error instanceof NotFoundException) {
        res
          .status(HttpStatus.BAD_REQUEST)
          .send(errorResponse(404, lan('login.user_not_found')));
      } else if (error instanceof UnauthorizedException) {
        res
          .status(HttpStatus.UNAUTHORIZED)
          .send(errorResponse(401, lan('login.invalid_password')));
      } else {
        res
          .status(HttpStatus.INTERNAL_SERVER_ERROR)
          .send(errorResponse(500, lan('common.internal_server_error')));
      }
      saveError(error, this.apiLogRepository);
    }
  }

  @Post('change-password')
  @UseGuards(AuthMiddleware)
  @UsePipes(
    new ValidationPipe({
      exceptionFactory: validationErrorMessage,
      transform: true,
      transformOptions: {
        enableImplicitConversion: true,
      },
    }),
  )
  async changePass(
    @Body() changePassDto: ChangePasswordDto,
    @Res() res: ExpressResponse,
  ) {
    try {
      const user = await this.authService.changePassword(changePassDto);

      res
        .status(HttpStatus.OK)
        .send(
          successResponse(
            200,
            lan('change.password.password_changed_success'),
            user,
          ),
        );
    } catch (error) {
      if (error instanceof NotFoundException) {
        res
          .status(HttpStatus.BAD_REQUEST)
          .send(errorResponse(404, lan('login.user_not_found')));
      } else if (error instanceof UnauthorizedException) {
        res
          .status(HttpStatus.UNAUTHORIZED)
          .send(
            errorResponse(400, lan('change.password.invalid_old_password')),
          );
      } else if (error instanceof BadRequestException) {
        res
          .status(HttpStatus.BAD_REQUEST)
          .send(
            errorResponse(400, lan('change.password.password_do_not_match')),
          );
      } else {
        res
          .status(HttpStatus.INTERNAL_SERVER_ERROR)
          .send(errorResponse(500, lan('common.internal_server_error')));
      }
      saveError(error, this.apiLogRepository);
    }
  }

  @Post('logout/:id')
  @UseGuards(AuthMiddleware)
  async logout(@Param('id') id: string, @Res() res: ExpressResponse) {
    try {
      await this.authService.logout(id);
      res
        .status(HttpStatus.OK)
        .send(successResponse(200, lan('logout.success')));
    } catch (error) {
      if (error instanceof UnauthorizedException) {
        res
          .status(HttpStatus.UNAUTHORIZED)
          .send(errorResponse(401, lan('login.user_not_found')));
      } else {
        res
          .status(HttpStatus.INTERNAL_SERVER_ERROR)
          .send(errorResponse(500, lan('common.internal_server_error')));
      }
      saveError(error, this.apiLogRepository);
    }
  }

  @Patch('update/:id')
  @UseInterceptors(FileInterceptor('attachment', profilePictureConfig))
  @UseGuards(AuthMiddleware)
  async updateUser(
    @Param('id') id: string,
    @UploadedFile() file,
    @Body(new ValidationPipe()) updateUserDto: UpdateAuthDto,
    @Res() res: ExpressResponse,
  ) {
    const queryRunner = this.connection.createQueryRunner();
    try {
      await queryRunner.connect();
      await queryRunner.startTransaction();

      if (file) {
        const attachmentUrl = `uploads/profile-picture/${file.filename}`;
        updateUserDto.profile_picture = attachmentUrl;
      }
      const updatedUser = await this.authService.updateUser(id, updateUserDto);
      res
        .status(HttpStatus.OK)
        .send(
          successResponse(200, lan('profile.updated_success'), updatedUser),
        );
    } catch (error) {
      if (
        error instanceof NotFoundException ||
        error instanceof BadRequestException
      ) {
        res
          .status(HttpStatus.BAD_REQUEST)
          .send(errorResponse(404, lan('login.user_not_found')));
      } else {
        res
          .status(HttpStatus.INTERNAL_SERVER_ERROR)
          .send(errorResponse(500, lan('common.internal_server_error')));
      }
      saveError(error, this.apiLogRepository);
    } finally {
      await queryRunner.release();
    }
  }

  @Get('get/:id')
  @UseGuards(AuthMiddleware)
  async getUser(@Param('id') id: string, @Res() res: ExpressResponse) {
    try {
      const user = await this.authService.findUser(id);
      if (user) {
        res
          .status(HttpStatus.OK)
          .send(
            successResponse(
              200,
              lan('common.data_retrieved_successfully'),
              user,
            ),
          );
      } else {
        res
          .status(HttpStatus.BAD_REQUEST)
          .send(errorResponse(404, lan('login.user_not_found')));
      }
    } catch (error) {
      if (
        error instanceof NotFoundException ||
        error instanceof BadRequestException
      ) {
        res
          .status(HttpStatus.BAD_REQUEST)
          .send(errorResponse(404, lan('login.user_not_found')));
      } else {
        res
          .status(HttpStatus.INTERNAL_SERVER_ERROR)
          .send(errorResponse(500, lan('common.internal_server_error')));
      }
      saveError(error, this.apiLogRepository);
    }
  }
}
