import { Component } from '@angular/core';
import {
  Meal,
  MealTime,
  MealsByDate,
} from 'src/app/shared/interfaces/services/api-fit/response/meal.interface';
import { ApiFitService } from 'src/app/shared/services/api-fit/api-fit.service';

import {
  primaryColor,
  quaternaryColor,
  secondaryColor,
} from 'src/app/shared/components/progress/progress.component';
import { QuantifiedNutrient } from 'src/app/shared/interfaces/services/api-fit/response/nutrient.interface';
import { UserService } from 'src/app/shared/services/user/user.service';

import { map } from 'rxjs';
import { NutrientsService } from 'src/app/shared/services/nutrients/nutrients.service';

interface Chart {
  title: string;
  subtitle: string;
  category: string;
  percent: number;
  fill: primaryColor;
  stroke: secondaryColor;
  subtitleColor: quaternaryColor;
}

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss'],
})
export class HomeComponent {
  today = new Date();

  consumedMeals: Meal[] = [];
  mealsByDates: MealsByDate[] = [];
  mealTimes: MealTime[] = [];

  macroNutrients: {
    [key: string]: {
      intake: number,
      needIntake: number,
      unit: string
    }
  } = {
    Calorias: {
      intake: 0,
      needIntake: 0,
      unit: 'kcal',
    },
    Carboidrato: {
      intake: 0,
      needIntake: 0,
      unit: 'g',
    },
    Proteína: {
      intake: 0,
      needIntake: 0,
      unit: 'g',
    },
    Lipídeos: {
      intake: 0,
      needIntake: 0,
      unit: 'g',
    },
  };

  diaryCharts: Chart[] = [
    {
      title: '0kcal',
      subtitle: '0kcal',
      category: 'Calorias',
      fill: 'var(--yellow-100)',
      stroke: 'var(--yellow-050)',
      subtitleColor: 'var(--yellow-300)',
      percent: 0,
    },
    {
      title: '0g',
      subtitle: '0g',
      category: 'Carboidratos',
      fill: 'var(--green-100)',
      stroke: 'var(--green-050)',
      subtitleColor: 'var(--green-300)',
      percent: 0,
    },
    {
      title: '0g',
      subtitle: '0g',
      category: 'Proteínas',
      fill: 'var(--blue-100)',
      stroke: 'var(--blue-050)',
      subtitleColor: 'var(--blue-300)',
      percent: 0,
    },
    {
      title: '0g',
      subtitle: '0g',
      category: 'Lipídeos',
      fill: 'var(--red-100)',
      stroke: 'var(--red-050)',
      subtitleColor: 'var(--red-300)',
      percent: 0,
    },
  ];

  constructor(
    private apiFitService: ApiFitService,
    private userService: UserService,
    private nutrientsService: NutrientsService
  ) {}

  async ngOnInit() {
    this.mealsByDates = await this.userService.fetchMealsByWeek(this.today);
    this.mealTimes = await this.fetchMealsTimes();

    this.setIntakesRecommendation();

    await this.setDailyMeals();
  }

  setIntakesRecommendation() {
    this.apiFitService.getPersonalIntakes()
    .subscribe({
      next: ({data}) => {
        data.forEach(nutrient => {
          const index = this.diaryCharts.findIndex(chart => chart.category.toLowerCase().startsWith(nutrient.name.toLowerCase()));
          this.diaryCharts[index].subtitle = `${nutrient.intake.quantity}${nutrient.intake.unit}`;
        });

        const rdaKcal = this.nutrientsService.getDailyEnergyExpenditure();
        this.macroNutrients['Calorias'].needIntake = rdaKcal;
        this.diaryCharts[0].subtitle = `${rdaKcal}kcal`;
      }
    });
  }

  async setDailyMeals() {
    this.mealsByDates = await this.userService.fetchMealsByWeek(this.today);

    const meals = this.mealsByDates.find(
      (mealsByDates) =>
        mealsByDates.date.split('T')[0] ===
        this.today.toISOString().split('T')[0]
    )?.meals;

    if (!meals) return;

    this.setConsumedMeals(meals);
    this.calculateCharts();
  }

  setConsumedMeals(meals: Meal[]) {
    this.consumedMeals = this.mealTimes.map((time) => {
      let meal = meals?.find((meal) => meal.mealTime === time.name);

      if (!meal) {
        meal = {
          id: 0,
          mealTime: time.name,
          mealDate: this.today,
          foods: [],
          recipes: [],
          nutrients: [],
        };
        return meal;
      }

      return meal;
    });
  }

  fetchMealsTimes() {
    return new Promise(
      (resolve: (mealTimes: MealTime[]) => void, reject: () => void) => {
        this.apiFitService
          .getMealTimes()
          .pipe(
            map((res) => {
              return {
                ...res,
                data: res.data.sort(this.sortMealTimesById),
              };
            })
          )
          .subscribe({
            next: (res) => resolve(res.data),
            error: reject,
          });
      }
    );
  }

  sortMealTimesById(a: MealTime, b: MealTime): number {
    if (a.id < b.id) return -1;
    if (a.id > b.id) return 1;
    return 0;
  }

  async calculateCharts() {
    const mealsData = await Promise.all(
      this.consumedMeals
        .filter((meal) => meal.id)
        .map((meal) => this.getMealData(meal.id))
    );

    const mealsNutrients = mealsData.map((meal) => meal.nutrients);
    
    this.apiFitService.getPersonalIntakes()
    .subscribe({
      next: d => {
        d.data.forEach(intake => {
          this.macroNutrients[intake.name].needIntake = intake.intake.quantity;
        });

        this.macroNutrients['Calorias'].needIntake = this.nutrientsService.getDailyEnergyExpenditure();

        this.macroNutrients = mealsData.reduce((acc, meal) => {
          meal.foods.forEach((food) => {
            const kcal = isNaN(food.kcal) ? 0 : food.kcal;
            acc['Calorias'].intake += kcal;
          });
    
          meal.recipes.forEach((recipe) => {
            const kcal = isNaN(recipe.consumedKcal) ? 0 : recipe.consumedKcal;
            acc['Calorias'].intake += kcal;
          });
    
          return acc;
        }, this.macroNutrients);
    
        this.macroNutrients = mealsNutrients.reduce((acc, mealNutrients) => {
          const getNutrientQuantity = (
            quantity: QuantifiedNutrient['quantity']
          ) => {
            const quantityToReturn = parseFloat(quantity.toString());
            return isNaN(quantityToReturn) ? 0 : quantityToReturn;
          };
    
          if (!mealNutrients) return acc;
    
          const carboIndex = mealNutrients.findIndex(
            (n) => n.name === 'Carboidrato'
          );
          if (carboIndex !== -1) {
            const carbo = mealNutrients[carboIndex];
            acc['Carboidrato'].intake += getNutrientQuantity(carbo.quantity);
          }
    
          const protIndex = mealNutrients.findIndex((n) => n.name === 'Proteína');
          if (protIndex !== -1) {
            const prot = mealNutrients[protIndex];
            acc['Proteína'].intake += getNutrientQuantity(prot.quantity);
          }
    
          const lipIndex = mealNutrients.findIndex((n) => n.name === 'Lipídeos');
          if (lipIndex !== -1) {
            const lip = mealNutrients[lipIndex];
            acc['Lipídeos'].intake += getNutrientQuantity(lip.quantity);
          }
    
          return acc;
        }, this.macroNutrients);
    
        this.diaryCharts[0].title = `${this.macroNutrients['Calorias'].intake}${this.macroNutrients['Calorias'].unit}`;
        this.diaryCharts[0].percent = this.calculateChartPercentage(
          this.macroNutrients['Calorias'].intake,
          this.macroNutrients['Calorias'].needIntake
        );
    
        this.diaryCharts[1].title = `${this.macroNutrients['Carboidrato'].intake}${this.macroNutrients['Carboidrato'].unit}`;
        this.diaryCharts[1].percent = this.calculateChartPercentage(
          this.macroNutrients['Carboidrato'].intake,
          this.macroNutrients['Carboidrato'].needIntake
        );
    
        this.diaryCharts[2].title = `${this.macroNutrients['Proteína'].intake}${this.macroNutrients['Proteína'].unit}`;
        this.diaryCharts[2].percent = this.calculateChartPercentage(
          this.macroNutrients['Proteína'].intake,
          this.macroNutrients['Proteína'].needIntake
        );
    
        this.diaryCharts[3].title = `${this.macroNutrients['Lipídeos'].intake}${this.macroNutrients['Lipídeos'].unit}`;
        this.diaryCharts[3].percent = this.calculateChartPercentage(
          this.macroNutrients['Lipídeos'].intake,
          this.macroNutrients['Lipídeos'].needIntake
        );
      }
    });
  }

  calculateChartPercentage(intake: number, needIntake: number): number {
    const percentage = Math.round((intake * 100) / needIntake);
    return percentage > 100 ? 100 : percentage;
  }

  getMealData(mealId: number) {
    return new Promise(
      (resolve: (meal: Meal) => void, reject: (error: unknown) => void) => {
        this.apiFitService.getMealById(mealId).subscribe({
          next: (r) => resolve(r.data),
          error: reject,
        });
      },
    );
  }

  async getDailyMeals() {
    const today = this.today.toISOString().split('T')[0];

    this.apiFitService
      .getMealsByWeek({
        date: today,
      })
      .subscribe({
        next: (r) => {
          const mealsByDate = r.data;

          const meals = mealsByDate.find(
            (mealsByDate) => mealsByDate.date.split('T')[0] === today
          )?.meals;

          this.consumedMeals = meals!;
          this.calculateCharts();
        },
      });
  }
}
