package kr.lunaticbum.back.lun.repository

import kr.lunaticbum.back.lun.model.AggregationCount
import kr.lunaticbum.back.lun.model.Post
import org.springframework.data.domain.Pageable
import org.springframework.data.mongodb.repository.Aggregation
import org.springframework.data.mongodb.repository.Query
import org.springframework.data.mongodb.repository.ReactiveMongoRepository
import org.springframework.stereotype.Repository
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono

@Repository
interface PostRepository : ReactiveMongoRepository<Post, String> {
    @Query("{ " +
            "  '\$and': [ " +
            "    { 'posting': true, 'isBlocked': false, 'modifyTime': { '\$lt': ?1 } }, " + // 기본 필터 (공개여부, 커서)
            "    { '\$or': [ " +
            "      { 'title': { '\$regex': ?0, '\$options': 'i' } }, " +
            "      { 'content': { '\$regex': ?0, '\$options': 'i' } }, " +
            "      { 'tags': { '\$regex': ?0, '\$options': 'i' } } " +
            "    ] } " +
            "  ] " +
            "}")
    fun searchPosts(keyword: String, maxTime: Long, pageable: Pageable): Flux<Post>

    @Aggregation(pipeline = [
        "{ \$sort: { modifyTime: -1 } }",
        "{ \$group: { _id: { \$ifNull: [\"\$originId\", \"\$_id\"] }, post: { \$first: \"\$\$ROOT\" } } }",
        "{ \$replaceRoot: { newRoot: \"\$post\" } }",
        // [핵심] GIBBERISH 포함, 공개글, 차단안됨, 그리고 입력받은 시간(?0)보다 과거의 글만 조회
        "{ \$match: { posting: true, isBlocked: false, modifyTime: { \$lt: ?0 } } }",
        "{ \$sort: { \"modifyTime\": -1 } }"
    ])
    fun findFeedPostsBefore(timestamp: Long, pageable: Pageable): Flux<Post>


    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>
    @Aggregation(pipeline = [
        "{ \$sort: { modifyTime: -1 } }",
        "{ \$group: { _id: { \$ifNull: [\"\$originId\", \"\$_id\"] }, post: { \$first: \"\$\$ROOT\" } } }",
        "{ \$replaceRoot: { newRoot: \"\$post\" } }",
        "{ \$match: { posting: true, postType: { \$ne: 'GIBBERISH' } } }",
        "{ \$sort: { \"modifyTime\": -1 } }"
    ])
    fun findTop5ByOrderByReadCountDesc(): Flux<Post>

    @Aggregation(pipeline = [
        "{ \$sort: { modifyTime: -1 } }",
        "{ \$group: { _id: { \$ifNull: [\"\$originId\", \"\$_id\"] }, post: { \$first: \"\$\$ROOT\" } } }",
        "{ \$replaceRoot: { newRoot: \"\$post\" } }",
        "{ \$match: { posting: true, postType: { \$ne: 'GIBBERISH' } } }",
        "{ \$sort: { \"modifyTime\": -1 } }"
    ])
    fun findTop5ByOrderByModifyTimeDesc(): Flux<Post>
    fun findByPostTypeOrderByModifyTimeDesc(postType: String): Flux<Post>

    // [단순화] 공개된 글 목록 조회 (페이지네이션)
    fun findByPostingIsTrueOrderByModifyTimeDesc(pageable: Pageable): Flux<Post>

    // [단순화] 공개된 글 개수 카운트
    fun countByPostingIsTrue(): Mono<Long>

    // [단순화] 인기글 5개 조회 (공개된 글 대상)
    fun findTop5ByPostingIsTrueOrderByReadCountDesc(): Flux<Post>

    // [단순화] 최신글 5개 조회 (공개된 글 대상)
    fun findTop5ByPostingIsTrueOrderByModifyTimeDesc(): Flux<Post>

    // [단순화] '글쓰기' 권한 사용자를 위한 조회 (공개된 글 + 내 비공개 글)
    fun findByPostingIsTrueOrWriterOrderByModifyTimeDesc(writer: String, pageable: Pageable): Flux<Post>
    fun countByPostingIsTrueOrWriter(writer: String): Mono<Long>


    // [신규 추가] 익명 사용자용 인기글 (공개된 고유 포스트 대상)
    @Aggregation(pipeline = [
        // 1. 모든 글을 최신순으로 정렬
        "{ \$sort: { modifyTime: -1 } }",
        // 2. 각 글의 '진짜 최신 버전'을 하나씩만 추출
        "{ \$group: { _id: { \$ifNull: [\"\$originId\", \"\$_id\"] }, post: { \$first: \"\$\$ROOT\" } } }",
        // 3. 원래 Post 형태로 복원
        "{ \$replaceRoot: { newRoot: \"\$post\" } }",
        // 4. 최신 버전들 중 '공개' 상태이고 '차단되지 않은' 글만 필터링
        "{ \$match: { posting: true, isBlocked: false } }",
        // 5. 최종 목록을 조회수(readCount) 순으로 정렬
        "{ \$sort: { readCount: -1 } }",
        // 6. 상위 5개만 선택
        "{ \$limit: 5 }"
    ])
    fun findTop5UniquePublishedByReadCountDesc(): Flux<Post>

    // [신규 추가] 익명 사용자용 최신글 (공개된 고유 포스트 대상)
    @Aggregation(pipeline = [
        // 1. 모든 글을 최신순으로 정렬
        "{ \$sort: { modifyTime: -1 } }",
        // 2. 각 글의 '진짜 최신 버전'을 하나씩만 추출
        "{ \$group: { _id: { \$ifNull: [\"\$originId\", \"\$_id\"] }, post: { \$first: \"\$\$ROOT\" } } }",
        // 3. 원래 Post 형태로 복원
        "{ \$replaceRoot: { newRoot: \"\$post\" } }",
        // 4. 최신 버전들 중 '공개' 상태이고 '차단되지 않은' 글만 필터링
        "{ \$match: { posting: true, isBlocked: false } }",
        // 5. 최종 목록을 다시 최신순으로 정렬
        "{ \$sort: { modifyTime: -1 } }",
        // 6. 상위 5개만 선택
        "{ \$limit: 5 }"
    ])
    fun findTop5UniquePublishedByModifyTimeDesc(): Flux<Post>


    /**
     * 익명 사용자를 위한 '고유 최신 글' 목록을 페이지네이션으로 조회합니다.
     * [버그 수정] 2차 정렬 경로를 "post.post.modifyTime" -> "post.modifyTime" 으로 변경
     */
    @Aggregation(pipeline = [
        "{ \$sort: { modifyTime: -1 } }",
        "{ \$group: { _id: { \$ifNull: [\"\$originId\", \"\$_id\"] }, post: { \$first: \"\$\$ROOT\" } } }",
        "{ \$sort: { \"post.modifyTime\": -1 } }", // [수정됨]
        "{ \$replaceRoot: { newRoot: \"\$post\" } }"
    ])
    fun findLatestUniqueOriginPaginated(pageable: Pageable): Flux<Post>

    /**
     * '고유 최신 글'의 총 개수를 카운트합니다. (페이지네이션의 totalElements 계산용)
     */
    @Aggregation(pipeline = [
        "{ \$sort: { modifyTime: -1 } }",
        "{ \$group: { _id: { \$ifNull: [\"\$originId\", \"\$_id\"] } } }", // 고유 ID로 그룹화
        "{ \$count: \"totalCount\" }" // 고유 그룹의 개수를 셈
    ])
    fun countLatestUniqueOrigin(): Mono<AggregationCount> // 헬퍼 클래스로 매핑

    @Aggregation(pipeline = [
        "{ \$match: { \$and: [ { \$or: [ { writer: ?0 }, { posting: true } ] }, { 'postType': { \$ne: 'GIBBERISH' } } ] } }",
        "{ \$sort: { modifyTime: -1 } }",
        "{ \$group: { _id: { \$ifNull: [\"\$originId\", \"\$_id\"] }, post: { \$first: \"\$\$ROOT\" } } }",
        "{ \$replaceRoot: { newRoot: \"\$post\" } }",
        "{ \$sort: { \"modifyTime\": -1 } }"
    ])
    fun findLatestUniqueForWriterPaginated(username: String, pageable: Pageable): Flux<Post>

    @Aggregation(pipeline = [
        "{ \$match: { \$and: [ { \$or: [ { writer: ?0 }, { posting: true } ] }, { 'postType': { \$ne: 'GIBBERISH' } } ] } }",
        "{ \$group: { _id: { \$ifNull: [\"\$originId\", \"\$_id\"] } } }",
        "{ \$count: \"totalCount\" }"
    ])
    fun countLatestUniqueForWriter(username: String): Mono<AggregationCount>


    /**
     * [수정] GIBBERISH 타입을 제외하고, posting이 true인 문서만 필터링하는 $match 단계를 추가합니다.
     */
    @Aggregation(pipeline = [
        "{ \$sort: { modifyTime: -1 } }",
        "{ \$group: { _id: { \$ifNull: [\"\$originId\", \"\$_id\"] }, post: { \$first: \"\$\$ROOT\" } } }",
        "{ \$replaceRoot: { newRoot: \"\$post\" } }",
        "{ \$match: { posting: true, postType: { \$ne: 'GIBBERISH' } } }",
        "{ \$sort: { \"modifyTime\": -1 } }"
    ])
    fun findLatestUniquePublishedPaginated(pageable: Pageable): Flux<Post>

    /**
     * '고유 최신 글' 중 공개된 글의 총 개수를 카운트합니다.
     * [수정] GIBBERISH 타입을 제외하고, posting이 true인 문서만 필터링하는 $match 단계를 추가합니다.
     */
    @Aggregation(pipeline = [
        "{ \$sort: { modifyTime: -1 } }",
        "{ \$group: { _id: { \$ifNull: [\"\$originId\", \"\$_id\"] }, post: { \$first: \"\$\$ROOT\" } } }",
        "{ \$replaceRoot: { newRoot: \"\$post\" } }",
        "{ \$match: { posting: true, postType: { \$ne: 'GIBBERISH' } } }",
        "{ \$count: \"totalCount\" }"
    ])
    fun countLatestUniquePublished(): Mono<AggregationCount>

    fun findByWriterOrderByModifyTimeDesc(writer: String, pageable: Pageable): Flux<Post> // [신규 추가]

    // [신규 추가] 특정 타입의 포스트 중 공개된 것을 무작위로 1개 조회
    @Aggregation(pipeline = [
        "{ \$match: { postType: ?0, posting: true, isBlocked: false } }", // 타입, 공개, 차단안됨 필터링
        "{ \$sample: { size: 1 } }" // 무작위로 1개 샘플링
    ])
    fun findRandomPublishedPostByType(postType: String): Mono<Post>

    // --- [신규 추가] 필터링을 위한 Repository 메소드 ---
    fun findByCategoryAndPostingIsTrueOrderByModifyTimeDesc(category: String, pageable: Pageable): Flux<Post>
    fun countByCategoryAndPostingIsTrue(category: String): Mono<Long>
    fun findByTagsRegexAndPostingIsTrueOrderByModifyTimeDesc(tag: String, pageable: Pageable): Flux<Post>
    fun countByTagsRegexAndPostingIsTrue(tag: String): Mono<Long>
    // [추가] MongoDB Aggregation을 사용해 고유 태그 목록을 효율적으로 조회
    @Aggregation(pipeline = [
        // 1. tags 필드가 null이면 빈 문자열로 만든 후 "," 기준으로 잘라 배열로 변환
        "{ \$project: { tags: { \$split: [ { \$ifNull: [ \"\$tags\", \"\" ] }, \",\" ] } } }",
        // 2. 생성된 tags 배열을 개별 문서로 분리 (예: ["a","b"] -> {tags:"a"}, {tags:"b"})
        "{ \$unwind: \"\$tags\" }",
        // 3. 각 태그의 앞뒤 공백 제거
        "{ \$project: { tag: { \$trim: { input: \"\$tags\" } } } }",
        // 4. 공백이 제거된 태그로 그룹화하여 고유한 값만 추출
        "{ \$group: { _id: \"\$tag\" } }",
        // 5. 그룹화 결과 중 빈 값("")은 제외
        "{ \$match: { _id: { \$ne: \"\" } } }"
    ])
    fun findDistinctTags(): Flux<org.bson.Document> // 반환 타입을 Document로 변경

    // [신규 추가] GIBBERISH 제외하고 조회
    fun findByPostTypeNotOrderByModifyTimeDesc(postType: String, pageable: Pageable): Flux<Post>
    fun countByPostTypeNot(postType: String): Mono<Long>
}
