import {
  ConflictException,
  Injectable,
  NotFoundException,
  UnauthorizedException,
} from '@nestjs/common';
import { SignupDto } from './dto/signupDto';
import * as bcrypt from 'bcrypt';
import { PrismaService } from 'src/prisma/prisma.service';
import { SigninDto } from './dto/signinDto';
import { MailerService } from 'src/mailer/mailer.service';
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import { ResetPwdDto } from './dto/resetPwdDto';
import * as speakeasy from 'speakeasy'
import { ResetPwdConfirmationDto } from './dto/ResetPwdConfirmationDto';
import { DeleteAccountDto } from './dto/deleteAccountDto';
import { UserDto } from './dto/userDto';
import { ChangePwdDto } from './dto/changePwdDto';

@Injectable()
export class AuthService {

  constructor(
    private readonly prismaService: PrismaService,
    private readonly mailerService: MailerService,
    private readonly jwtService: JwtService,
    private readonly configService: ConfigService
  ) {}
  async signup(signupDto: SignupDto) {

    const { prenom, nom, username, password, fastFoodId } = signupDto;

    const user = await this.prismaService.user.findUnique({ where: { username } });
    if (user) throw new ConflictException('User already exist');

    const fastfood = await this.prismaService.fastFood.findUnique({where:{id:fastFoodId}});
    if (!fastfood) throw new NotFoundException('Les fastfood selectionne n\'existe pas')

    const hash = await bcrypt.hash(password, 10);

    const userCreated = await this.prismaService.user.create({
      data: { prenom, nom, username, password: hash, fastFoodId },
    });

    const role = await this.prismaService.role.findFirst({where:{libelle:"GERANT"}})

    await this.prismaService.roleUser.create({data:{roleId:role.id,userId:userCreated.id}})
    //await this.mailerService.sendMail(email);
    return {
      status: 'SUCCESS',
      message: 'User created succesfully',
    };
  }

  async signin(signinDto: SigninDto) {

    const { username, password } = signinDto;

    const user = await this.prismaService.user.findUnique({ 
      where: { username }, 
      include:{
        rolesUser:{
          include:{role:true}
        }
      } 
    });

    if (!user) throw new NotFoundException('User not found');
    const match = await bcrypt.compare(password, user.password);
    if (!match) throw new UnauthorizedException('pwd not correct');
    const payload = {
      sub: user.id,
      username: user.username
    }
    const token = this.jwtService.sign(payload, {expiresIn:'2h', secret: this.configService.get('SECRET_KEY')});

    const roles = []

    for (const r of user.rolesUser){
      roles.push(r.role)
    }

    return{
      status: 'SUCCESS',
      token, 
      user:{
        prenom:user.prenom,
        nom:user.nom,
        roles:roles
      }
    }
  }

  async resetPwd(resetPwdDto: ResetPwdDto) {

    const { username } = resetPwdDto;

    const user = await this.prismaService.user.findUnique({ where: { username } });
    if (!user) throw new NotFoundException('User not found');

    const code = speakeasy.totp({
      secret: this.configService.get('OTP_CODE'),
      digits: 5,
      step: 60*10, //duree
      encoding: 'base32' 
    })

    const url = 'http://127.0.0.1:3000/reset-password-confirmation'

    //this.mailerService.sendTesetPwd(user.username, url, code)

    return {
      status: 'SUCCESS',
      messsage:"Reset pwd mail has been sent"
    }

  }

  async resetPwdConfirmation(resetPwdConfirmationDto: ResetPwdConfirmationDto) {
    const {username, code, password} = resetPwdConfirmationDto
    
    const user = await this.prismaService.user.findUnique({ where: { username } });
    if (!user) throw new NotFoundException('User not found');

    const match = speakeasy.totp.verify({
      secret: this.configService.get("OTP_CODE"),
      token:  code,
      digits: 5,
      step: 60*10, //duree
      encoding: 'base32' 
    })
    if(!match) throw new UnauthorizedException("Invalid/expired token")

    const hash = await bcrypt.hash(password, 10)

    await this.prismaService.user.update({where:{username}, data:{password:hash}})

    return {
      status: 'SUCCESS',
      messsage:"Pwd updated"
    }
    
  }


  async deleteUser(userId: number, deleteAccountDto:DeleteAccountDto) {

    const user = await this.prismaService.user.findUnique({ where: { id:userId } });
    if (!user) throw new NotFoundException('User not found');

    const match = await bcrypt.compare(deleteAccountDto.password, user.password);
    if (!match) throw new UnauthorizedException('pwd not correct');

    await this.prismaService.user.delete({where: {id:userId}})

    return {
      status: 'SUCCESS',
      messsage:"User deleted"
    }

  }


  async createUser(userDto: UserDto, fastFoodId: any) {

    const {username, prenom, nom} = userDto

    const user = await this.prismaService.user.findUnique({where:{username}});
    
    if (user) throw new ConflictException('User already exist');

    const hash = await bcrypt.hash('12345678', 10)

    const userCreated = await this.prismaService.user.create({data:{username,prenom,nom,password:hash, fastFoodId}})

    const role = await this.prismaService.role.findFirst({where:{libelle:'USER'}})

    await this.prismaService.roleUser.create({data:{roleId:role.id,userId:userCreated.id}})

    return {
      status: 'SUCCESS',
      message: 'User created succesfully',
    };
    
  }
  
  async changePwd(changePwdDto: ChangePwdDto, userId) {

    if (!(changePwdDto.newPassord == changePwdDto.passwordConfirm)) throw new UnauthorizedException('Les mots de passe doivent correspondre')

    const user = await this.prismaService.user.findUnique({ where: { id:userId } });
    if (!user) throw new NotFoundException('User not found');

    const match = await bcrypt.compare(changePwdDto.password, user.password);
    if (!match) throw new UnauthorizedException('pwd not correct');

    const hash = await bcrypt.hash(changePwdDto.newPassord, 10)

    await this.prismaService.user.update({where:{id:user.id}, data:{password:hash}})

    return {
      status: 'SUCCESS',
      message: 'Pwd changed succesfully',
    };
}
}
