package kr.lunaticbum.back.lun.service


import kr.lunaticbum.back.lun.model.BookmarkType
import kr.lunaticbum.back.lun.model.MetadataStatus
import kr.lunaticbum.back.lun.model.WebBookmark
import kr.lunaticbum.back.lun.repository.WebBookmarkRepository
import kr.lunaticbum.back.lun.utils.LogService
import org.jsoup.Jsoup
import org.springframework.data.mongodb.repository.ReactiveMongoRepository
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Service
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import reactor.core.scheduler.Schedulers

@Service
class BookmarkProcessorService(
    private val bookmarkRepository: WebBookmarkRepository,
    private val logService: LogService
) {

    // fixedDelayString = "60000" -> 1분에 한 번씩 실행
    @Scheduled(fixedDelayString = "60000")
    fun processPendingBookmarks() {
        logService.log("Starting scheduled job: Process Pending Bookmarks...")

        bookmarkRepository.findByMetadataStatus(MetadataStatus.PENDING.name) // PENDING 상태인 북마크 조회
            .flatMap { bookmark ->
                // 각 북마크에 대해 메타데이터를 가져오고 DB를 업데이트하는 비동기 작업을 수행
                fetchAndApplyMetadata(bookmark)
            }
            .subscribe(
                { updatedBookmark -> logService.log("Successfully processed bookmark ID: ${updatedBookmark.id}") },
                { error -> logService.log("Error during bookmark processing: ${error.message}") }
            )
    }

    private fun fetchAndApplyMetadata(bookmark: WebBookmark): Mono<WebBookmark> {
        return Mono.fromCallable {
            // Jsoup 호출은 블로킹(blocking) 작업이므로 fromCallable로 감싸고
            // 별도 스레드에서 실행되도록 subscribeOn을 사용
            if(bookmark.bookmarkType.equals(BookmarkType.URL.name, ignoreCase = true)){
                logService.log("Fetching metadata for: ${bookmark.contentUrls.first()}")
                val doc = Jsoup.connect(bookmark.contentUrls.first()).timeout(10000).get() // 10초 타임아웃

                // 메타데이터 추출
                val title = doc.select("meta[property=og:title]").attr("content").ifEmpty { doc.title() }
                val description = doc.select("meta[property=og:description]").attr("content")
                val imageUrl = doc.select("meta[property=og:image]").attr("content")

                // 북마크 객체 업데이트
                bookmark.title = title
                bookmark.description = description
                bookmark.thumbnailUrl = imageUrl
                bookmark.metadataStatus = MetadataStatus.COMPLETED.name // 상태를 COMPLETED로 변경
            }
            bookmark

        }
            .subscribeOn(Schedulers.boundedElastic())
            .flatMap { updatedBookmark ->
                // 업데이트된 북마크를 DB에 저장
                bookmarkRepository.save(updatedBookmark)
            }
            .onErrorResume { error ->
                // 오류 발생 시 상태를 FAILED로 변경하여 저장
                logService.log("Failed to fetch metadata for URL: ${bookmark.url}. Error: ${error.message}")
                bookmark.metadataStatus = MetadataStatus.FAILED.name
                bookmarkRepository.save(bookmark)
            }
    }
}
