package kr.lunaticbum.back.lun.service

import kr.lunaticbum.back.lun.model.Visibility
import kr.lunaticbum.back.lun.model.WebBookmark
import kr.lunaticbum.back.lun.repository.WebBookmarkRepository
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageImpl
import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Sort
import org.springframework.data.mongodb.core.FindAndModifyOptions
import org.springframework.data.mongodb.core.ReactiveMongoTemplate
import org.springframework.data.mongodb.core.query.Criteria
import org.springframework.data.mongodb.core.query.Query
import org.springframework.data.mongodb.core.query.Update
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.stereotype.Service
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono

@Service
class WebBookmarkService(private val repository: WebBookmarkRepository,
                         private val reactiveMongoTemplate: ReactiveMongoTemplate
    // [수정] 생성자에 ReactiveMongoTemplate를 추가하여 스프링이 주입하도록 합니다.
) {



    // [이 메소드를 추가하세요]
    fun findById(id: String): Mono<WebBookmark> {
        return repository.findById(id)
    }
    // [수정] map 대신 flatMap과 Mono.justOrEmpty를 사용하여 NullPointerException 방지
    fun findAllDistinctCategories(): Flux<String> {
        return repository.findDistinctCategories()
            .flatMap { map -> Mono.justOrEmpty(map["_id"]?.toString()) }
            .filter { it.isNotBlank() }
    }

    // [수정] map 대신 flatMap과 Mono.justOrEmpty를 사용하여 NullPointerException 방지
    fun findAllDistinctTags(): Flux<String> {
        return repository.findDistinctTags()
            .flatMap { map -> Mono.justOrEmpty(map["_id"]?.toString()) }
            .filter { it.isNotBlank() }
    }


    fun getBookmarksForUser(userId: String): Flux<WebBookmark> {
        return repository.findByUserIdOrderBySavedAtDesc(userId)
    }

    fun saveBookmark(bookmark: WebBookmark): Mono<WebBookmark> {
        // 여기에 중복 저장 방지 로직 등을 추가할 수 있음
        return repository.save(bookmark)
    }

    // 필요하다면 삭제, 수정 기능 추가
    fun deleteBookmark(id: String): Mono<Void> {
        return repository.deleteById(id)
    }

    // [수정] getVisibleBookmarks 메소드에 필터링 기능 추가
    fun getVisibleBookmarks(
        userDetails: UserDetails?,
        pageable: Pageable,
        category: String?, // 카테고리 파라미터 추가
        tag: String?       // 태그 파라미터 추가
    ): Mono<Page<WebBookmark>> {
        val visibleScopes = when {
            userDetails != null -> listOf(Visibility.PUBLIC.name, Visibility.MEMBERS.name)
            else -> listOf(Visibility.PUBLIC.name)
        }

        // 동적 쿼리 생성 시작
        val query = Query(Criteria.where("visibility").`in`(visibleScopes))
            .with(Sort.by(Sort.Direction.DESC, "savedAt")) // <-- 이 줄을 추가하세요.
            .with(pageable)

        // 카테고리 조건 추가
        if (!category.isNullOrBlank()) {
            query.addCriteria(Criteria.where("category").`is`(category))
        }

        // 태그 조건 추가 (tags 배열에 해당 태그가 포함되어 있는지 확인)
        if (!tag.isNullOrBlank()) {
            query.addCriteria(Criteria.where("tags").`in`(tag))
        }

        // 데이터 조회 및 카운트
        val bookmarks = reactiveMongoTemplate.find(query, WebBookmark::class.java).collectList()
        val totalCount = reactiveMongoTemplate.count(Query.of(query).limit(-1).skip(-1), WebBookmark::class.java)

        return Mono.zip(bookmarks, totalCount).map { tuple ->
            PageImpl(tuple.t1, pageable, tuple.t2)
        }
    }

    /**
     * 북마크의 좋아요 카운트를 1 증가시킵니다.
     * @param bookmarkId 대상 북마크의 ID
     * @return 업데이트된 WebBookmark 객체
     */
    fun incrementVote(bookmarkId: String): Mono<WebBookmark> {
        val query = Query.query(Criteria.where("id").`is`(bookmarkId))
        val update = Update().inc("voteCount", 1)
        val options = FindAndModifyOptions.options().returnNew(true)
        return reactiveMongoTemplate.findAndModify(query, update, options, WebBookmark::class.java)
    }

    /**
     * 북마크의 싫어요 카운트를 1 증가시킵니다.
     * @param bookmarkId 대상 북마크의 ID
     * @return 업데이트된 WebBookmark 객체
     */
    fun incrementUnlike(bookmarkId: String): Mono<WebBookmark> {
        val query = Query.query(Criteria.where("id").`is`(bookmarkId))
        val update = Update().inc("unlikeCount", 1)
        val options = FindAndModifyOptions.options().returnNew(true)
        return reactiveMongoTemplate.findAndModify(query, update, options, WebBookmark::class.java)
    }

}