package app.mealsmadeeasy.api.recipe;

import app.mealsmadeeasy.api.user.User;
import app.mealsmadeeasy.api.util.NoSuchEntityWithIdException;
import app.mealsmadeeasy.api.util.NoSuchEntityWithUsernameAndSlugException;
import org.jetbrains.annotations.Nullable;
import org.springframework.stereotype.Component;

import java.util.Objects;

@Component
public class RecipeSecurity {

    private final RecipeRepository recipeRepository;

    public RecipeSecurity(RecipeRepository recipeRepository) {
        this.recipeRepository = recipeRepository;
    }

    public boolean isOwner(Recipe recipe, @Nullable User user) {
        if (user == null) return false;
        return recipe.getOwner() != null && recipe.getOwner().getId().equals(user.getId());
    }

    public boolean isOwner(Integer recipeId, @Nullable User user) {
        final Recipe recipe = this.recipeRepository.findById(recipeId).orElseThrow(
                () -> new NoSuchEntityWithIdException(Recipe.class, recipeId)
        );
        return this.isOwner(recipe, user);
    }

    public boolean isOwner(String username, String slug, @Nullable User user) {
        final Recipe recipe = this.recipeRepository.findByOwnerUsernameAndSlug(username, slug).orElseThrow(
                () -> new NoSuchEntityWithUsernameAndSlugException(Recipe.class, username, slug)
        );
        return this.isOwner(recipe, user);
    }

    public boolean isViewableBy(Recipe recipe, @Nullable User user) {
        if (recipe.getIsPublic()) {
            // public recipe
            return true;
        } else if (user == null) {
            // a non-public recipe with no principal
            return false;
        } else if (Objects.equals(recipe.getOwner().getId(), user.getId())) {
            // is owner
            return true;
        } else {
            // check if viewer
            final Recipe withViewers = this.recipeRepository.findByIdWithViewers(recipe.getId())
                    .orElseThrow(() -> new NoSuchEntityWithIdException(Recipe.class, recipe.getId()));
            for (final User viewer : withViewers.getViewers()) {
                if (viewer.getId() != null && viewer.getId().equals(user.getId())) {
                    return true;
                }
            }
        }
        // non-public recipe and not viewer
        return false;
    }

    public boolean isViewableBy(String ownerUsername, String slug, @Nullable User user) {
        final Recipe recipe = this.recipeRepository.findByOwnerUsernameAndSlug(ownerUsername, slug)
                .orElseThrow(() -> new NoSuchEntityWithUsernameAndSlugException(Recipe.class, ownerUsername, slug));
        return this.isViewableBy(recipe, user);
    }

    public boolean isViewableBy(Integer recipeId, @Nullable User user) {
        final Recipe recipe = this.recipeRepository.findById(recipeId).orElseThrow(
                () -> new NoSuchEntityWithIdException(Recipe.class, recipeId)
        );
        return this.isViewableBy(recipe, user);
    }

}
