import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';

import { UserStore } from 'src/app/core/interfaces/user.interface';
import { Ingredient } from '../../interfaces/services/api-fit/response/ingredient.interface';
import {
  NutrientUnit,
  QuantifiedNutrient,
  ReferencedNutrient,
} from '../../interfaces/services/api-fit/response/nutrient.interface';
import {
  Intake,
  NutrientIntake,
} from '../../interfaces/services/api-fit/response/intake.interface';
import { UserService } from '../user/user.service';

@Injectable({
  providedIn: 'root',
})
export class NutrientsService {
  private user!: UserStore;
  private ageRange: keyof NutrientIntake = '19-30';

  constructor(
    private store: Store<{ user: UserStore }>,
    private userService: UserService,
  ) {
    this.store.select('user').subscribe({
      next: (user) => {
        this.user = user;
        this.setAgeRange();
      },
    });
  }

  private units: Record<NutrientUnit, string> = {
    g: 'g',
    mg: 'mg',
    mcg: 'mcg',
    percents: '%',
  };

  sumNutrients(ingredients: Ingredient[]): QuantifiedNutrient[] {
    const summarizedNutrients: QuantifiedNutrient[] = [];

    ingredients.forEach((ingredient) => {
      const nutrients = ingredient.nutrients;

      nutrients.forEach((nutrient) => {
        const nutrientIndex = summarizedNutrients.findIndex(
          (n) => n.name === nutrient.name,
        );
        let newQuantity: QuantifiedNutrient['quantity'] = 0;

        if (typeof nutrient.quantityPer100 === 'number') {
          newQuantity = (ingredient.quantity * nutrient.quantityPer100) / 100;
        } else {
          newQuantity = nutrient.quantityPer100;
        }

        if (nutrientIndex === -1) {
          summarizedNutrients.push({
            ...nutrient,
            quantity: newQuantity,
          });

          return;
        }

        const nutrientQuantity = summarizedNutrients[nutrientIndex].quantity;

        if (typeof nutrientQuantity !== typeof newQuantity) {
          const numericQuantity =
            typeof nutrientQuantity === 'number'
              ? nutrientQuantity
              : newQuantity;

          summarizedNutrients[nutrientIndex].quantity = numericQuantity;

          return;
        }

        const summedQuantity =
          (nutrientQuantity as number) + (newQuantity as number);
        summarizedNutrients[nutrientIndex].quantity = summedQuantity;
      });
    });

    return summarizedNutrients;
  }

  private setAgeRange() {
    const userAge = this.userService.calculateAge(this.user.birthDate) ?? 0;

    const ageRanges: Record<keyof NutrientIntake, () => boolean> = {
      '0-0.5': () => userAge <= 0.5,
      '0.6-1': () => userAge <= 1,
      '1-3': () => userAge <= 3,
      '4-8': () => userAge <= 6,
      '9-13': () => userAge <= 13,
      '14-18': () => userAge <= 18,
      '19-30': () => userAge <= 30,
      '31-50': () => userAge <= 50,
      '51-70': () => userAge <= 70,
      '>70': () => userAge > 70,
    };

    const ageRange = Object.keys(ageRanges).find(
      (r) => ageRanges[r as keyof NutrientIntake]?.(),
    );
    this.ageRange = (ageRange ?? '19-30') as keyof NutrientIntake;
  }

  calculateVD(
    nutrient: QuantifiedNutrient,
    quantity: number,
  ): ReferencedNutrient {
    let vd: number | '*' = '*';

    if (!nutrient.intakes) {
      return {
        ...nutrient,
        vd: '*',
      };
    }

    const recommendedIntake =
      nutrient.intakes[this.ageRange as keyof NutrientIntake] ??
      nutrient.intakes['personal' as keyof NutrientIntake];

    if (!recommendedIntake) {
      return {
        ...nutrient,
        vd: '*',
      };
    }

    let needIntake = Object.values(recommendedIntake)[0] as Intake;

    if (Object.keys(recommendedIntake).includes('quantity')) {
      needIntake = recommendedIntake as Intake;
    } else if (!Object.keys(recommendedIntake).includes('both')) {
      const needIntakeKey =
        Object.keys(recommendedIntake).find((k) => k === this.user.gender) ??
        'male';
      needIntake = recommendedIntake[
        needIntakeKey as keyof typeof recommendedIntake
      ] as Intake;
    }

    if (!needIntake?.quantity) {
      vd = '*';
    } else {
      vd = (quantity * 100) / (needIntake.quantity ?? 0) || 0;
    }

    return {
      ...nutrient,
      vd: vd,
    };
  }

  calculateTotalEnergy(ingredients: Ingredient[]): number {
    const energy = ingredients.reduce((acc, ingredient) => {
      const kcalPer100 = isNaN(ingredient.kcalPer100)
        ? 0
        : ingredient.kcalPer100;

      acc += kcalPer100 * (ingredient.quantity / 100);
      return acc;
    }, 0);

    return Math.round(energy);
  }

  getUnit(unit: NutrientUnit): string {
    return this.units[unit];
  }

  getDailyEnergyExpenditure(): number {
    return this.user.totalDailyEnergyExpenditure;
  }
}
