import {
  BadGatewayException,
  BadRequestException,
  ConflictException,
  ForbiddenException,
  Injectable,
  InternalServerErrorException,
  NotFoundException,
} from '@nestjs/common';
import {
  RetrieveMealsForTheWeekQueryParamsDto,
  RetrieveMealsForTheWeekResponseDto,
  RetrieveMealsForTheWeek_MealsForTheDay,
} from './dtos/retrieve-meals-for-the-week.dto';
import moment from 'moment';
import { EntityManager } from 'typeorm';
import { IUser } from '../../../../shared/decorators/auth/user.decorator';
import { MealFoodDto } from '../../../../shared/dtos/meals/meal-food.dto';
import { MealDto } from '../../../../shared/dtos/meals/meal.dto';
import { SingleIdParamsDto } from '../../../../shared/dtos/params/single-id-params.dto';
import { mergeAndSumIntakesAndNutrientsQuantity } from '../../../../shared/functions/nutrients/merge-and-sum-intakes-and-nutrients-quantity.function';
import {
  RetrieveMealResponseDto,
  RetrieveMeal_Meal,
} from './dtos/retrieve-meal.dto';
import { CreateMealDto, CreateMeal_Recipe } from './dtos/create-meal.dto';
import {
  RetrieveMealsTimesQueryParamsDto,
  RetrieveMealsTimesResponseDto,
} from './dtos/retrieve-meals-times.dto';
import {
  UpdateMealDto,
  UpdateMeal_Food,
  UpdateMeal_Recipe,
} from './dtos/update-meal.dto';
import isEmpty from 'lodash/isEmpty';
import { retrieveUserTDEE } from '../../../../shared/functions/user/retrieve-user-tdee.function';

@Injectable()
export class MealsService {
  constructor(private readonly _entityManager: EntityManager) {}

  async retrieveMealsForTheWeek(
    queryParams: RetrieveMealsForTheWeekQueryParamsDto,
    locale: string,
    user: IUser,
  ): Promise<RetrieveMealsForTheWeekResponseDto> {
    const { id } = user;
    const { date } = queryParams;

    const localeDate = moment(moment(date).locale(locale));

    const startOfWeek = localeDate
      .startOf('week')
      .utc(false)
      .startOf('day')
      .toDate();

    const endOfWeek = localeDate
      .endOf('week')
      .utc(false)
      .startOf('day')
      .toDate();

    const MEALS_SQL = `
	    SELECT
        meal.date AS date,
        JSON_ARRAYAGG(
          JSON_OBJECT(
            'id', meal.id,
            'mealTime', meal_time.name,
            'foods', (
              SELECT
                JSON_ARRAYAGG(
                  JSON_OBJECT(
                    'id', food.id,
                    'name', food.name,
                    'quantity', meal_has_food.quantity,
                    'kcal', IFNULL(ROUND(food.kcal * meal_has_food.quantity / 100), '*')
                  )
                )
              FROM meal_has_food
              INNER JOIN food
                ON meal_has_food.food_id = food.id
              WHERE meal_has_food.meal_id = meal.id
              GROUP BY
                meal.id
            ),
            'recipes', (
              SELECT
                JSON_ARRAYAGG(
                  JSON_OBJECT(
                    'id', recipe.id,
                    'name', recipe.name,
                    'servings', recipe.servings,
                    'consumedServings', meal_has_recipe.consumed_servings,
                    'consumedKcal', (
                      SELECT
                        ROUND(SUM(food.kcal * recipe_has_food.quantity / 100) / recipe.servings * meal_has_recipe.consumed_servings)
                      FROM recipe
                      INNER JOIN recipe_has_food
                        ON recipe.id = recipe_has_food.recipe_id
                      INNER JOIN food
                        ON recipe_has_food.food_id = food.id
                      WHERE recipe.id = meal_has_recipe.recipe_id
                    )
                  )
                )
              FROM recipe
              INNER JOIN meal_has_recipe
                ON recipe.id = meal_has_recipe.recipe_id
              WHERE meal_has_recipe.meal_id = meal.id
            )
          )
        ) meals
      FROM meal
      INNER JOIN meal_time
        ON meal.meal_time_id = meal_time.id
      WHERE user_person_id = ?
        AND date BETWEEN ? AND ?
      GROUP BY
        meal.date
    `;

    const mealsForTheWeek: RetrieveMealsForTheWeek_MealsForTheDay[] =
      await this._entityManager
        .query(MEALS_SQL, [id, startOfWeek, endOfWeek])
        .catch(() => {
          throw new BadGatewayException();
        });

    if (!mealsForTheWeek || mealsForTheWeek.length === 0) return { data: [] };

    mealsForTheWeek.forEach((mealsForTheDay) => {
      mealsForTheDay.meals = mealsForTheDay?.meals?.map((meal) => {
        meal.foods = meal?.foods?.map((food) => new MealFoodDto(food));

        return new MealDto(meal);
      });
    });

    return {
      data: mealsForTheWeek,
    };
  }

  async retrieveMeal(
    params: SingleIdParamsDto,
    user: IUser,
  ): Promise<RetrieveMealResponseDto> {
    const { id } = params;
    const { id: userId } = user;

    const MEAL_SQL = `
	    SELECT
        meal.id AS id,
        meal.date AS mealDate,
        meal_time.name AS mealTime,
        (
          SELECT
            JSON_ARRAYAGG(
              JSON_OBJECT(
                'id', food.id,
                'name', food.name,
                'quantity', meal_has_food.quantity,
                'kcal', IFNULL(ROUND(food.kcal * meal_has_food.quantity / 100), '*')
              )
            )
          FROM meal_has_food
          INNER JOIN food
            ON meal_has_food.food_id = food.id
          WHERE meal_has_food.meal_id = meal.id
          GROUP BY
            meal.id
        ) AS foods,
        (
          SELECT
            JSON_ARRAYAGG(
              JSON_OBJECT(
                'id', recipe.id,
                'name', recipe.name,
                'servings', recipe.servings,
                'consumedServings', meal_has_recipe.consumed_servings,
                'consumedKcal', (
                  SELECT
                    ROUND(SUM(food.kcal * recipe_has_food.quantity / 100) / recipe.servings * meal_has_recipe.consumed_servings)
                  FROM recipe
                  INNER JOIN recipe_has_food
                    ON recipe.id = recipe_has_food.recipe_id
                  INNER JOIN food
                    ON recipe_has_food.food_id = food.id
                  WHERE recipe.id = meal_has_recipe.recipe_id
                )
              )
            )
          FROM recipe
          INNER JOIN meal_has_recipe
            ON recipe.id = meal_has_recipe.recipe_id
          WHERE meal_has_recipe.meal_id = meal.id
        ) AS recipes,
        (
          SELECT
            JSON_ARRAYAGG(
              JSON_OBJECT(
                'id', nutrient_quantity.id,
                'name', nutrient_quantity.name,
                'category', nutrient_quantity.category,
                'intakes', nutrient_quantity.intakes,
                'personalIntakes', nutrient_quantity.personal_intakes
              )
            )
          FROM
          (
            SELECT
              nutrient.id AS id,
              nutrient.name AS name, 
              nutrient_category.name AS category,
              (
                SELECT
                  JSON_ARRAYAGG(
                    JSON_OBJECT(
                      daily_intake_age_group.name, JSON_OBJECT(
                        nutrient_has_daily_intake.gender, JSON_OBJECT(
                          'quantity', nutrient_has_daily_intake.quantity,
                          'unit', nutrient_has_daily_intake.unit
                        )
                      )
                    )
                  )
                FROM nutrient_has_daily_intake
                INNER JOIN daily_intake_age_group
                  ON nutrient_has_daily_intake.daily_intake_age_group_id = daily_intake_age_group.id
                WHERE nutrient_has_daily_intake.nutrient_id = nutrient.id
              ) AS intakes,
              IF(
                nutrient_personal_intake.nutrient_id IS NOT NULL,
                JSON_OBJECT(
                  'kcalPerGram', nutrient_personal_intake.kcal_per_gram,
                  'tdeePercentage', nutrient_personal_intake.tdee_percentage
                ),
                null
              ) AS personal_intakes
            FROM meal AS _meal
            LEFT JOIN meal_has_food
              ON _meal.id = meal_has_food.meal_id
            LEFT JOIN meal_has_recipe
              ON _meal.id = meal_has_recipe.meal_id
            LEFT JOIN recipe
              ON meal_has_recipe.recipe_id = recipe.id
            LEFT JOIN recipe_has_food
              ON meal_has_recipe.recipe_id = recipe_has_food.recipe_id
            LEFT JOIN food_has_nutrient
              ON meal_has_food.food_id = food_has_nutrient.food_id
              OR recipe_has_food.food_id = food_has_nutrient.food_id
            INNER JOIN nutrient
              ON food_has_nutrient.nutrient_id = nutrient.id
            LEFT JOIN nutrient_personal_intake
              ON nutrient.id = nutrient_personal_intake.nutrient_id
            LEFT JOIN nutrient_category
              ON nutrient.nutrient_category_id = nutrient_category.id
            WHERE _meal.id = ?
            GROUP BY
              nutrient.id,
              personal_intakes
          ) AS nutrient_quantity
        ) AS nutrients
      FROM meal
      INNER JOIN meal_time
        ON meal.meal_time_id = meal_time.id
      WHERE user_person_id = ?
        AND meal.id = ?
    `;

    const [meal]: [RetrieveMeal_Meal] = await this._entityManager
      .query(MEAL_SQL, [id, userId, id])
      .catch(() => {
        throw new BadGatewayException();
      });

    if (!meal) throw new NotFoundException();

    const MEAL_FOODS_NUTRIENTS_SQL = `
      SELECT
        nutrient.id AS id,
        JSON_ARRAYAGG(
          JSON_OBJECT(
            'nutrientQuantity', IFNULL(food_has_nutrient.quantity, food_has_nutrient.flag),
            'foodQuantity', meal_has_food.quantity,
            'unit', food_has_nutrient.unit
          )
        ) AS quantity
      FROM meal_has_food
      INNER JOIN food_has_nutrient
        ON meal_has_food.food_id = food_has_nutrient.food_id
      INNER JOIN nutrient
        ON food_has_nutrient.nutrient_id = nutrient.id
      LEFT JOIN nutrient_personal_intake
        ON nutrient.id = nutrient_personal_intake.nutrient_id
      LEFT JOIN nutrient_category
        ON nutrient.nutrient_category_id = nutrient_category.id
      WHERE meal_has_food.meal_id = ?
      GROUP BY nutrient.id
    `;

    const destructuredMealFoodsNutrients = await this._entityManager
      .query(MEAL_FOODS_NUTRIENTS_SQL, [id])
      .catch(() => {
        throw new BadGatewayException();
      });

    const RECIPES_NUTRIENTS_SQL = `
      SELECT
        JSON_ARRAYAGG(
          JSON_OBJECT(
            'id', _quantities.id,
            'quantity', _quantities.quantity
          )
        ) AS quantities
      FROM (
        SELECT
          nutrient.id AS id,
          JSON_ARRAYAGG(
            JSON_OBJECT(
              'nutrientQuantity', IFNULL(food_has_nutrient.quantity, food_has_nutrient.flag),
              'foodQuantity', recipe_has_food.quantity * (_meal_has_recipe.consumed_servings / recipe.servings),
              'unit', food_has_nutrient.unit
            )
          ) AS quantity
        FROM recipe
        INNER JOIN recipe_has_food
          ON recipe.id = recipe_has_food.recipe_id
        INNER JOIN food_has_nutrient
          ON recipe_has_food.food_id = food_has_nutrient.food_id
        INNER JOIN nutrient
          ON food_has_nutrient.nutrient_id = nutrient.id
        LEFT JOIN nutrient_personal_intake
          ON nutrient.id = nutrient_personal_intake.nutrient_id
        LEFT JOIN nutrient_category
          ON nutrient.nutrient_category_id = nutrient_category.id
        INNER JOIN meal_has_recipe AS _meal_has_recipe
          ON recipe.id = _meal_has_recipe.recipe_id  -- Add this JOIN clause
        WHERE _meal_has_recipe.meal_id = ?
        GROUP BY nutrient.id
      ) AS _quantities
    `;

    const destructuredRecipesNutrients = await this._entityManager
      .query(RECIPES_NUTRIENTS_SQL, [id, id])
      .catch(() => {
        throw new BadGatewayException();
      });

    if (meal.nutrients && meal.nutrients.length !== 0) {
      const mealFoodsNutrients = {};
      const recipesNutrients = {};

      if (
        destructuredMealFoodsNutrients &&
        destructuredMealFoodsNutrients.length !== 0
      ) {
        destructuredMealFoodsNutrients.forEach((nutrient) => {
          mealFoodsNutrients[nutrient.id] = nutrient.quantity;
        });
      }

      if (
        destructuredRecipesNutrients &&
        destructuredRecipesNutrients.length !== 0
      ) {
        destructuredRecipesNutrients.forEach((destructuredRecipeNutrients) => {
          destructuredRecipeNutrients?.quantities?.forEach((nutrient) => {
            recipesNutrients[nutrient.id] = [
              ...(recipesNutrients[nutrient.id] ?? []),
              ...(nutrient.quantity ?? []),
            ];
          });
        });
      }

      meal.nutrients.map((nutrient) => {
        nutrient.quantity = [
          ...(mealFoodsNutrients[nutrient.id] ?? []),
          ...(recipesNutrients[nutrient.id] ?? []),
        ] as any;
      });

      let userTDEE: number | null | undefined;

      if (user) userTDEE = await retrieveUserTDEE(this._entityManager, user);

      mergeAndSumIntakesAndNutrientsQuantity(meal.nutrients, userTDEE);
    }

    meal.foods = meal?.foods?.map((food) => new MealFoodDto(food));

    return {
      data: new RetrieveMeal_Meal(meal),
    };
  }

  private async _retrieveMealsTimesByIds(ids: number[]) {
    const TAGS_BY_ID_SQL = `
      SELECT
        id,
        name
      FROM meal_time
      WHERE id IN (${ids.map(() => '?').join(',')})
    `;

    const tags = await this._entityManager
      .query(TAGS_BY_ID_SQL, ids)
      .catch(() => {
        throw new BadGatewayException();
      });

    return { data: tags ?? [] };
  }

  async retrieveMealsTimes(
    queryParams: RetrieveMealsTimesQueryParamsDto,
  ): Promise<RetrieveMealsTimesResponseDto> {
    const { page, search_by_name: searchByName, ids } = queryParams;

    if (ids) return this._retrieveMealsTimesByIds(ids);

    const mealsTimesPerPage = 20;

    const MEAL_TIMES_SQL = `
      SELECT 
        id,
        name
      FROM meal_time
      ${searchByName ? 'WHERE MATCH(name) AGAINST(? IN BOOLEAN MODE)' : ''}
      LIMIT ${mealsTimesPerPage} OFFSET ${(page - 1) * mealsTimesPerPage};
    `;

    let parameters: any[];

    if (searchByName) {
      const searchTerms = searchByName.replace(/ +/g, '*+*');

      parameters = [`*${searchTerms}*`];
    }

    const mealsTimes = await this._entityManager
      .query(MEAL_TIMES_SQL, parameters)
      .catch(() => {
        throw new BadGatewayException();
      });

    if (!mealsTimes || mealsTimes.length === 0) return { data: [] };

    const COUNT_SQL = `
      SELECT
        COUNT(*) AS count
      FROM meal_time
      ${searchByName ? 'WHERE MATCH(name) AGAINST(? IN BOOLEAN MODE)' : ''}
    `;

    const [{ count }] = await this._entityManager
      .query(COUNT_SQL, parameters)
      .catch(() => {
        throw new BadGatewayException();
      });

    return {
      data: mealsTimes,
      actualPage: page,
      totalPages: Math.ceil(count / mealsTimesPerPage),
    };
  }

  private async _checkIfRecipesWereNotDeleted(recipes: CreateMeal_Recipe[]) {
    const SQL = `
      SELECT COUNT(*) AS count FROM recipe
      WHERE id IN (${Array(recipes.length).fill('?').join(',')})
        AND was_deleted = false
    `;

    const [{ count }] = await this._entityManager
      .query(
        SQL,
        recipes.map((recipe) => recipe.id),
      )
      .catch(() => {
        throw new BadGatewayException();
      });

    if (Number(count) === recipes.length) return;

    throw new ConflictException('Receita não existe');
  }

  async createMeal(
    meal: CreateMealDto,
    user: IUser,
  ): Promise<RetrieveMealResponseDto> {
    let mealId = null;

    if (meal.recipes && meal.recipes.length !== 0) {
      await this._checkIfRecipesWereNotDeleted(meal.recipes);
    }

    await this._entityManager
      .transaction(async (transaction) => {
        const MEAL_SQL =
          'INSERT INTO meal (user_person_id, meal_time_id, date) VALUES (?, ?, ?)';

        await transaction.query(MEAL_SQL, [
          user.id,
          meal.mealTimeId,
          meal.date,
        ]);

        [{ mealId }] = await transaction.query(
          'SELECT LAST_INSERT_ID() AS mealId',
        );

        if (meal.foods && meal.foods.length !== 0) {
          const FOODS_SQL = `
            INSERT INTO meal_has_food (meal_id, food_id, quantity)
            VALUES ${Array(meal.foods.length).fill('(?, ?, ?)').join(',')}
          `;

          await transaction.query(
            FOODS_SQL,
            meal.foods.flatMap((food) => [mealId, food.id, food.quantity]),
          );
        }

        if (meal.recipes && meal.recipes.length !== 0) {
          const RECIPES_SQL = `
            INSERT INTO meal_has_recipe (meal_id, recipe_id, consumed_servings)
            VALUES ${Array(meal.recipes.length).fill('(?, ?, ?)').join(',')}
          `;

          await transaction.query(
            RECIPES_SQL,
            meal.recipes.flatMap((recipe) => [
              mealId,
              recipe.id,
              recipe.consumedServings,
            ]),
          );
        }
      })
      .catch((error) => {
        if (error.message.includes('FK_MealHasRecipe_Recipe'))
          throw new ConflictException('Receita não existe');

        if (error.message.includes('FK_MealHasFood_Food'))
          throw new ConflictException('Alimento não existe');

        if (error.message.includes('FK_Meal_MealTime'))
          throw new ConflictException('Horário de refeição não existe');

        if (
          error.message.includes('UQ_Meal_Date_And_MealTimeId_Per_UserPersonId')
        ) {
          throw new ConflictException(
            'Refeição para o dia e horário especificado já existe',
          );
        }

        throw new BadGatewayException();
      });

    if (!mealId) throw new InternalServerErrorException();

    return this.retrieveMeal({ id: mealId }, user);
  }

  private async _checkIfRecipesToCreateWereNotDeleted(
    mealId: number,
    recipes: CreateMeal_Recipe[],
  ) {
    const SQL = `
      SELECT
        JSON_ARRAYAGG(recipe_id) AS alreadyAddedRecipes
      FROM meal_has_recipe
      WHERE meal_id = ?
    `;

    const [{ alreadyAddedRecipes }] = await this._entityManager
      .query(SQL, [mealId])
      .catch(() => {
        throw new BadGatewayException();
      });

    const recipesToCreate = alreadyAddedRecipes
      ? recipes.filter((recipe) => !alreadyAddedRecipes.includes(recipe.id))
      : recipes;

    if (recipesToCreate.length === 0) return;

    await this._checkIfRecipesWereNotDeleted(recipesToCreate);
  }

  private async _checkIfUserOwnsMeal(recipeId: number, user: IUser) {
    const CREATOR_SQL = 'SELECT user_person_id AS id FROM meal WHERE id = ?';

    const [creator] = await this._entityManager
      .query(CREATOR_SQL, [recipeId])
      .catch(() => {
        throw new BadGatewayException();
      });

    if (!creator) throw new NotFoundException();

    if (creator.id !== user?.id) throw new ForbiddenException();
  }

  private _updateFoods(
    transaction: EntityManager,
    recipeId: number,
    foods: UpdateMeal_Food[],
  ): Promise<any>[] {
    const queries = [];

    const toCreateOrUpdate = foods.filter((food) => food.delete !== true);

    const toDelete = foods.filter((food) => food.delete);

    if (toCreateOrUpdate.length !== 0) {
      const TO_CREATE_OR_UPDATE_SQL = `
        INSERT INTO meal_has_food (meal_id, food_id, quantity)
        VALUES ${Array(toCreateOrUpdate.length).fill('(?, ?, ?)').join(',')}
        ON DUPLICATE KEY
          UPDATE quantity = VALUES(quantity);
      `;

      const query = transaction.query(
        TO_CREATE_OR_UPDATE_SQL,
        toCreateOrUpdate.flatMap((food) => [recipeId, food.id, food.quantity]),
      );

      queries.push(query);
    }

    if (toDelete.length !== 0) {
      const TO_DELETE_SQL = `
        DELETE FROM meal_has_food WHERE meal_id = ?
          AND food_id IN (${Array(toDelete.length).fill('?').join(',')})
      `;

      const query = transaction.query(TO_DELETE_SQL, [
        recipeId,
        ...toDelete.map((food) => food.id),
      ]);

      queries.push(query);
    }

    return queries;
  }

  private _updateRecipes(
    transaction: EntityManager,
    recipeId: number,
    recipes: UpdateMeal_Recipe[],
  ): Promise<any>[] {
    const queries = [];

    const toCreateOrUpdate = recipes.filter((recipe) => recipe.delete !== true);

    const toDelete = recipes.filter((recipe) => recipe.delete);

    if (toCreateOrUpdate.length !== 0) {
      const TO_CREATE_OR_UPDATE_SQL = `
        INSERT INTO meal_has_recipe (meal_id, recipe_id, consumed_servings)
        VALUES ${Array(toCreateOrUpdate.length).fill('(?, ?, ?)').join(',')}
        ON DUPLICATE KEY
          UPDATE consumed_servings = VALUES(consumed_servings);
      `;

      const query = transaction.query(
        TO_CREATE_OR_UPDATE_SQL,
        toCreateOrUpdate.flatMap((recipe) => [
          recipeId,
          recipe.id,
          recipe.consumedServings,
        ]),
      );

      queries.push(query);
    }

    if (toDelete.length !== 0) {
      const TO_DELETE_SQL = `
        DELETE FROM meal_has_recipe WHERE meal_id = ?
          AND recipe_id IN (${Array(toDelete.length).fill('?').join(',')})
      `;

      const query = transaction.query(TO_DELETE_SQL, [
        recipeId,
        ...toDelete.map((recipe) => recipe.id),
      ]);

      queries.push(query);
    }

    return queries;
  }

  async updateMeal(
    params: SingleIdParamsDto,
    meal: UpdateMealDto,
    user: IUser,
  ): Promise<RetrieveMealResponseDto> {
    if (isEmpty(meal))
      throw new BadRequestException('Body não deve estar vazio');

    const { id } = params;

    const recipesToCreateOrUpdate =
      meal.recipes?.filter((recipe) => recipe.delete !== true) ?? [];

    if (recipesToCreateOrUpdate.length !== 0) {
      await this._checkIfRecipesToCreateWereNotDeleted(
        id,
        recipesToCreateOrUpdate,
      );
    }

    await this._checkIfUserOwnsMeal(id, user);

    await this._entityManager
      .transaction(async (transaction) => {
        const queries = [];

        if (meal.date || meal.mealTimeId) {
          const recipeFields = [
            meal.date ? 'date = ?' : null,
            meal.mealTimeId ? 'meal_time_id = ?' : null,
          ].filter((field) => !!field);

          const RECIPE_SQL = `
            UPDATE meal SET
              ${recipeFields.join(',')}
            WHERE id = ?
          `;

          const recipeParameters = [meal.date, meal.mealTimeId, id].filter(
            (parameter) => !!parameter,
          );

          const query = transaction.query(RECIPE_SQL, recipeParameters);

          queries.push(query);
        }

        if (meal.foods) {
          const _queries = this._updateFoods(transaction, id, meal.foods);

          queries.push(..._queries);
        }

        if (meal.recipes) {
          const _queries = this._updateRecipes(transaction, id, meal.recipes);

          queries.push(..._queries);
        }

        await Promise.all(queries);
      })
      .catch((error) => {
        if (error.message.includes('FK_MealHasRecipe_Recipe'))
          throw new ConflictException('Receita não existe');

        if (error.message.includes('FK_MealHasFood_Food'))
          throw new ConflictException('Alimento não existe');

        if (error.message.includes('FK_Meal_MealTime'))
          throw new ConflictException('Horário de refeição não existe');

        if (
          error.message.includes('UQ_Meal_Date_And_MealTimeId_Per_UserPersonId')
        ) {
          throw new ConflictException(
            'Refeição para o dia e horário especificado já existe',
          );
        }

        throw new BadGatewayException();
      });

    return this.retrieveMeal({ id }, user);
  }

  async deleteMeal(params: SingleIdParamsDto, user: IUser) {
    const { id } = params;

    const response = await this.retrieveMeal({ id }, user);

    await this._entityManager
      .transaction(async (transaction) => {
        const MEAL_CHILD_TABLES_SQL = `
          DELETE
            meal_has_food,
            meal_has_recipe
          FROM meal
          LEFT JOIN meal_has_food
            ON meal.id = meal_has_food.meal_id
          LEFT JOIN meal_has_recipe
            ON meal.id = meal_has_recipe.meal_id
          WHERE user_person_id = ?
            AND meal.id = ?
        `;

        await transaction.query(MEAL_CHILD_TABLES_SQL, [user.id, id]);

        const MEAL_SQL =
          'DELETE FROM meal WHERE user_person_id = ? AND meal.id = ?';

        await transaction.query(MEAL_SQL, [user.id, id]);
      })
      .catch(() => {
        throw new BadGatewayException();
      });

    return response;
  }
}
