import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs';

import { environment } from 'src/environments/environment.development';

import {
  AuthLogin,
  AuthRegister,
  CheckEmail,
  CheckUsername,
  GoogleLogin,
} from '../../interfaces/services/api-fit/request/auth.interface';
import { AuthTokenData } from '../../interfaces/services/api-fit/response/auth.interface';

import {
  EditRecipe,
  GetRecipeIngredients,
  LikeRecipe,
  NewRecipe,
  RecipeFilters,
  SaveRecipePicture,
} from '../../interfaces/services/api-fit/request/recipe.interface';
import {
  GenericResponse,
  PagedResponse,
  Response,
} from '../../interfaces/services/api-fit/response/response.interface';
import { Ingredient } from '../../interfaces/services/api-fit/response/ingredient.interface';
import { UserStore } from 'src/app/core/interfaces/user.interface';
import { PhysicalActivity } from '../../interfaces/services/api-fit/response/physicalActivity.interface';
import { Goal } from '../../interfaces/services/api-fit/response/goal.interface';
import { UploadAvatar } from '../../interfaces/services/api-fit/request/user.interface';
import {
  Recipe,
  RecipeTag,
  RecipeTagFilters,
} from '../../interfaces/services/api-fit/response/recipe.interface';
import { QueryParams } from '../../helpers/QueryParams';
import {
  Food,
  MinifiedFood,
} from '../../interfaces/services/api-fit/response/food.interface';
import { FoodFilters } from '../../interfaces/services/api-fit/request/food.interface';
import {
  CreateMeal,
  EditMeal,
  GetMeals,
  MealTimeFilters,
} from '../../interfaces/services/api-fit/request/meal.interface';
import {
  Meal,
  MealTime,
  MealsByDate,
} from '../../interfaces/services/api-fit/response/meal.interface';
import { PersonalIntake } from '../../interfaces/services/api-fit/response/intake.interface';

@Injectable({
  providedIn: 'root',
})
export class ApiFitService {
  readonly baseUrl = environment.apiFitBaseUrl;

  constructor(private http: HttpClient) {}

  login(payload: AuthLogin) {
    return this.http.post<Response<AuthTokenData>>(
      `${this.baseUrl}/auth/login`,
      payload,
    );
  }

  loginWithGoogle(payload: GoogleLogin) {
    return this.http.post<Response<AuthTokenData>>(
      `${this.baseUrl}/auth/sign-in/google`,
      payload,
    );
  }

  signIn(payload: AuthRegister) {
    return this.http.post<Response<AuthTokenData>>(
      `${this.baseUrl}/user`,
      payload,
    );
  }

  uploadAvatar(payload: UploadAvatar) {
    const formData = new FormData();
    formData.append('image', payload.image);

    return this.http.post<GenericResponse>(
      `${this.baseUrl}/user/upload/avatar`,
      formData,
    );
  }

  patchUser(payload: AuthRegister) {
    return this.http.patch<GenericResponse>(`${this.baseUrl}/user`, payload);
  }

  checkUsernameExists(payload: CheckUsername) {
    return this.http.post(`${this.baseUrl}/user/exists/username`, payload);
  }

  checkEmailExists(payload: CheckEmail) {
    return this.http.post(`${this.baseUrl}/user/exists/email`, payload);
  }

  countLikedRecipes() {
    return this.http.get<Response<number>>(
      `${this.baseUrl}/user/count/liked-recipes`,
    );
  }

  getUserInformations() {
    return this.http.get<Response<UserStore>>(`${this.baseUrl}/user`);
  }

  getUsersCount() {
    return this.http.get<Response<number>>(`${this.baseUrl}/users/count`);
  }

  getUserByUsername(username: string) {
    return this.http.get<Response<UserStore>>(
      `${this.baseUrl}/users/username/${username}`,
    );
  }

  getPhysicalActivities() {
    return this.http.get<Response<PhysicalActivity[]>>(
      `${this.baseUrl}/users/physical-activities`,
    );
  }

  getGoals() {
    return this.http.get<Response<Goal[]>>(`${this.baseUrl}/users/goals`);
  }

  getRecipesCount() {
    return this.http.get<Response<number>>(`${this.baseUrl}/recipes/count`);
  }

  getRecipesTags(filter?: RecipeTagFilters) {
    let endpoint = `${this.baseUrl}/recipes/tags`;

    if (filter) {
      const queryParams = QueryParams.toString(filter);
      endpoint += `?${queryParams}`;
    }

    return this.http.get<PagedResponse<RecipeTag>>(endpoint);
  }

  getTagById(tagId: number) {
    return this.http.get<Response<RecipeTag>>(
      `${this.baseUrl}/recipes/tags/${tagId}`,
    );
  }

  getAllRecipes(filter?: RecipeFilters) {
    let endpoint = `${this.baseUrl}/recipes`;

    if (filter) {
      const queryParams = QueryParams.toString(filter);
      endpoint += `?${queryParams}`;
    }

    return this.http.get<PagedResponse<Recipe>>(endpoint)
            .pipe(
              map(
                res => {
                  return {
                    ...res,
                    data: res.data.map((recipe, index) => {
                      const timestamp = new Date().getTime();

                      return {
                        ...recipe,
                        pictureUrl: `${recipe.pictureUrl}?${timestamp}`
                      };
                    })
                  };
                }
              )        
            );
  }

  getRecipeById(recipeId: number) {
    return this.http.get<Response<Recipe>>(
      `${this.baseUrl}/recipes/${recipeId}`,
    ).pipe(
      map(res => {
        const timestamp = new Date().getTime();

        return {
          ...res,
          data: {
            ...res.data,
            pictureUrl: `${res.data.pictureUrl}?${timestamp}`
          }
        };
      })
    );
  }

  getRecommendedRecipes() {
    return this.http.get<Response<Recipe[]>>(
      `${this.baseUrl}/recipes/recommendations`,
    );
  }

  getRecipeIngredients({ recipeId }: GetRecipeIngredients) {
    return this.http.get<Response<Ingredient[]>>(
      `${this.baseUrl}/recipes/${recipeId}/ingredients`,
    );
  }

  likeRecipe({ recipeId }: LikeRecipe) {
    return this.http.post(`${this.baseUrl}/recipes/${recipeId}/like`, null);
  }

  unlikeRecipe({ recipeId }: LikeRecipe) {
    return this.http.delete(`${this.baseUrl}/recipes/${recipeId}/like`);
  }

  excludeRecipe(recipeId: number) {
    return this.http.delete(`${this.baseUrl}/recipes/${recipeId}`);
  }

  createRecipe(payload: NewRecipe) {
    return this.http.post<Response<Recipe>>(`${this.baseUrl}/recipes`, payload);
  }

  editRecipe(recipeId: number, payload: EditRecipe) {
    return this.http.patch<Response<Recipe>>(
      `${this.baseUrl}/recipes/${recipeId}`,
      payload,
    );
  }

  saveRecipePicture(payload: SaveRecipePicture) {
    const formData = new FormData();
    formData.append('image', payload.picture);

    return this.http.post<unknown>(
      `${this.baseUrl}/recipes/${payload.recipeId}/upload/picture`,
      formData,
    );
  }

  getFoods(filter?: FoodFilters) {
    let endpoint = `${this.baseUrl}/foods`;

    if (filter) {
      const queryParams = QueryParams.toString(filter);
      endpoint += `?${queryParams}`;
    }

    return this.http.get<PagedResponse<MinifiedFood>>(endpoint);
  }

  getFoodById(foodId: number) {
    return this.http.get<Response<Food>>(`${this.baseUrl}/foods/${foodId}`);
  }

  getFrequentAddedFoods() {
    return this.http.get<Response<MinifiedFood[]>>(
      `${this.baseUrl}/foods/frequent`,
    );
  }

  getPersonalIntakes() {
    return this.http.get<Response<PersonalIntake[]>>(`${this.baseUrl}/user/personal-intakes`);
  }

  getMealTimes(filter?: MealTimeFilters) {
    let endpoint = `${this.baseUrl}/user/meals/times`;

    if (filter) {
      const queryParams = QueryParams.toString(filter);
      endpoint += `?${queryParams}`;
    }

    return this.http.get<Response<MealTime[]>>(endpoint);
  }

  getMealsByWeek(payload: GetMeals) {
    const queryParams = QueryParams.toString(payload);
    return this.http.get<Response<MealsByDate[]>>(
      `${this.baseUrl}/user/meals/week?${queryParams}`,
    );
  }

  getMealById(mealId: number) {
    return this.http.get<Response<Meal>>(
      `${this.baseUrl}/user/meals/${mealId}`,
    );
  }

  editMeal(mealId: number, payload: EditMeal) {
    return this.http.patch<Response<Meal>>(
      `${this.baseUrl}/user/meals/${mealId}`,
      payload,
    );
  }

  createMeal(payload: CreateMeal) {
    return this.http.post<Response<Meal>>(
      `${this.baseUrl}/user/meals`,
      payload,
    );
  }
}
