package kr.lunaticbum.back.lun.controllers.view

import com.fasterxml.jackson.databind.ObjectMapper
import kotlinx.coroutines.reactive.awaitSingle
import kr.lunaticbum.back.lun.model.BookmarkImage
import kr.lunaticbum.back.lun.model.Comment
import kr.lunaticbum.back.lun.model.CommentResponse
import kr.lunaticbum.back.lun.model.ImageMetaService
import kr.lunaticbum.back.lun.model.ResultMV
import kr.lunaticbum.back.lun.model.VoteResponse
import kr.lunaticbum.back.lun.service.CommentService
import kr.lunaticbum.back.lun.service.WebBookmarkService
import kr.lunaticbum.back.lun.utils.PayloadDecoder
import org.springframework.data.domain.PageRequest
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.ResponseBody
import reactor.core.publisher.Mono

@Controller
@RequestMapping("/bookmarks")
class BookmarkController(
    private val bookmarkService: WebBookmarkService,
    private val imageMetaService: ImageMetaService,
    private val commentService: CommentService,
    private val objectMapper: ObjectMapper
) {

    @GetMapping
    suspend fun bookmarkListPage(
        @RequestParam(value = "page", defaultValue = "0") page: Int,
        @RequestParam(required = false) category: String?,
        @RequestParam(required = false) tag: String?,
        @AuthenticationPrincipal userDetails: UserDetails?
    ): ResultMV {
        val vm = ResultMV("content/bookmarks")
        val pageable = PageRequest.of(page, 9)

        val bookmarksPage = bookmarkService.getVisibleBookmarks(userDetails, pageable, category, tag).awaitSingle()

        val processedBookmarksPage = bookmarksPage.map { bookmark ->
            if (bookmark.images.isEmpty() && bookmark.contentUrls.isNotEmpty()) {
                bookmark.copy(
                    images = bookmark.contentUrls.map { url -> BookmarkImage(url = url, isVisible = true) }
                )
            } else {
                bookmark
            }
        }
        vm.modelMap["bookmarksPage"] = processedBookmarksPage
        vm.modelMap["allCategories"] = bookmarkService.findAllDistinctCategories().collectList().awaitSingle()
        vm.modelMap["allTags"] = bookmarkService.findAllDistinctTags().collectList().awaitSingle()
        vm.modelMap["currentCategory"] = category
        vm.modelMap["currentTag"] = tag

        vm.setTitle("저장된 페이지 목록")
        return vm
    }

    @PostMapping("/{bookmarkId}/like")
    @ResponseBody
    fun likeBookmark(@PathVariable bookmarkId: String): Mono<VoteResponse> {
        return bookmarkService.incrementVote(bookmarkId).map {
            VoteResponse(it.voteCount, it.unlikeCount)
        }
    }

    @PostMapping("/{bookmarkId}/unlike")
    @ResponseBody
    fun unlikeBookmark(@PathVariable bookmarkId: String): Mono<VoteResponse> {
        return bookmarkService.incrementUnlike(bookmarkId).map {
            VoteResponse(it.voteCount, it.unlikeCount)
        }
    }

    @GetMapping("/{bookmarkId}/comments")
    @ResponseBody
    fun getComments(@PathVariable bookmarkId: String): Mono<CommentResponse> {
        return commentService.getCommentsForPost(bookmarkId)
            .collectList()
            .map { comments -> CommentResponse(0, "Success", comments) }
    }

    @PostMapping("/{bookmarkId}/comments")
    @ResponseBody
    fun addComment(
        @PathVariable bookmarkId: String,
        @RequestBody rawPayload: String,
        @AuthenticationPrincipal user: UserDetails?
    ): Mono<CommentResponse> {
        val comment = PayloadDecoder.decode(rawPayload, Comment::class.java, objectMapper)
        comment.postId = bookmarkId
        comment.writer = user?.username ?: "Anonymous"
        comment.writeTime = System.currentTimeMillis()

        return commentService.addComment(comment)
            .map { CommentResponse(0, "Success") }
    }
}