UnfavoriteUseCase.kt

package com.example.realworldkotlinspringbootjdbc.usecase.favorite

import arrow.core.Either
import arrow.core.Either.Left
import arrow.core.Either.Right
import arrow.core.getOrHandle
import arrow.core.left
import arrow.core.right
import arrow.core.toOption
import com.example.realworldkotlinspringbootjdbc.domain.ArticleRepository
import com.example.realworldkotlinspringbootjdbc.domain.ProfileRepository
import com.example.realworldkotlinspringbootjdbc.domain.RegisteredUser
import com.example.realworldkotlinspringbootjdbc.domain.article.Slug
import com.example.realworldkotlinspringbootjdbc.usecase.shared_model.CreatedArticleWithAuthor
import com.example.realworldkotlinspringbootjdbc.util.MyError
import org.springframework.stereotype.Service

interface UnfavoriteUseCase {
    fun execute(slug: String?, currentUser: RegisteredUser): Either<Error, CreatedArticleWithAuthor> =
        throw NotImplementedError()

    sealed interface Error : MyError {
        data class InvalidSlug(override val errors: List<MyError.ValidationError>) : Error, MyError.ValidationErrors
        data class NotFound(override val cause: MyError) :
            Error,
            MyError.MyErrorWithMyError
    }
}

@Service
class UnfavoriteUseCaseImpl(
    val articleRepository: ArticleRepository,
    val profileRepository: ProfileRepository
) : UnfavoriteUseCase {
    override fun execute(
        slug: String?,
        currentUser: RegisteredUser
    ): Either<UnfavoriteUseCase.Error, CreatedArticleWithAuthor> {
        /**
         * Slug のバリデーション
         * Invalid -> 早期 return
         */
        val validatedSlug = Slug.new(slug).fold(
            { return UnfavoriteUseCase.Error.InvalidSlug(it).left() },
            { it }
        )

        /**
         * お気に入り登録解除
         */
        val unfavoriteResult =
            when (val unfavoriteResult = articleRepository.unfavorite(validatedSlug, currentUser.userId)) {
                /**
                 * お気に入り登録解除 失敗
                 */
                is Left -> return when (val error = unfavoriteResult.value) {
                    /**
                     * 原因: 作成済記事が見つからなかった
                     */
                    is ArticleRepository.UnfavoriteError.NotFoundCreatedArticleBySlug -> UnfavoriteUseCase.Error.NotFound(
                        error
                    ).left()
                }
                /**
                 * お気に入り登録解除 成功
                 */
                is Right -> unfavoriteResult
            }

        /**
         * author を取得
         * 必ず 1 件見つかるため、first を指定している
         */
        val author =
            profileRepository.filterByUserIds(setOf(unfavoriteResult.value.authorId), currentUser.userId.toOption())
                .getOrHandle { throw UnsupportedOperationException("現在この分岐に入ることは無い") }.first()

        /**
         * author を followedUsers から取得
         */
        return CreatedArticleWithAuthor(
            article = unfavoriteResult.value,
            author = author
        ).right()
    }
}