package kr.lunaticbum.back.lun.model

import kr.lunaticbum.back.lun.utils.LogService
import lombok.AllArgsConstructor
import lombok.Data
import lombok.NoArgsConstructor
import org.bson.BsonType
import org.bson.codecs.pojo.annotations.BsonId
import org.bson.codecs.pojo.annotations.BsonRepresentation
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Pageable
import org.springframework.data.mongodb.core.ReactiveMongoTemplate
import org.springframework.data.mongodb.core.mapping.Document
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.data.mongodb.repository.ReactiveMongoRepository
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Repository
import org.springframework.stereotype.Service
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import java.net.URLDecoder
import java.time.Duration

@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "Post")
class Post {
    @BsonId
    @BsonRepresentation(BsonType.OBJECT_ID)
    var id: String? = null

    var originId: String? = null

    var title : String? = null
    var content : String? = null
    var category : String? = null
    var tags : String? = null

    var html : String? = null
    var image : String? = null
    var thumb : String? = null

    var writer : String? = null
    var writeTime : Long = 0
    var posting : Boolean = false
    var firstPostLat : Double = 0.0
    var firstPostLon : Double = 0.0
    var firstAddress = ""
    var modifyAddress = ""

    var modifyTime : Long = 0
    var modifyLat : Double = 0.0
    var modifyLon : Double = 0.0

    var readCount : Long = 0
    var voteCount : Long = 0
    var unlikeCount : Long = 0
}

@Document(collection = "Comment")
class Comment {
    @BsonId
    var id: String? = null
    var postId: String? = null            // 댓글이 달린 포스트의 id
    var parentId: String? = null  // 대댓글이면 상위 댓글의 id, 최상위 댓글이면 null
    var writer: String? = null
    var content: String? = null
    var writeTime: Long? = null
    var mentions: List<String>? = null  // 언급된 유저 아이디(선택)
}
@Repository
interface CommentRepository : ReactiveMongoRepository<Comment, String> {
    fun findByPostIdAndParentIdIsNullOrderByWriteTimeAsc(postId: String): Flux<Comment> // 최상위 댓글
    fun findByParentIdOrderByWriteTimeAsc(parentId: String): Flux<Comment>
}

@Service
class CommentService(private val commentRepository: CommentRepository) {

    fun addComment(comment: Comment): Mono<Comment> {
        // 예시: 부모 댓글 존재 여부/권한 검증 등 비즈니스 로직 처리
        return commentRepository.save(comment)
    }

    fun getCommentsForPost(postId: String): Flux<Comment> {
        return commentRepository.findByPostIdAndParentIdIsNullOrderByWriteTimeAsc(postId)
    }

    // 기타: 대댓글 불러오기, 신고/삭제, 멘션 알림 등 확장 가능
}

@Repository
interface PostRepository : ReactiveMongoRepository<Post, String> {
    fun findAllByModifyTime(time : Long? = 0): Flux<Post>
//    @org.springframework.data.mongodb.repository.Query("{ '\$and': [ { 'posting': true }, { '\$expr': { '\$gte': [ { '\$strLenCP': '\$id' }, 4 ] } } ] }")
    fun findAllByOrderByModifyTimeDesc(pageable: Pageable): Flux<Post>
    fun countByOrderByModifyTimeDesc(): Mono<Long>
    fun findTop5ByOrderByReadCountDesc(): Flux<Post>
    fun findTop5ByOrderByModifyTimeDesc(): Flux<Post>
}


@Service
class PostManager(
    private val postRepository: PostRepository,
    private val reactiveMongoTemplate: ReactiveMongoTemplate
)  {
    @Autowired
    private lateinit var logService: LogService

    @Autowired
    private lateinit var bCryptPasswordEncoder: PasswordEncoder
    //    fun getPost(id : String) : Mono<Post> = postRepository.findById(id)
    fun getPost(id: String): Mono<Post> {
        val query = Query.query(Criteria.where("id").`is`(id))
        val update = Update().inc("readCount", 1)

        return reactiveMongoTemplate.findAndModify(query, update, Post::class.java)
            .switchIfEmpty(Mono.error(NoSuchElementException("Post not found with id $id")))
    }


    fun find20(pageable :Pageable) : List<Post> {
        println("pageSize >>> ${pageable.pageSize}")
        println("pageNumber >>> ${pageable.pageNumber}")
        return  postRepository.findAllByOrderByModifyTimeDesc(pageable)
            .doOnNext { println(it) }         // map 대신 doOnNext로 로그 출력
            .collectList()                    // Flux<Post> → Mono<List<Post>>
            .block(Duration.ofSeconds(30))    // Mono<List<Post>> → List<Post>
            ?: listOf()
    }

    fun getTop10Posts(): Flux<Post>  {
        return postRepository.findTop5ByOrderByReadCountDesc().map { p ->
            p.title = URLDecoder.decode(p.title)
            if (p.title?.isEmpty() == true) {
                p.title = "무제(無題)"
            }
            println(p.title)
            p
        }
    }

    fun getRecent10Posts(): Flux<Post>  {
        return postRepository.findTop5ByOrderByModifyTimeDesc().map { p ->
            p.title = URLDecoder.decode(p.title)
            if (p.title?.isEmpty() == true) {
                p.title = "무제(無題)"
            }
            println(p.title)
            p
        }

    }



    fun find4() : List<Post> {
        val originalList = postRepository.findAllByModifyTime(0)
            .takeLast(4)
            .buffer(4)
            .blockLast(Duration.ofSeconds(30)) ?: listOf()

        return originalList + List((4 - originalList.size).coerceAtLeast(0)) {
            Post() // 기본값 생성 (필드 초기화 필요)
        }
//        return  postRepository.findAllByModifyTime(0).takeLast(4).buffer(4).blockLast(Duration.ofSeconds(30)) ?: listOf()
    }

    fun find20() : List<Post> {
        return  postRepository.findAllByModifyTime(0).takeLast(20).buffer(20).blockLast(Duration.ofSeconds(30)) ?: listOf()
    }

    fun save(post: Post): Mono<Post> {
        println("saved user before ${post}")
//        user.hashPassword(bCryptPasswordEncoder)
        return postRepository.save(post).apply {
            subscribe {
                println("saved user after ${this@apply}")
            }
        }
    }


}