import { ConflictException, Injectable, BadRequestException, NotFoundException } from '@nestjs/common';
import { CreateUtilisateurDto } from './dto/create-utilisateur.dto';
import { UpdateUtilisateurDto } from './dto/update-utilisateur.dto';
import { Utilisateur } from './entities/utilisateur.entity';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import * as bcrypt from 'bcryptjs';
import { plainToInstance } from 'class-transformer';
import { ProfilsService } from '../profils/profils.service';
import { CreateAdminsDto } from './dto/create-admin.dto';
import { ReferenceGeneratorHelper } from 'src/common/helpers/reference-generator.helper';
import { OtpService } from '../otp/otp.service';
import { NotifyService } from 'src/services/notify/notify.service';
import { CreatePartnerPwdDto } from './dto/update-partner-pwd.dto';


@Injectable()
export class UtilisateursService {

  constructor( 
    @InjectRepository(Utilisateur) 
    private utilisateurRepository: Repository<Utilisateur>,
    private readonly otpService: OtpService,
    private profilService: ProfilsService,
  
  ){}

  async createAdminUser(createUtilisateurDto: CreateAdminsDto): Promise<Partial<Utilisateur>> {
    try {

      //Vérifie si l'utilisateur admin existe
      /* const IsAdminExiste = await this.findOneByEmail(createUtilisateurDto.email);
      console.log(IsAdminExiste);
      
      if (IsAdminExiste) {
        delete (IsAdminExiste as any).mot_de_passe;
        return IsAdminExiste
      } */

      //Récupération du profil admin
      const profil = await this.profilService.findOneByCode('admin');
    
      createUtilisateurDto.mot_de_passe = String(process.env.ADMIN_PASSWORD);
      createUtilisateurDto.profil = profil;
      createUtilisateurDto.statut = 'enable';
      const hashPassword = await bcrypt.hash(createUtilisateurDto.mot_de_passe, 10);
      createUtilisateurDto.mot_de_passe = hashPassword;
      const user = await this.utilisateurRepository.save(createUtilisateurDto);
      delete (user as any).mot_de_passe;
      return user;
    
    } catch (error) {
          if (error.code === '23505') {
            // Vérifier le message pour savoir quelle contrainte est violée
            if (error.detail.includes('email')) {
              throw new ConflictException('Le mail existe déjà');
            }
            
            throw new ConflictException('Cette donnée existe déjà en base' + error);
          }
          throw new BadRequestException('Erreur interne du serveur ' + error);
    }
  }

  async create(createUtilisateurDto: CreateUtilisateurDto): Promise<any> {
    try {

        const pwd = ReferenceGeneratorHelper.generate('OBA$&@');
        
        const hashPassword = await bcrypt.hash(pwd, 10);
        createUtilisateurDto.mot_de_passe = hashPassword;
        createUtilisateurDto.tempPasswordExpiresAt = new Date(Date.now() + 3 * 24 * 60 * 60 * 1000); // maintenant + 3 jours

        const saveuser = await this.utilisateurRepository.save(createUtilisateurDto);

        //const saveotp = await this.otpService.generateOtp(saveuser);

        return {saveuser, password: pwd};

        } catch (error) {
          if (error.code === '23505') {
            // Vérifier le message pour savoir quelle contrainte est violée
            if (error.detail.includes('email')) {
              throw new ConflictException('Le mail existe déjà');
            }
            
            throw new ConflictException('Cette donnée existe déjà en base' + error.detail);
          }
          throw new BadRequestException('Erreur interne du serveur ' + error.detail);
        }
  }

  async findAll(): Promise<Utilisateur[]> {
    const utilisateurs = await this.utilisateurRepository.find({ 
      order: {nom: 'ASC'}, 
      relations: ['profil']
    }) ;
    return plainToInstance(Utilisateur, utilisateurs);
  }

  async findOne(id: number): Promise<Utilisateur> {
    const data = await this.utilisateurRepository.findOne({where: {id: id}});
        if(!data){
          throw new NotFoundException('Utilisateur inexistant');
        }
    
        return data;
  }

  async findOneByEmail(email: string): Promise<Utilisateur> {    
    const data = await this.utilisateurRepository.findOne({where: {email: email}});
    
        if(!data || data == null){
          throw new NotFoundException('Utilisateur inexistant');
        }
        return data;
  }

  async update(id: number, updateUtilisateurDto: UpdateUtilisateurDto): Promise<Utilisateur> {
    try {
      const user = await this.utilisateurRepository.preload({id, ...updateUtilisateurDto});
      if(!user){
        throw new NotFoundException('Utilisateur inexistant');
      }
      return await this.utilisateurRepository.save(user)
    } catch (error) {
      throw new BadRequestException(error.message);
    }
  }

  async updatepwd(id: number, data: {password: string}): Promise<string> {
    try {
      
      const hashPassword = await bcrypt.hash(data.password, 10);
      
      const pwd = await this.utilisateurRepository.preload({id, ...{mot_de_passe: hashPassword}});
   
      await this.utilisateurRepository.save({...pwd, updated_pwd: true})
      if(!pwd){
        throw new NotFoundException('Utilisateur inexistant');
      }
      return 'Modification du mot de passe effectué avec succès';
    } catch (error) {
      throw new BadRequestException(error.message);
    }
  }

  async firsrlyUpdatePwd(email: string, body: CreatePartnerPwdDto): Promise<any> {
    
    
    const user = await this.utilisateurRepository.findOne({ where: {email: email}});

    if (!user) {
      throw new NotFoundException('Utilisateur non trouvé');
    }

    if (user.updated_pwd == true) {
        throw new BadRequestException('Vous avez dejà modifier le mot de passe temporaire');
    }
    
    const hashPassword = await bcrypt.hash(body.password, 10);
    
    user.mot_de_passe = hashPassword;
    user.updated_pwd = true;

    await this.utilisateurRepository.save(user);

    
    return 'Modification du mot de passe effectué avec succès';
   
  }

  async remove(id: number) {
    return await this.utilisateurRepository.softDelete(id);
  }

  async signin(email: string): Promise<Utilisateur> {
    try {

      const data = await this.utilisateurRepository.findOne({
        where: {email}, 
        relations: []
      });

      if(!data){
        throw new NotFoundException('Email ou mot de passe incorrecte');
      }
      
      return data;

    } catch (error) {
      throw new BadRequestException(error.message);
    }
  }

  async findAndCountBy(id: number){
  }

}
