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

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

import { SignupStore } from 'src/app/core/interfaces/store/signup.store';
import { SignupDispatchers } from 'src/app/core/stores/index.dispatchers';
import { Router } from '@angular/router';
import { ApiFitService } from 'src/app/shared/services/api-fit/api-fit.service';
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';
import { Location } from '@angular/common';
import { UserService } from 'src/app/shared/services/user/user.service';
import { AuthService } from 'src/app/core/services/auth/auth.service';
import { AuthRegister } from 'src/app/shared/interfaces/services/api-fit/request/auth.interface';

type NeededFields = keyof Pick<
  SignupStore,
  | 'fullName'
  | 'username'
  | 'email'
  | 'password'
  | 'gender'
  | 'weight'
  | 'height'
  | 'birthDate'
  | 'physicalActivityId'
  | 'goalId'
>;

@Component({
  selector: 'app-activity',
  templateUrl: './activity.component.html',
  styleUrls: ['./activity.component.scss'],
})
export class ActivityComponent {
  signup$: Observable<SignupStore>;
  signupModel: SignupStore = {} as SignupStore;
  recommendedRate = 0;

  fieldsAreFilled: Record<NeededFields, boolean> = {
    fullName: false,
    username: false,
    email: false,
    password: false,
    gender: false,
    weight: false,
    height: false,
    birthDate: false,
    physicalActivityId: false,
    goalId: false,
  };

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

  isModalPhysicalActivityOpen = false;

  constructor(
    private store: Store<{ signup: SignupStore }>,
    private apiFitService: ApiFitService,
    private userService: UserService,
    private authService: AuthService,
    private signupDispatchers: SignupDispatchers,
    private router: Router,
    private location: Location,
  ) {
    this.signup$ = this.store.select('signup');
  }

  async ngOnInit() {
    await this.setFieldsFromStore();
    const [physicalActivies, goals] = await this.fetchFormData();

    this.physicalActivies = physicalActivies;
    this.goals = goals;

    this.calculateRecommendedRate();
  }

  isSocialSignup() {
    return !!this.signupModel.googleToken;
  }

  setFieldsFromStore() {
    return new Promise((resolve, reject) => {
      this.signup$.subscribe({
        next: (data) => {
          Object.assign(this.signupModel, data);

          const storeFields = Object.keys(data) as NeededFields[];
          const ignoreFields: NeededFields[] = ['physicalActivityId', 'goalId'];
          if (
            !this.isSocialSignup() &&
            storeFields.some(
              (field) => !data[field] && !ignoreFields.includes(field),
            )
          ) {
            this.router.navigate(['app/auth/signup']);
            reject();
          }

          this.validateForm();
          resolve(true);
        },
        error: () => {
          this.router.navigate(['app/auth/signup']);
          reject();
        },
      });
    });
  }

  fetchFormData() {
    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,
        });
      },
    );
  }

  handleGoBack() {
    this.location.back();
  }

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

  setPhysicalActivityLabel() {
    const id = this.signupModel.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.signupModel.physicalActivityId = activity?.id ?? 0;
    activity && this.updateStore();
  }

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

  getGoalValue() {
    const id = this.signupModel.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.signupModel.goalId = goal?.id ?? 0;
    goal && this.updateStore();
  }

  updateStore() {
    this.signupDispatchers.setSignup({ ...this.signupModel });
    this.calculateRecommendedRate();
  }

  validateForm() {
    Object.entries(this.signupModel).forEach(([key, value]) => {
      const requiredFields = Object.keys(this.fieldsAreFilled);

      if (requiredFields.includes(key)) {
        this.fieldsAreFilled[key as NeededFields] = !!value;
      }
    });
  }

  isFormValid(): boolean {
    if (this.isSocialSignup()) {
      return !!this.signupModel.physicalActivityId && !!this.signupModel.goalId;
    }

    return Object.values(this.fieldsAreFilled).every((value) => value);
  }

  calculateRecommendedRate() {
    const { gender, weight, height, birthDate, physicalActivityId, goalId } =
      this.signupModel;
    const age = this.userService.calculateAge(birthDate);
    const activity = this.physicalActivies.find(
      (ac) => ac.id === physicalActivityId,
    );
    const goal = this.goals.find((g) => g.id === goalId);

    if (!activity || !goal) return;

    const tmb = this.userService.calculateTMB(
      gender,
      weight,
      height * 100,
      age ?? 0,
    );
    const get = this.userService.calculateGET(tmb, activity.tdeeMultiplier);
    const recommendedRate = this.userService.calculateRecommendedRate(
      get,
      goal.bmrOffset,
    );

    this.recommendedRate = Math.round(recommendedRate);
  }

  submitForm(ev: Event) {
    ev.preventDefault();

    if (!this.isFormValid()) return;

    const payload: AuthRegister = this.signupModel;

    if (this.isSocialSignup() && !this.signupModel.password) {
      payload.password = undefined;
    }

    this.apiFitService.signIn(payload).subscribe({
      next: (res) => {
        try {
          this.authService.setAuthorizationToken(res.data.accessToken);
          this.router.navigate(['app/auth/signup/avatar']);
        } catch {
          return;
        }
      },
    });
  }

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