import { Component } from '@angular/core';

import { Store } from '@ngrx/store';
import { forkJoin } from 'rxjs';

import { UserGender, UserStore } from 'src/app/core/interfaces/user.interface';
import { FormData } from 'src/app/shared/interfaces/services/api-fit/request/user.interface';
import { USER_REGEX } from 'src/app/shared/constants/regex.constants';

import { ApiFitService } from 'src/app/shared/services/api-fit/api-fit.service';
import { UserDispatchers } from 'src/app/core/stores/index.dispatchers';
import { AuthRegister } from 'src/app/shared/interfaces/services/api-fit/request/auth.interface';
import { PhysicalActivity } from 'src/app/shared/interfaces/services/api-fit/response/physicalActivity.interface';
import { Goal } from 'src/app/shared/interfaces/services/api-fit/response/goal.interface';

@Component({
  selector: 'app-account-config',
  templateUrl: './account-config.component.html',
  styleUrls: ['./account-config.component.scss'],
})
export class AccountConfigComponent {
  genderOptions: Record<string, UserGender> = {
    Masculino: 'male',
    Feminino: 'female',
  };

  userGender!: keyof typeof this.genderOptions;
  genderOptionsLabels = Object.keys(this.genderOptions);

  user!: UserStore;
  newUser!: UserStore;

  physicalActivies: PhysicalActivity[] = [];
  goals: Goal[] = [];

  isModalPhysicalActivityOpen = false;

  userAvatar!: File;

  userPassword = '';
  userRepeatedPassword = '';

  regexOptions = {
    username: USER_REGEX['username'],
    fullName: USER_REGEX['fullName'],
    email: USER_REGEX['email'],
    password: USER_REGEX['password']
  };

  errorsList: Record<
    keyof FormData | 'repeatedPassword',
    {
      actual: string;
      regexError: string;
    }
  > = {
    fullName: {
      actual: '',
      regexError: 'Por favor, digite um nome completo válido',
    },
    username: {
      actual: '',
      regexError: 'O nome de usuário deve conter apenas letras, números e/ou _',
    },
    email: {
      actual: '',
      regexError: 'Por favor, digite um e-mail válido',
    },
    password: {
      actual: '',
      regexError: 'A senha deve conter no mínimo 8 caracteres',
    },
    repeatedPassword: {
      actual: '',
      regexError: 'A senha deve conter no mínimo 8 caracteres',
    },
    birthDate: {
      actual: '',
      regexError: 'Insira uma data válida',
    },
  };

  validityList: Record<keyof FormData | 'repeatedPassword', boolean> = {
    fullName: true,
    username: true,
    email: true,
    password: true,
    repeatedPassword: true,
    birthDate: true,
  };

  confirmEditIsOpen = false;
  alertModal = false;
  alertModalTitle = '';
  alertModalDescription = '';

  constructor(
    private userStore: Store<{ user: UserStore }>,
    private userDispatchers: UserDispatchers,
    private apiFitService: ApiFitService,
  ) {}

  async ngOnInit() {
    this.userStore.select('user').subscribe({
      next: (p) => {
        this.user = {
          ...this.user,
          ...p,
        };

        this.newUser = {
          ...this.user,
          ...p,
        };
      },
    });

    const [physicalActivies, goals] = await this.fetchPhysicalActivities();
    this.physicalActivies = physicalActivies;
    this.goals = goals;
  }

  handleGenderChange(gender: string) {
    this.userGender = gender;
    this.newUser.gender = this.genderOptions[gender];
  }

  getUserGender() {
    if (this.user.gender === 'male') this.userGender = 'Masculino';
    else this.userGender = 'Feminino';

    return this.userGender;
  }

  setUserAvatar(file: File) {
    this.userAvatar = file;
  }

  getUserBirthDate() {
    const [year, month, day] = new Date(this.newUser.birthDate)
      .toISOString()
      .split('-');

    const date = `${year}-${month}-${day.slice(0, 2)}`;
    return date;
  }

  handleBirthDateChange(value: string) {
    const [year, month, day] = value.split('-');
    const date = new Date(`${month}/${day}/${year}`);

    if (!date.getTime() || value.length < 10) {
      return;
    }

    this.newUser.birthDate = new Date(value);
  }

  updateUser() {
    const changedEntries = Object.entries(this.newUser).filter(
      ([key, value]) => value !== this.user[key as keyof UserStore],
    );
    const userPatch = Object.fromEntries(changedEntries) as AuthRegister;

    if (
      this.userRepeatedPassword &&
      this.userPassword === this.userRepeatedPassword
    ) {
      userPatch.password = this.userRepeatedPassword;
    }

    const requests = [];

    if (this.userAvatar) {
      requests.push(
        this.apiFitService.uploadAvatar({
          image: this.userAvatar,
        }),
      );
    }

    if (Object.values(userPatch).length > 0) {
      if (userPatch.fullName) {
        userPatch.fullName = userPatch?.fullName.trim().replace(/ +/g, ' ');
      }

      if (userPatch.email) {
        userPatch.email = userPatch?.email.trim().replace(/ +/, ' ');
      }

      if (userPatch.password) {
        userPatch.password = this.userPassword.trim();
      }

      requests.push(this.apiFitService.patchUser(userPatch));
    }

    forkJoin(requests).subscribe({
      next: (res: any) => {
        const userWasUpdated = Object.values(userPatch).length > 0;
        let updatedUser: UserStore | null = null;

        if (userWasUpdated) {
          if (res.length > 1) {
            updatedUser = res[1].data;
          } else {
            updatedUser = res[0].data;
          }
        }

        this.alertModalTitle = 'Sucesso';
        this.alertModalDescription = 'Seus dados foram alterados com sucesso';
        this.alertModal = true;

        this.userDispatchers.setUser({
          ...this.user,
          ...this.newUser,
          ...updatedUser
        });
      },
      error: () => {
        this.alertModalTitle = 'Ocorreu um erro';
        this.alertModalDescription = 'Ops... Houve um erro, tente novamente';
        this.alertModal = true;
      },
    });
  }

  checkUsernameDisponibility(username: string) {
    if (username === this.user.username) return;

    if (this.errorsList['username'].actual) {
      this.validityList['username'] = false;
      return;
    }

    this.apiFitService.checkUsernameExists({ username }).subscribe({
      next: () => {
        this.errorsList.username.actual = 'Este username não está disponível';
        this.validityList['username'] = false;
      },
      error: () => {
        this.errorsList.username.actual = '';
        this.validityList['username'] = true;
      },
    });
  }

  checkEmailDisponibility(email: string) {
    if (email === this.user.email) return;

    this.apiFitService.checkEmailExists({ email }).subscribe({
      next: () => {
        this.errorsList.email.actual = 'Este e-mail não está disponível';
        this.validityList['email'] = false;
      },
      error: () => {
        this.errorsList.email.actual = '';
        this.validityList['email'] = true;
      },
    });
  }

  checkUserHasChanged() {
    const userKeys = Object.keys(this.user);
    const newUserKeys = Object.keys(this.newUser);

    if (userKeys.length !== newUserKeys.length) return true;

    if (
      userKeys.some(
        (key) =>
          this.user[key as keyof UserStore] !==
          this.newUser[key as keyof UserStore],
      )
    )
      return true;

    if (this.userAvatar !== undefined) return true;

    if (this.userPassword !== '') return true;

    return false;
  }

  openEditDialog() {
    this.confirmEditIsOpen = true;
  }

  async onConfirmEdit() {
    this.updateUser();
  }

  checkIfWeightIsValid() {
    if (this.newUser.weight < 1) return false;
    return true;
  }

  checkifHeightIsValid() {
    if (this.newUser.height < 0.1) return false;
    return true;
  }

  isFormValid() {
    if(Object.values(this.validityList).some((v) => v === false)) return false;
    
    if(!this.checkIfWeightIsValid()) return false;
    if(!this.checkifHeightIsValid()) return false;

    return true;
  }

  handleMessageError(
    field: keyof FormData | 'repeatedPassword',
    message: string,
  ) {
    this.errorsList[field].actual = message;
    this.validityList[field] = message ? false : true;
  }

  handlePasswordChange(value: string) {
    if (
      value.trim() === this.userRepeatedPassword.trim() &&
      value.trim() === this.userPassword.trim()
    ) {
      this.errorsList.repeatedPassword.actual = '';
      this.validityList['password'] = true;
    } else {
      this.errorsList.repeatedPassword.actual = 'As senhas não coincidem';
      this.validityList['password'] = false;
    }
  }

  validateUsername(username: string) {
    const isNumber = (value: string) => !isNaN(parseFloat(value));
    const errorList = this.errorsList['username'];

    const illegalCombinations = ['..', '__', '._', '_.'];

    if (isNumber(username[0])) {
      errorList.actual = 'O nome de usuário deve começar com uma letra';
      return;
    }

    if (illegalCombinations.some(combination => username.includes(combination))) {
      errorList.actual = 'O nome de usuário não deve conter múltiplos pontos/underlines seguidos';
      return;
    }

    if (username.length > 32) {
      errorList.actual = 'O nome de usuário deve ser menor que 32 caracteres';
      return;
    }
  }

  fetchPhysicalActivities() {
    return new Promise(
      (
        resolve: (
          value: [physicalActivies: PhysicalActivity[], goals: Goal[]],
        ) => void,
        reject: (reason?: string) => void,
      ) => {
        const fetchPhysicalActivities =
          this.apiFitService.getPhysicalActivities();
        const fetchGoalsOptions = this.apiFitService.getGoals();

        const initialRequests = forkJoin([
          fetchPhysicalActivities,
          fetchGoalsOptions,
        ]);

        initialRequests.subscribe({
          next: ([physicalActivies, goals]) => {
            resolve([physicalActivies.data, goals.data]);
          },
          error: reject,
        });
      },
    );
  }

  getPhysicalActivitiesLabels() {
    return this.physicalActivies.map((p) => p.label);
  }

  setPhysicalActivityLabel() {
    const id = this.newUser.physicalActivityId;
    const label = this.physicalActivies.find((ac) => ac.id === id)?.label;

    return label ?? '';
  }

  setPhysicalActivity(label: string) {
    const activity = this.physicalActivies.find(
      (pa) => pa.label.toLowerCase() === label.toLowerCase(),
    );

    this.newUser.physicalActivityId = activity?.id ?? 0;
  }

  getGoalsLabels() {
    return this.goals.map((g) => g.label);
  }

  getGoalValue() {
    const id = this.newUser.goalId;
    const goal = this.goals.find((g) => g.id === id);

    return goal?.label ?? '';
  }

  setGoal(label: string) {
    const goal = this.goals.find(
      (g) => g.label.toLowerCase() === label.toLowerCase(),
    );

    this.newUser.goalId = goal?.id ?? 0;
  }

  toggleModalPhysicalActivity() {
    this.isModalPhysicalActivityOpen = !this.isModalPhysicalActivityOpen;
  }
}
