package app.mealsmadeeasy.api.recipe.comment;

import app.mealsmadeeasy.api.markdown.MarkdownService;
import app.mealsmadeeasy.api.recipe.Recipe;
import app.mealsmadeeasy.api.recipe.RecipeRepository;
import app.mealsmadeeasy.api.user.User;
import app.mealsmadeeasy.api.util.NoSuchEntityWithIdException;
import app.mealsmadeeasy.api.util.NoSuchEntityWithUsernameAndSlugException;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;

import java.time.OffsetDateTime;

import static java.util.Objects.requireNonNull;

@Service
public class RecipeCommentServiceImpl implements RecipeCommentService {

    private final RecipeCommentRepository recipeCommentRepository;
    private final RecipeRepository recipeRepository;
    private final MarkdownService markdownService;

    public RecipeCommentServiceImpl(
            RecipeCommentRepository recipeCommentRepository,
            RecipeRepository recipeRepository,
            MarkdownService markdownService
    ) {
        this.recipeCommentRepository = recipeCommentRepository;
        this.recipeRepository = recipeRepository;
        this.markdownService = markdownService;
    }

    @Override
    @PreAuthorize("@recipeSecurity.isViewableBy(#recipeUsername, #recipeSlug, #commenter)")
    public RecipeComment create(
            String recipeUsername,
            String recipeSlug,
            User commenter,
            RecipeCommentCreateBody body
    ) {
        requireNonNull(commenter);
        final RecipeComment draft = new RecipeComment();
        draft.setCreated(OffsetDateTime.now());
        draft.setRawText(body.getText());
        draft.setCachedRenderedText(this.markdownService.renderAndCleanMarkdown(body.getText()));
        draft.setOwner(commenter);
        final Recipe recipe = this.recipeRepository.findByOwnerUsernameAndSlug(recipeUsername, recipeSlug)
                .orElseThrow(
                        () -> new NoSuchEntityWithUsernameAndSlugException(Recipe.class, recipeUsername, recipeSlug)
                );
        draft.setRecipe(recipe);
        return this.recipeCommentRepository.save(draft);
    }

    @PostAuthorize("@recipeSecurity.isViewableBy(returnObject.recipe, #viewer)")
    private RecipeComment loadCommentEntity(Integer commentId, User viewer) {
        return this.recipeCommentRepository.findById(commentId).orElseThrow(
                () -> new NoSuchEntityWithIdException(RecipeComment.class, commentId)
        );
    }

    @Override
    public RecipeComment get(Integer commentId, User viewer) {
        return this.loadCommentEntity(commentId, viewer);
    }

    @Override
    @PreAuthorize("@recipeSecurity.isViewableBy(#recipeUsername, #recipeSlug, #viewer)")
    public Slice<RecipeCommentView> getComments(
            String recipeUsername,
            String recipeSlug,
            Pageable pageable,
            User viewer
    ) {
        final Recipe recipe = this.recipeRepository.findByOwnerUsernameAndSlug(recipeUsername, recipeSlug).orElseThrow(
                () -> new NoSuchEntityWithUsernameAndSlugException(Recipe.class, recipeUsername, recipeSlug)
        );
        final Slice<RecipeComment> commentEntities = this.recipeCommentRepository.findAllByRecipe(recipe, pageable);
        return commentEntities.map(commentEntity -> RecipeCommentView.from(
                commentEntity,
                false
        ));
    }

    @Override
    public RecipeComment update(Integer commentId, User viewer, RecipeCommentUpdateSpec spec) {
        final RecipeComment entity = this.loadCommentEntity(commentId, viewer);
        entity.setRawText(spec.getRawText());
        return this.recipeCommentRepository.save(entity);
    }

    @PostAuthorize("@recipeSecurity.isOwner(returnObject.recipe, #modifier)")
    private RecipeComment loadForDelete(Integer commentId, User modifier) {
        return this.recipeCommentRepository.findById(commentId).orElseThrow(() ->
                new NoSuchEntityWithIdException(RecipeComment.class, commentId)
        );
    }

    @Override
    public void delete(Integer commentId, User modifier) {
        final RecipeComment entityToDelete = this.loadForDelete(commentId, modifier);
        this.recipeCommentRepository.delete(entityToDelete);
    }

    @Override
    @PreAuthorize("@recipeSecurity.isViewableBy(#recipeUsername, #recipeSlug, #viewer)")
    public int countComments(String recipeUsername, String recipeSlug, User viewer) {
        return this.recipeCommentRepository.countByUsernameAndSlug(recipeUsername, recipeSlug);
    }

}
