import {
  BadRequestException,
  HttpStatus,
  Injectable,
  Res,
  UnauthorizedException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import * as bcrypt from 'bcrypt';
import { AdminsService } from '../admins/services/admins.service';
import Admin from '../admins/entities/admin.entity';
import { plainToClass } from 'class-transformer';
import { Request, Response } from 'express';
import { Constant } from './ultils/constant';
import { ChangePasswordDto } from './dto/change-password.dto';
import AppResponse from '@/response/app-response';
import { GenerateKeysService } from '../admins/services/generate-key.service';

@Injectable()
export class AuthService {
  constructor(
    private jwtService: JwtService,
    private readonly adminService: AdminsService,
    private readonly generateKeyService: GenerateKeysService,
  ) {}

  async validateAdmin(
    username: string,
    password: string,
    message = 'Username or Password is cornet',
  ) {
    const user = await this.adminService.adminRepo.findOne({
      where: { username },
    });

    if (!user || !(await bcrypt.compare(password, user.password))) {
      throw new UnauthorizedException(
        AppResponse.toResponse(null, {
          message,
          status_code: HttpStatus.UNAUTHORIZED,
        }),
      );
    }

    return user;
  }

  async login(user: Admin, response: Response) {
    const payload = { _id: user.id };
    const accessToken = this.jwtService.sign(payload, { expiresIn: '15m' });
    const refreshToken = this.jwtService.sign(
      { refresh_key: true, ...payload },
      { expiresIn: '7d' },
    );

    response.cookie(Constant.ACCESS_TOKEN, accessToken, {
      httpOnly: true,
      maxAge: 15 * 60 * 1000,
      sameSite: 'lax',
    });

    response.cookie(Constant.REFRESH_TOKEN, refreshToken, {
      httpOnly: true,
      maxAge: 7 * 24 * 60 * 60 * 1000,
      sameSite: 'lax',
    });

    return AppResponse.toResponse(true, { message: 'Login Success' });
  }

  async logout(@Res() response: Response) {
    response.cookie(Constant.ACCESS_TOKEN, '', {
      httpOnly: true,
      maxAge: 0,
      sameSite: 'lax',
    });

    response.cookie(Constant.REFRESH_TOKEN, '', {
      httpOnly: true,
      maxAge: 0,
      sameSite: 'lax',
    });

    return AppResponse.toResponse(true, { message: 'Logout Success' });
  }

  async changePassword(
    data: ChangePasswordDto,
    request: Request,
    response: Response,
  ) {
    const admin = request['admin'];

    if (!admin) throw new UnauthorizedException(AppResponse.toResponse(false));

    await this.validateAdmin(
      admin.username,
      data.password,
      'Password is invalid !',
    );

    const newPassword = await bcrypt.hash(data.newPassword, 10);
    const result = await this.adminService.adminRepo.update(admin.id, {
      password: newPassword,
    });

    if (!result.affected)
      throw new BadRequestException(AppResponse.toResponse(false));

    await this.logout(response);

    return AppResponse.toResponse(true, {
      message: 'Change password success. Please re-login !',
    });
  }

  async me(request: Request) {
    const admin = request['admin'];

    if (!admin) throw new UnauthorizedException(AppResponse.toResponse(null));

    return AppResponse.toResponse(admin);
  }
}
