import { Component, OnInit } from '@angular/core';
import { UserConsult, UserConsultResponse } from '../../../../interfaces/user.interface';
import { AbstractControl, FormControl, FormGroup, FormGroupDirective, NgForm, ValidationErrors, ValidatorFn, Validators } from '@angular/forms';
import { ProfileResponse, ProfilesResponse } from '../../../../interfaces/profiles.interface';
import { ErrorStateMatcher } from '@angular/material/core';
import { ActivatedRoute } from '@angular/router';
import { UsersProfilesService } from '../../../../services/users-profiles.service';
import { Location } from '@angular/common';
import { EncService } from '../../../../services/enc.service';
import { SocketService } from '../../../../services/socket.service';
import { SystemAdminService } from '../../../../services/system-admin.service';
import { lastValueFrom } from 'rxjs';
import { PasswordFormatResponse } from '../../../../interfaces/password-format.interface';
import { ResourcesService } from '../../../../services/resources.service';

export const VALID_CHARS = ['¡','!','#','$','%','&','/','(',')','=','¿','?','*','-','+','.',',','-','_','`','´','~','^','{','}','[',']','¨','"',"'",'|','°','@','<','>',';',':'];
@Component({
    selector: 'app-new-user',
    templateUrl: './new-user.component.html',
    styleUrl: './new-user.component.css',
    standalone: false
})
export class NewUserComponent implements OnInit {
  action: string;
  user: string;
  isNew: boolean;
  isLoading: boolean;
  hasError: boolean;
  errorStr: string;
  userConsult: UserConsult | null;
  userForm: FormGroup;
  profiles: ProfileResponse[];
  hidePassword: boolean;
  hideConfirmPassword: boolean;
  saveEnabled: boolean;
  screenSize: number;

  samePassMatcher: NotSamePasswordsMatcher | null;
  generalMatcher: GeneralMatcher | null;
  passMinLength: number;
  passMinUpper: number;
  passMinNumber: number;
  passMinChars: number;

  constructor(
    private _route: ActivatedRoute, 
    private _usersProfilesService: UsersProfilesService, 
    private _location: Location, 
    private _encService: EncService,
    private _socketService: SocketService,
    private _systemAdminService: SystemAdminService,
    private _resourcesService: ResourcesService,
  ) { 
    this.action = "";
    this.user = "";
    this.isNew = true;
    this.isLoading = true;
    this.hasError = false;
    this.errorStr = "";
    this.userConsult = null;
    this.profiles = [];
    this.hidePassword = true;
    this.hideConfirmPassword = true;
    this.saveEnabled = true;
    this.screenSize = window.innerWidth;
    this.samePassMatcher = new NotSamePasswordsMatcher();
    this.generalMatcher = new GeneralMatcher();
    this.passMinLength = 0;
    this.passMinUpper = 0;
    this.passMinNumber = 0;
    this.passMinChars = 0;

    this.userForm = new FormGroup({
      id: new FormControl({value: '', disabled: true}),
      name: new FormControl('', [Validators.required, Validators.maxLength(50)]),
      fApe: new FormControl('', [Validators.required, Validators.maxLength(50)]),
      sApe: new FormControl('', Validators.maxLength(50)),
      email: new FormControl('', [Validators.required, Validators.maxLength(50), Validators.email]),
      perf: new FormControl({value: '', disabled: true}, Validators.required),
    });
  }

  ngOnInit(): void {
    this._route.queryParams.subscribe(params => {
      let info = params['info'];
      this._encService.decrypt(info).then((infoDec) => {
        let infoObj = JSON.parse(infoDec);

        this.isNew = infoObj.action == "insert";
        this.action = infoObj.action == "insert" ? "Insertar Nuevo" : "Modificar";
        this.user = infoObj.user;
        
        if(this.isNew){
          this.saveEnabled = true;
          this.userForm.addControl('password', new FormControl('', [Validators.required]));
          this.userForm.addControl('password_confirmation', new FormControl('', [Validators.required]));
          this.userForm.addValidators(this.checkPasswords);
          this.userForm.addValidators(this.whiteSpacesValidator);
          this.userForm.controls['perf'].enable();

          this.getProfiles();
        }else{
          this.getUser(this.user);
        }
      })
    });
  }

  checkPasswords: ValidatorFn = (group: AbstractControl): ValidationErrors | null => {
    let pass = group.get('password')?.value;
    let confPass = group.get('password_confirmation')?.value;
    return pass === confPass ? null : { notSame: true }
  }

  whiteSpacesValidator: ValidatorFn = (group: AbstractControl): ValidationErrors | null => {
    let pass = group.get('password')!.value;
    return !pass.includes(' ') ? null : { whiteSpaces: true }
  }

  async getProfiles(){
    try{
      let idEnc = localStorage.getItem('idusuario')!;
      let profiles: ProfilesResponse = await lastValueFrom(this._usersProfilesService.getProfiles(idEnc, 1));
      
      this.hasError = profiles.error;
      this.errorStr = profiles.msg;

      if(this.hasError){
        this.isLoading = false;
      }else{
        let userProfile = this.userConsult == null ? null : await this._encService.decrypt(this.userConsult.PERFIL as string);
        let profilesArr: ProfileResponse[] = [];

        for(const profile of profiles.response){
          let idProfDec = await this._encService.decrypt(profile.IDPERFIL);
          profile.NOMBREPERFIL = `${profile.NOMBREPERFIL} (${idProfDec})`;

          if(userProfile != null && idProfDec == userProfile) this.userForm.controls['perf'].setValue(profile.IDPERFIL);
          if(profile.ESTADO == 'Activo'){
            profilesArr.push(profile);
          }
        }

        this.profiles = profilesArr;
        this.getPasswordPattern();
      }
    }catch(error: any){
      if(error.error.msg == undefined){
        this.errorStr = "Ocurrió un error inesperado (3).";
      }else if(error.error.msg == undefined){
        this.errorStr = "Ocurrió un error inesperado (4).";
      }else{
        this.errorStr = error.error.msg;
      }

      this.hasError = true;
      this.isLoading = false;
    }
  }

  async getPasswordPattern(){
    try{
      let idUser = localStorage.getItem('idusuario')!;
      let passwordPattern: PasswordFormatResponse = await lastValueFrom(this._systemAdminService.getPasswordFormat(idUser, 1));

      this.hasError = passwordPattern.error;
      this.errorStr = passwordPattern.msg;

      if(!this.hasError && this.isNew){
        this.userForm.controls['password'].setValidators(Validators.minLength(passwordPattern.response.password_format.min_length));
        this.userForm.controls['password_confirmation'].setValidators(Validators.minLength(passwordPattern.response.password_format.min_length));
        this.passMinLength = passwordPattern.response.password_format.min_length;

        if(passwordPattern.response.password_format.upper_enabled){
          this.passMinUpper = passwordPattern.response.password_format.min_upper;
          this.userForm.addValidators(this.passMinUpperValidator);
        }

        if(passwordPattern.response.password_format.number_enabled){
          this.passMinNumber = passwordPattern.response.password_format.min_number;
          this.userForm.addValidators(this.passMinNumberValidator);
        }

        if(passwordPattern.response.password_format.chars_enabled){
          this.passMinChars = passwordPattern.response.password_format.min_chars;
          this.userForm.addValidators(this.passMinCharsValidator);
        }
      }

      this.isLoading = false;
    }catch(error: any){
      console.log(error);
      
      if(error.error == undefined){
        this.errorStr = 'Ocurrió un error inesperado (5).';
      }else if(error.error.msg == undefined){
        this.errorStr = 'Ocurrió un error inesperado (6).';
      }else{
        this.errorStr = error.error.msg;
      }

      this.hasError = true;
      this.isLoading = false;
    }
  }

  passMinUpperValidator: ValidatorFn = (group: AbstractControl): ValidationErrors | null => {
    let pass = group.get('password')!.value;
    let valArr = pass.split('');
    let upperCount = 0;

    valArr.forEach((char: string) => {
      let upperRegex = new RegExp('[A-Z]');
      if(char.match(upperRegex)){
        upperCount++;
      }
    });
    
    return upperCount >= this.passMinUpper ? null : { minUpper: true }
  }

  passMinNumberValidator: ValidatorFn = (group: AbstractControl): ValidationErrors | null => {
    let pass = group.get('password')!.value;
    let valArr = pass.split('');
    let numberCount = 0;

    valArr.forEach((char: string) => {
      let upperRegex = new RegExp('[0-9]');
      if(char.match(upperRegex)){
        numberCount++;
      }
    });

    return numberCount >= this.passMinNumber ? null : { minNumber: true }
  }

  passMinCharsValidator: ValidatorFn = (group: AbstractControl): ValidationErrors | null => {
    let pass = group.get('password')!.value;
    let valArr = pass.split('');
    let charsCount = 0;

    valArr.forEach((char: string) => {
      let specialChars = VALID_CHARS;
      if(specialChars.includes(char)){
        charsCount++;
      }
    });
    
    return charsCount >= this.passMinChars ? null : { minChars: true }
  }

  async getUser(idUser: string){
    try{
      let idEnc = await this._encService.encrypt(idUser);
      let idEncSol = localStorage.getItem('idusuario')!;
      
      let userInfo: UserConsultResponse = await lastValueFrom(this._usersProfilesService.getUser(
        idEnc, 
        idEncSol, 
        1
      ));
      
      this.hasError = userInfo.error;
      this.errorStr = userInfo.msg;

      if(this.hasError){
        this.isLoading = false;
      }else{
        let idUserDec = await this._encService.decrypt(userInfo.response.IDUSUARIO);
        this.userForm.controls['id'].setValue(idUserDec);
        this.userForm.controls['name'].setValue(userInfo.response.NOMBRE);
        this.userForm.controls['fApe'].setValue(userInfo.response.APEPAT);
        this.userForm.controls['sApe'].setValue(userInfo.response.APEMAT);
        this.userForm.controls['email'].setValue(userInfo.response.EMAIL);
        
        this.userConsult = userInfo.response;
        this.saveEnabled = this.userConsult.ESTATUS != 'Eliminado';

        if(this.userConsult.ESTATUS != 'Eliminado'){
          this.userForm.controls['perf'].enable();
        }
        
        this.getProfiles();
      }
    }catch(error: any){
      if(error.error == undefined){
        this.errorStr = 'Ocurrió un error inesperado (1).';
      }else if(error.error.msg == undefined){
        this.errorStr = 'Ocurrió un error inesperado (2).';
      }else{
        this.errorStr = error.error.msg;
      }

      this.hasError = true;
      this.isLoading = false;
    }
  }

  onResize():void{
    this.screenSize = window.innerWidth;
  }

  goBack(steps: number){
    window.history.go(steps * -1);
  }

  getErrorMessage(control: string) {
    switch(control){
      case 'name':
        if(this.userForm.get('name')?.hasError('required')) return "Este campo es obligatorio.";
      break;
      case 'fApe':
        if(this.userForm.get('fApe')?.hasError('required')) return "Este campo es obligatorio.";
      break;
      case 'email':
        if(this.userForm.get('email')?.hasError('required')) return "Este campo es obligatorio.";
        if(this.userForm.get('email')?.hasError('email')) return "El correo no tiene un formáto válido.";
      break;
      case 'perf':
        if(this.userForm.get('perf')?.hasError('required')) return "Este campo es obligatorio.";
      break;
      case 'password':
        if(this.userForm.get('password')?.hasError('required')) return "Este campo es obligatorio.";
        if(this.userForm.get('password')?.hasError('minlength')) return `La contraseña debe tener al menos ${this.passMinLength} caracteres.`;
        if(this.userForm.errors != null && this.userForm.errors!['minUpper']) return `La contraseña debe contener al menos ${this.passMinUpper} letra(s) mayúscula(s).`;
        if(this.userForm.errors != null && this.userForm.errors!['minNumber']) return `La contraseña debe contener al menos ${this.passMinNumber} número(s).`;
        if(this.userForm.errors != null && this.userForm.errors!['minChars']) return `La contraseña debe contener al menos ${this.passMinChars} caracter(es) especial(es).`;
        if(this.userForm.errors != null && this.userForm.errors!['whiteSpaces']) return `La contraseña no debe contener espacios en blanco.`;
      break;
      case 'password_confirmation':
        if(this.userForm.get('password_confirmation')?.hasError('required')) return "Este campo es obligatorio.";
        if(this.userForm.get('password_confirmation')?.hasError('minlength')) return `La contraseña debe tener al menos ${this.passMinLength} caracteres.`;
        if(this.userForm.errors != null && this.userForm.errors!['notSame']) return "Las contraseñas no coinciden.";
      break;
    }
    
    return "A";
  }

  generatePass(){
    const ULET = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
    const LLET = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
    const NUMS = [0, 1, 3, 4, 5, 6, 7, 8, 9];
    const CHAR = ['!', '#', '$', '%', '&', '(', ')', '=', '°', '*', '^', '{', '}', '[', ']', '-', '_', '?'];

    let password = "";
    for(var i = 0; i < 16; i++){
      let arraySelector = Math.floor(Math.random() * 4);
      switch(arraySelector){
        case 0:
          let mayusSelector = Math.floor(Math.random() * ULET.length);
          password += ULET[mayusSelector];
        break;
        case 1:
          let minusSelector = Math.floor(Math.random() * LLET.length);
          password += LLET[minusSelector];
        break;
        case 2:
          let numSelector = Math.floor(Math.random() * NUMS.length);
          password += NUMS[numSelector].toString();
        break;
        case 3:
          let charSelector = Math.floor(Math.random() * NUMS.length);
          password += CHAR[charSelector];
        break;
      }
    }

    this.userForm.controls['password'].setValue(password);
    this.userForm.controls['password_confirmation'].setValue(password);
  }

  async save(){
    let info = this.userForm.getRawValue();
    
    try{
      if(this.isNew){
        await lastValueFrom(this._usersProfilesService.newUser({
          id_user: localStorage.getItem('idusuario'),
          linea: 1,
          name: info.name,
          fApe: info.fApe,
          sApe: info.sApe == "" ? null : info.sApe,
          perf: info.perf,
          email: info.email,
          password: info.password,
          password_confirmation: info.password_confirmation,
        }));
        this._resourcesService.openSnackBar('El usuario fue registrado correctamente.');
      }else{
        let idEnc = await this._encService.encrypt(info.id);
        let idUser = localStorage.getItem('idusuario');
        let perfEnc = await this._encService.encrypt(`${info.perf!}`);

        await lastValueFrom(this._usersProfilesService.updateUser({
          id: idEnc,
          name: info.name,
          fApe: info.fApe,
          sApe: info.sApe == "" ? null : info.sApe,
          email: info.email,
          perf: info.perf,
          id_user: idUser,
          linea: 1
        }));

        this._resourcesService.openSnackBar('El usuario fue actualizado correctamente.');
        this._socketService.profileUpdated(perfEnc);
      }

      this._location.back();
    }catch(error: any){
      if(error.error == undefined){
        this._resourcesService.openSnackBar('Ocurrió un error inesperado');
      }else if(error.error.msg == undefined){
        this._resourcesService.openSnackBar('Ocurrió un error inesperado');
      }else{
        this._resourcesService.openSnackBar(error.error.msg);
      }
    }
  }
}

export class NotSamePasswordsMatcher implements ErrorStateMatcher{
  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
    return form?.form.controls['password'].value != form?.form.controls['password_confirmation'].value;
  }
}

export class GeneralMatcher implements ErrorStateMatcher{
  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
    let errorsArr: string[] = [];
    if(form?.errors != null && form.errors != undefined){
      errorsArr = Object.keys(form?.errors);
    }

    return errorsArr.includes('minUpper') || errorsArr.includes('minNumber') || errorsArr.includes('minChars') || errorsArr.includes('whiteSpaces');
  }
}
