import { Component } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { Store } from '@ngrx/store';
import { LexoRank } from 'lexorank';
import { Observable, forkJoin, tap } from 'rxjs';
import { UserStore } from 'src/app/core/interfaces/user.interface';
import { LoaderDispatchers, ToastDispatchers } from 'src/app/core/stores/index.dispatchers';
import { FoodCardProps } from 'src/app/shared/interfaces/components/cards/food-card/food-card.interface';
import {
  EditIngredient,
  EditRecipe,
  EditStepByStep,
} from 'src/app/shared/interfaces/services/api-fit/request/recipe.interface';
import { Ingredient } from 'src/app/shared/interfaces/services/api-fit/response/ingredient.interface';
import {
  Recipe,
  RecipeTag,
} from 'src/app/shared/interfaces/services/api-fit/response/recipe.interface';
import { ApiFitService } from 'src/app/shared/services/api-fit/api-fit.service';

@Component({
  selector: 'app-edit',
  templateUrl: './edit.component.html',
  styleUrls: ['./edit.component.scss'],
})
export class EditComponent {
  originalRecipe: Recipe = {
    id: 0,
    name: '',
    creator: '',
    pictureUrl: '',
    isLiked: false,
    servings: 1,
    kcal: 0,
    likes: 0,
    ingredients: [],
    stepBySteps: [],
    tags: [],
  };

  editedRecipe: Recipe = {
    id: 0,
    name: '',
    creator: '',
    pictureUrl: '',
    isLiked: false,
    servings: 1,
    kcal: 0,
    likes: 0,
    ingredients: [],
    stepBySteps: [],
    tags: [],
  };

  recipePicture: File | null = null;

  selectedTags: RecipeTag[] = [];
  selectedIngredients: FoodCardProps[] = [];

  isModalAddIngredientOpen = false;
  isModalAddTagOpen = false;
  isModalNutritionalInformationOpen = false;

  ingredientToEdit: FoodCardProps | null = null;

  recipeIngredientsInformation: Ingredient[] = [];

  validationList = {
    title: {
      regex: [/^[a-zà-ú- ]+$/i],
      message: 'O nome da receita deve conter apenas letras',
      hasError: false
    }
  };

  constructor(
    private apiFitService: ApiFitService,
    private toastDispatchers: ToastDispatchers,
    private router: Router,
    private route: ActivatedRoute,
    private store: Store<{ user: UserStore }>,
    private loaderDispatchers: LoaderDispatchers
  ) {}

  async ngOnInit() {
    try {
      const recipeId = await this.getRecipeIdFromParam();

      this.originalRecipe = await this.fetchOriginalRecipe(recipeId);
      this.editedRecipe = structuredClone(this.originalRecipe);

      const isAuthorized = await this.verifyAuthorization();
      if (!isAuthorized) this.router.navigate(['/app/foods']);

      this.selectedIngredients = await this.fetchFoodCards();
      this.selectedTags = this.editedRecipe.tags ?? [];
    } catch (err) {
      this.router.navigate(['/app/foods']);
    }
  }

  verifyAuthorization() {
    return new Promise(
    (
      resolve: (value: unknown) => void,
      reject: (value: unknown) => void
    ) => {
      this.store.select('user')
      .subscribe({
        next: d => d.username === this.originalRecipe.creator ? resolve(true) : reject(false),
        error: reject
      });
    });
  }

  getRecipeIdFromParam() {
    return new Promise(
      (resolve: (recipeId: number) => void, reject: (err: unknown) => void) => {
        this.route.params.subscribe({
          next: (p) => {
            if (!p['id']) reject('ID not found');
            resolve(p['id']);
          },
          error: reject,
        });
      },
    );
  }

  fetchOriginalRecipe(id: number) {
    return new Promise(
      (
        resolve: (recipe: Recipe) => void,
        reject: (reason: unknown) => void,
      ) => {
        this.apiFitService.getRecipeById(id).subscribe({
          next: (r) => resolve(r.data),
          error: reject,
        });
      },
    );
  }

  fetchFoodCards() {
    return new Promise(
      (
        resolve: (foodCards: FoodCardProps[]) => void,
        reject: (reason: unknown) => void,
      ) => {
        const ingredientsRequests = this.originalRecipe.ingredients.map(
          (ingredient) => {
            return this.apiFitService.getFoodById(ingredient.id);
          },
        );

        forkJoin(ingredientsRequests).subscribe({
          next: (res) => {
            const ingredientsInformation = res.map((r) => r.data);

            const cards = ingredientsInformation.map((i) => {
              const ingredient = this.originalRecipe.ingredients.find(
                (ingredient) => ingredient.id === i.id,
              ) as Recipe['ingredients'][0];

              return {
                id: i.id,
                name: i.name,
                quantity: ingredient.quantity,
                kcal: (i.kcalPer100 * 100) / ingredient.quantity,
                type: 'food',
              } as FoodCardProps;
            });

            resolve(cards);
          },
          error: reject,
        });
      },
    );
  }

  setRecipeStep(index: number, value: string) {
    const newValue = value.trim();

    if (!newValue) {
      if (index === this.editedRecipe.stepBySteps.length - 1) return;

      this.editedRecipe.stepBySteps.splice(index, 1);
      return;
    }

    this.editedRecipe.stepBySteps[index].description = newValue;

    if (index === this.editedRecipe.stepBySteps.length - 1) {
      setTimeout(() => {
        this.editedRecipe.stepBySteps.push({
          description: '',
          lexoRank: '',
        });
      }, 250);
    }
  }

  openModalAddTag() {
    this.isModalAddTagOpen = true;
  }

  openModalAddIngredient() {
    this.isModalAddIngredientOpen = true;
  }

  handleAddIngredient(ingredient: FoodCardProps) {
    const index = this.selectedIngredients.findIndex(
      (item) => item.id === ingredient.id,
    );

    if (index !== -1) {
      this.editedRecipe.ingredients.splice(index, 1);
      this.selectedIngredients.splice(index, 1);
    }

    const newIngredient = { ...ingredient } as FoodCardProps;

    this.editedRecipe.ingredients.push(newIngredient);
    this.selectedIngredients.push(newIngredient);
    this.ingredientToEdit = null;
  }

  handleEditIngredient(ingredient: FoodCardProps) {
    this.ingredientToEdit = ingredient;
    this.isModalAddIngredientOpen = true;
  }

  handleRemoveIngredient(ingredient: FoodCardProps) {
    const index = this.selectedIngredients.findIndex(
      (item) => item.id === ingredient.id,
    );

    if (index === -1) return;

    this.editedRecipe.ingredients.splice(index, 1);
    this.selectedIngredients.splice(index, 1);
  }

  handleSelectedTags(tags: RecipeTag[]) {
    this.selectedTags = [...tags];
  }

  removeSelectedTag(tag: RecipeTag) {
    const index = this.selectedTags.findIndex((item) => item.id === tag.id);

    if (index === -1) return;

    this.selectedTags.splice(index, 1);
  }

  updateIngredient() {
    if (!this.ingredientToEdit) return;

    const index = this.selectedIngredients.findIndex(
      (item) => item.id === this.ingredientToEdit?.id,
    );

    if (index === -1) return;

    this.editedRecipe.ingredients[index] = { ...this.ingredientToEdit };
    this.selectedIngredients[index] = { ...this.ingredientToEdit };

    this.ingredientToEdit = null;
    this.isModalAddIngredientOpen = false;
  }

  isModalNutritionalInformationEnabled() {
    return this.editedRecipe.ingredients.length > 0;
  }

  openModalNutritionalInformation() {
    this.isModalNutritionalInformationOpen = true;

    const ingredientsRequests = this.editedRecipe.ingredients.map(
      (ingredient) => {
        return this.apiFitService.getFoodById(ingredient.id);
      },
    );

    forkJoin(ingredientsRequests).subscribe({
      next: (res) => {
        const ingredientsInformation = res.map((r) => r.data);

        this.recipeIngredientsInformation = ingredientsInformation.map((i) => {
          const quantity = this.editedRecipe.ingredients.find(
            (ingredient) => ingredient.id === i.id,
          )?.quantity;

          return {
            ...i,
            quantity: quantity ?? 0,
          };
        });
      },
    });
  }

  isRecipeValid() {
    const hasSomeValidationError = Object.values(this.validationList).some(val => val.hasError);

    return (
      this.editedRecipe.name.trim() !== '' &&
      this.editedRecipe.ingredients.length > 0 &&
      this.editedRecipe.stepBySteps.filter((s) => s.description.trim()).length >
        0 &&
      this.editedRecipe.servings > 0 &&
      !hasSomeValidationError
    );
  }

  saveRecipePicture(recipeId: number) {
    return new Promise(
      (resolve: (data: unknown) => void, reject: (reason: unknown) => void) => {
        if (!this.recipePicture) {
          resolve(null);
          return;
        }

        this.apiFitService
          .saveRecipePicture({
            picture: this.recipePicture,
            recipeId: recipeId,
          })
          .subscribe({
            next: resolve,
            error: reject,
          });
      },
    );
  }

  editRecipe() {
    const payload: EditRecipe = {};

    if (this.editedRecipe.name !== this.originalRecipe.name) {
      payload.name = this.editedRecipe.name;
    }

    if (this.editedRecipe.servings !== this.originalRecipe.servings) {
      payload.servings = this.editedRecipe.servings;
    }

    const addedIngredients: EditIngredient[] =
      this.editedRecipe.ingredients.filter(
        (i) => !this.originalRecipe.ingredients.find((ing) => ing.id === i.id),
      );

    const editedIngredients: EditIngredient[] =
      this.editedRecipe.ingredients.filter((i) =>
        this.originalRecipe.ingredients.find(
          (ing) => ing.id === i.id && ing.quantity !== i.quantity,
        ),
      );

    const deletedIngredients: EditIngredient[] = this.originalRecipe.ingredients
      .filter(
        (i) => !this.editedRecipe.ingredients.find((ing) => ing.id === i.id),
      )
      .map((i) => {
        return {
          ...i,
          delete: true,
        };
      });

    if (
      addedIngredients.length ||
      editedIngredients.length ||
      deletedIngredients.length
    ) {
      payload.ingredients = [
        ...addedIngredients,
        ...editedIngredients,
        ...deletedIngredients,
      ];
    }

    const lastLexoRank = LexoRank.parse(
      this.originalRecipe.stepBySteps.at(-1)?.lexoRank ?? '',
    );
    let lexoRank = LexoRank.from(
      lastLexoRank.getBucket(),
      lastLexoRank.getDecimal(),
    ).genNext();

    const steps = [
      ...this.originalRecipe.stepBySteps.map((s) => {
        return {
          ...s,
          delete: true,
        } as EditStepByStep;
      }),
      ...this.editedRecipe.stepBySteps
        .filter((s) => s.description.trim())
        .map((s) => {
          lexoRank = lexoRank.genNext();

          return {
            ...s,
            lexoRank: lexoRank.toString(),
          };
        }),
    ];
    payload.stepBySteps = steps;
    payload.tagsIds = this.selectedTags.map((t) => t.id);

    const requests: Observable<unknown>[] = [
      this.apiFitService.editRecipe(this.originalRecipe.id, payload),
    ];

    if (this.recipePicture) {
      requests.push(
        this.apiFitService.saveRecipePicture({
          recipeId: this.originalRecipe.id,
          picture: this.recipePicture,
        }),
      );
    }

    forkJoin(requests)
    .pipe(
      tap({
        subscribe: () => this.loaderDispatchers.showLoader()
      })
    )
    .subscribe({
      next: () => {
        this.toastDispatchers.showSuccess({
          isVisible: true,
          delay: 3000,
          message: 'Receita editada com sucesso',
          shouldHideAutomatically: true,
          showCloseButton: true,
        });

        setTimeout(() => {
          this.router.navigate(['/app/foods', this.originalRecipe.id]);
          this.loaderDispatchers.hideLoader();
        }, 1025);
      },
      error: () => {
        this.toastDispatchers.showError({
          isVisible: true,
          delay: 3000,
          message: 'Ocorreu um erro ao editar a receita',
          shouldHideAutomatically: true,
          showCloseButton: true,
        });
        this.loaderDispatchers.hideLoader();
      },
    });
  }
}
