-- | Edit a comment's text. If the comment was flagged, unflag it and send a
-- notification to the flagger.
editCommentDB :: UserId -> CommentId -> Markdown -> Language
              -> SYDB (Either Text ())
editCommentDB user_id comment_id text language =
    updateComment >>= \case
        Left err -> return $ Left err
        Right () -> do
            lift (fetchCommentFlaggingDB comment_id) >>= \case
                Nothing -> return ()
                Just (Entity comment_flagging_id CommentFlagging{..}) -> do
                    langs <- lift $ lift getLanguages
                    render <- getUrlRender
                    rendered_route <-
                        lift $
                            makeCommentRouteDB langs comment_id
                            >>= return . render . fromJust
                    let notif_text = Markdown $
                            "A comment you flagged has been edited and reposted to "
                            <> "the site. You can view it [here]("
                            <> rendered_route <> ")."
                    -- delete flagging and all flagging reasons with it.
                    lift $ deleteCascade comment_flagging_id
                    sendPreferredUserNotificationDB
                        (Just $ NotificationSender user_id)
                        (NotificationReceiver commentFlaggingFlagger)
                        NotifFlagRepost
                        Nothing
                        notif_text
            return $ Right ()
  where
    updateComment = do
        mexistent_ticket <- lift $ getBy $ UniqueTicket comment_id
        existent_tags    <- lift $ fetchTagNamesDB comment_id
        let content_with_tags    = unMarkdown text
            content_without_tags = Markdown $ stripTags content_with_tags
            content_tags         = parseTags    content_with_tags
            new_tags             = content_tags \\ existent_tags
        case parseTicket content_with_tags of
            Left err -> return $ Left err
            Right mnew_ticket-> do
                lift $ do
                    now <- liftIO getCurrentTime
                    insertTagsDB user_id comment_id new_tags

                    case (mexistent_ticket, mnew_ticket) of
                        (Just existing_ticket, Just new_ticket) ->
                            update $ \t -> do
                                set t [ TicketUpdatedTs =. val now
                                      , TicketName      =. val (unTicketName new_ticket) ]
                                where_ $ t ^. TicketId ==. val (entityKey existing_ticket)
                        (Nothing, Just new_ticket) ->
                            insert_ $ Ticket now now (unTicketName new_ticket) comment_id
                        (Just _,  Nothing) -> deleteBy $ UniqueTicket comment_id
                        (Nothing, Nothing) -> return ()

                    update $ \c -> do
                    set c [ CommentText     =. val content_without_tags
                          , CommentLanguage =. val language
                          ]
                    where_ (c ^. CommentId ==. val comment_id)

                return $ Right ()

-- | Flag a comment. Send a notification to the poster about the flagging.
-- Return whether or not the flag was successful (fails if the comment was
-- already flagged.)
editCommentDB :: UserId -> CommentId -> Markdown -> Language
              -> SYDB (Either Text ())
editCommentDB user_id comment_id text language =
    updateComment >>= \case
        Left err -> return $ Left err
        Right () -> do
            lift (fetchCommentFlaggingDB comment_id) >>= \case
                Nothing -> return ()
                Just (Entity comment_flagging_id CommentFlagging{..}) -> do
                    langs <- lift $ lift getLanguages
                    render <- getUrlRender
                    rendered_route <-
                        lift $
                            makeCommentRouteDB langs comment_id
                            >>= return . render . fromJust
                    let notif_text = Markdown $
                            "A comment you flagged has been edited and reposted to "
                            <> "the site. You can view it [here]("
                            <> rendered_route <> ")."
                    -- delete flagging and all flagging reasons with it.
                    lift $ deleteCascade comment_flagging_id
                    sendPreferredUserNotificationDB
                        (Just $ NotificationSender user_id)
                        (NotificationReceiver commentFlaggingFlagger)
                        NotifFlagRepost
                        Nothing
                        notif_text
            return $ Right ()
  where
    updateComment = do
        mexistent_ticket <- lift $ getBy $ UniqueTicket comment_id
        existent_tags    <- lift $ fetchTagNamesDB comment_id
        let content_with_tags    = unMarkdown text
            content_without_tags = Markdown $ stripTags content_with_tags
            content_tags         = parseTags    content_with_tags
            new_tags             = content_tags \\ existent_tags
        case parseTicket content_with_tags of
            Left err -> return $ Left err
            Right mnew_ticket-> do
                lift $ do
                    now <- liftIO getCurrentTime
                    insertTagsDB user_id comment_id new_tags

                    case (mexistent_ticket, mnew_ticket) of
                        (Just existing_ticket, Just new_ticket) ->
                            update $ \t -> do
                                set t [ TicketUpdatedTs =. val now
                                      , TicketName      =. val (unTicketName new_ticket) ]
                                where_ $ t ^. TicketId ==. val (entityKey existing_ticket)
                        (Nothing, Just new_ticket) ->
                            insert_ $ Ticket now now (unTicketName new_ticket) comment_id
                        (Just _,  Nothing) -> deleteBy $ UniqueTicket comment_id
                        (Nothing, Nothing) -> return ()

                    update $ \c -> do
                    set c [ CommentText     =. val content_without_tags
                          , CommentLanguage =. val language
                          ]
                    where_ (c ^. CommentId ==. val comment_id)

                return $ Right ()

-- | Flag a comment. Send a notification to the poster about the flagging.
-- Return whether or not the flag was successful (fails if the comment was
-- already flagged.)
editCommentDB user_id comment_id text language =
    updateComment >>= \case
        Left err -> return $ Left err
        Right () -> do
            lift (fetchCommentFlaggingDB comment_id) >>= \case
                Nothing -> return ()
                Just (Entity comment_flagging_id CommentFlagging{..}) -> do
                    langs <- lift $ lift getLanguages
                    render <- getUrlRender
                    rendered_route <-
                        lift $
                            makeCommentRouteDB langs comment_id
                            >>= return . render . fromJust
                    let notif_text = Markdown $
                            "A comment you flagged has been edited and reposted to "
                            <> "the site. You can view it [here]("
                            <> rendered_route <> ")."
                    -- delete flagging and all flagging reasons with it.
                    lift $ deleteCascade comment_flagging_id
                    sendPreferredUserNotificationDB
                        (Just $ NotificationSender user_id)
                        (NotificationReceiver commentFlaggingFlagger)
                        NotifFlagRepost
                        Nothing
                        notif_text
            return $ Right ()
  where
    updateComment = do
        mexistent_ticket <- lift $ getBy $ UniqueTicket comment_id
        existent_tags    <- lift $ fetchTagNamesDB comment_id
        let content_with_tags    = unMarkdown text
            content_without_tags = Markdown $ stripTags content_with_tags
            content_tags         = parseTags    content_with_tags
            new_tags             = content_tags \\ existent_tags
        case parseTicket content_with_tags of
            Left err -> return $ Left err
            Right mnew_ticket-> do
                lift $ do
                    now <- liftIO getCurrentTime
                    insertTagsDB user_id comment_id new_tags

                    case (mexistent_ticket, mnew_ticket) of
                        (Just existing_ticket, Just new_ticket) ->
                            update $ \t -> do
                                set t [ TicketUpdatedTs =. val now
                                      , TicketName      =. val (unTicketName new_ticket) ]
                                where_ $ t ^. TicketId ==. val (entityKey existing_ticket)
                        (Nothing, Just new_ticket) ->
                            insert_ $ Ticket now now (unTicketName new_ticket) comment_id
                        (Just _,  Nothing) -> deleteBy $ UniqueTicket comment_id
                        (Nothing, Nothing) -> return ()

                    update $ \c -> do
                    set c [ CommentText     =. val content_without_tags
                          , CommentLanguage =. val language
                          ]
                    where_ (c ^. CommentId ==. val comment_id)

                return $ Right ()

-- | Flag a comment. Send a notification to the poster about the flagging.
-- Return whether or not the flag was successful (fails if the comment was
-- already flagged.)