content: {-# LANGUAGE DataKinds #-}
  {-# LANGUAGE DeriveAnyClass #-}
  {-# LANGUAGE FlexibleContexts #-}
  {-# LANGUAGE TemplateHaskell #-}
  {-# LANGUAGE TypeOperators #-}
  
  module RealWorld.Infra.Web.Handler.Article.Unfavorite where
  
  import Data.Aeson (ToJSON)
  import Data.Aeson.Types (FromJSON)
  import Data.Swagger (ToSchema)
  import Effectful (Eff)
  import qualified Effectful as Eff
  import Effectful.Error.Dynamic (Error, throwError)
  import Effectful.Katip
  import RealWorld.Domain.Adapter.Manager.TxManager (TxManager)
  import RealWorld.Domain.Adapter.Repository.ArticleRepository (ArticleRepository)
  import RealWorld.Domain.Adapter.Repository.FavoriteRepository (FavoriteRepository)
  import RealWorld.Domain.Adapter.Repository.UserRepository (UserRepository)
  import RealWorld.Domain.Command.Article.UseCase (UnfavoriteArticleError (..))
  import qualified RealWorld.Domain.Command.Article.UseCase as ArticleUseCase
  import RealWorld.Domain.Query.Data (Article (..), Profile)
  import qualified RealWorld.Domain.Query.Data as Query
  import RealWorld.Domain.Query.QueryService (QueryService)
  import qualified RealWorld.Domain.Query.QueryService as QueryService
  import RealWorld.Infra.Web.Auth (ApiAuth (..))
  import RealWorld.Infra.Web.ErrorResponse (badRequest, notFound')
  import RealWorld.Infra.Web.Schema ()
  import Relude
  import Servant (Capture, Delete, JSON, ServerError, (:>))
  
  type Route =
    "articles"
      :> Capture "slug" Text
      :> "favorite"
      :> Delete '[JSON] UnfavoriteArticleResponse
  
  data UnfavoriteArticleResponse = UnfavoriteArticleResponse
    { article :: Article
    }
    deriving stock (Show, Generic)
    deriving anyclass (FromJSON, ToJSON, ToSchema)
  
  toError :: ArticleUseCase.UnfavoriteArticleError -> ServerError
  toError UnfavoriteArticleErrorInvalidUserId = badRequest "Invalid User Id"
  toError UnfavoriteArticleErrorInvalidSlug = badRequest "Invalid slug"
  toError UnfavoriteArticleErrorArticleNotFound = notFound' "Article not found"
  toError UnfavroiteArticleErrorUserNotFound = notFound' "User not found"
  toError UnfavroiteArticleErrorIsNotFavorited = badRequest "Is not favorited"
  toError UnfavoriteArticleErrorNotFavorited = badRequest "Not favorited"
  
  handler ::
    ( KatipE Eff.:> es
    , ArticleRepository Eff.:> es
    , FavoriteRepository Eff.:> es
    , UserRepository Eff.:> es
    , TxManager Eff.:> es
    , QueryService Eff.:> es
    , Error ServerError Eff.:> es
    ) =>
    ApiAuth ->
    Text ->
    Eff es UnfavoriteArticleResponse
  handler (ApiAuth userId _) slug = do
    result <- ArticleUseCase.unfavoriteArticle toCommand
    case result of
      Right result' -> do
        let params = Query.GetProfileParams Nothing result'.authorUsername
        profile <- QueryService.getProfile params `whenNothingM` throwError (notFound' "Author not found")
        pure $ UnfavoriteArticleResponse $ toArticle result' profile
      Left err -> do
        katipAddContext (sl "error" err) $ do
          $(logTM) ErrorS "unfavorite error"
        throwError $ toError err
   where
    toCommand :: ArticleUseCase.UnfavoriteArticleCommand
    toCommand =
      ArticleUseCase.UnfavoriteArticleCommand
        { userId = userId
        , slug = slug
        }
    toArticle :: ArticleUseCase.UnfavoriteArticleResult -> Profile -> Article
    toArticle result author =
      Article
        { slug = result.slug
        , title = result.title
        , description = result.description
        , body = result.body
        , tagList = result.tags
        , createdAt = result.createdAt
        , updatedAt = result.updatedAt
        , favorited = False
        , favoritesCount = result.favoritesCount
        , author = author
        }