package kr.lunaticbum.back.lun.controllers.api

import com.fasterxml.jackson.databind.ObjectMapper
import kotlinx.coroutines.reactor.awaitSingle
import kotlinx.coroutines.reactor.awaitSingleOrNull
import kr.lunaticbum.back.lun.model.BookmarkDataDto
import kr.lunaticbum.back.lun.model.BookmarkImage
import kr.lunaticbum.back.lun.model.BookmarkType
import kr.lunaticbum.back.lun.model.BookmarkUpdateRequest
import kr.lunaticbum.back.lun.model.ImageMetaService
import kr.lunaticbum.back.lun.model.ImageUrlRequest
import kr.lunaticbum.back.lun.model.ImageVisibilityRequest
import kr.lunaticbum.back.lun.model.Visibility
import kr.lunaticbum.back.lun.model.WebBookmark
import kr.lunaticbum.back.lun.service.CommentService
import kr.lunaticbum.back.lun.service.WebBookmarkService
import kr.lunaticbum.back.lun.utils.LogService
import org.springframework.beans.factory.annotation.Value
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.web.bind.annotation.DeleteMapping
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.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestPart
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.multipart.MultipartFile
import reactor.core.publisher.Mono
import java.nio.file.Files
import java.nio.file.Paths
import java.util.UUID

@RestController
@RequestMapping("/api/bookmarks")
class BookmarkApiController(
    private val bookmarkService: WebBookmarkService,
    private val imageMetaService: ImageMetaService,
    private val commentService: CommentService,
    private val objectMapper: ObjectMapper,
    private val logService: LogService,
) {
    @Value("\${image.upload.path}")
    private val uploadPath: String? = null

    @GetMapping("/categories")
    fun getBookmarkCategories(): Mono<List<String>> {
        return bookmarkService.findAllDistinctCategories().collectList()
    }

    @GetMapping("/tags")
    fun getBookmarkTags(): Mono<List<String>> {
        return bookmarkService.findAllDistinctTags().collectList()
    }

    @GetMapping("/list")
    suspend fun getBookmarkList(
        @AuthenticationPrincipal userDetails: UserDetails?,
        pageable: Pageable
    ): ResponseEntity<Page<WebBookmark>> {
        val bookmarksPage = bookmarkService.getVisibleBookmarks(userDetails, pageable,null,null).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
            }
        }
        return ResponseEntity.ok(processedBookmarksPage)
    }

    @PostMapping("/with-image", consumes = [MediaType.MULTIPART_FORM_DATA_VALUE])
    fun saveBookmarkWithImage(
        @RequestPart("imageFile") imageFile: MultipartFile,
        @RequestPart("bookmarkData") bookmarkDataJson: String,
        @AuthenticationPrincipal user: UserDetails?
    ): Mono<ResponseEntity<WebBookmark>> {
        if (user == null || uploadPath.isNullOrBlank()) {
            return Mono.just(ResponseEntity.status(HttpStatus.UNAUTHORIZED).build())
        }

        val uniqueFilename = "${UUID.randomUUID()}_${imageFile.originalFilename}"
        val targetPath = Paths.get(uploadPath, uniqueFilename)
        try {
            imageFile.transferTo(targetPath.toFile())
        } catch (e: Exception) {
            return Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build())
        }

        val bookmarkData: BookmarkDataDto = objectMapper.readValue(bookmarkDataJson, BookmarkDataDto::class.java)

        val newBookmark = WebBookmark(
            userId = user.username,
            url = bookmarkData.url,
            userComment = bookmarkData.userComment,
            visibility = bookmarkData.visibility ?: "PRIVATE",
            metadataStatus = "PENDING",
            userSelectedImageUrl = "/api/images/$uniqueFilename"
        )

        return bookmarkService.saveBookmark(newBookmark)
            .map { savedBookmark -> ResponseEntity.status(HttpStatus.CREATED).body(savedBookmark) }
    }

    @PostMapping("/with-content", consumes = [MediaType.MULTIPART_FORM_DATA_VALUE])
    fun saveBookmarkWithContent(
        @RequestPart("files") files: List<MultipartFile>,
        @RequestPart("bookmarkData") bookmarkDataJson: String,
        @AuthenticationPrincipal user: UserDetails?
    ): Mono<ResponseEntity<WebBookmark>> {
        logService.log("uploadPath >>> ${uploadPath}")
        if (user == null || uploadPath.isNullOrBlank()) {
            return Mono.just(ResponseEntity.status(HttpStatus.UNAUTHORIZED).build())
        }

        val savedFilePaths = files.mapNotNull { file ->
            val uniqueFilename = "${UUID.randomUUID()}_${file.originalFilename}"
            val targetPath = Paths.get(uploadPath, uniqueFilename)
            try {
                file.transferTo(targetPath.toFile())
                "/api/images/$uniqueFilename"
            } catch (e: Exception) {
                e.printStackTrace()
                null
            }
        }

        if (savedFilePaths.isEmpty()) {
            return Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build())
        }

        val bookmarkData: BookmarkDataDto = objectMapper.readValue(bookmarkDataJson, BookmarkDataDto::class.java)

        val newBookmark = WebBookmark(
            userId = user.username,
            url = bookmarkData.url,
            bookmarkType = bookmarkData.bookmarkType ?: BookmarkType.IMAGE.name,
            contentUrls = savedFilePaths,
            userComment = bookmarkData.userComment,
            visibility = bookmarkData.visibility ?: "PRIVATE",
            metadataStatus = "COMPLETED",
            thumbnailUrl = savedFilePaths.first()
        )

        return bookmarkService.saveBookmark(newBookmark)
            .map { savedBookmark -> ResponseEntity.status(HttpStatus.CREATED).body(savedBookmark) }
    }

    @GetMapping("/{id}")
    suspend fun getBookmarkById(
        @PathVariable id: String,
        @AuthenticationPrincipal userDetails: UserDetails?
    ): ResponseEntity<WebBookmark> {
        val bookmark = bookmarkService.findById(id).awaitSingleOrNull()
            ?: return ResponseEntity.notFound().build()

        val isOwner = userDetails?.username == bookmark.userId
        val canView = when (bookmark.visibility) {
            Visibility.PUBLIC.name -> true
            Visibility.MEMBERS.name -> userDetails != null
            Visibility.PRIVATE.name -> isOwner
            else -> false
        }

        return if (canView) {
            ResponseEntity.ok(bookmark)
        } else {
            ResponseEntity.status(HttpStatus.FORBIDDEN).build()
        }
    }

    @DeleteMapping("/{id}")
    suspend fun deleteBookmark(
        @PathVariable id: String,
        @AuthenticationPrincipal userDetails: UserDetails?
    ): ResponseEntity<Map<String, Any>> {
        logService.log("북마크 삭제 요청: ID=$id, 사용자=${userDetails?.username}")

        if (userDetails == null) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(mapOf("message" to "인증이 필요합니다."))
        }

        val bookmark = bookmarkService.findById(id).awaitSingleOrNull()
        if (bookmark == null) {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body(mapOf("message" to "삭제할 북마크를 찾을 수 없습니다: ID=$id"))
        }

        if (userDetails.username != bookmark.userId) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).body(mapOf("message" to "이 북마크를 삭제할 권한이 없습니다."))
        }

        return try {
            bookmarkService.deleteBookmark(id).awaitSingleOrNull()
            logService.log("DB 삭제 성공: ID=$id")
            ResponseEntity.ok(mapOf("message" to "북마크가 성공적으로 삭제되었습니다.", "id" to id))
        } catch (e: Exception) {
            logService.log("DB 삭제 중 예외 발생: ID=$id, 오류=${e.message}")
            ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(mapOf("message" to "북마크 삭제 중 서버 오류가 발생했습니다."))
        }
    }

    @PutMapping("/{id}")
    suspend fun updateBookmark(
        @PathVariable id: String,
        @RequestBody request: BookmarkUpdateRequest,
        @AuthenticationPrincipal userDetails: UserDetails?
    ): ResponseEntity<*> {
        logService.log("북마크 업데이트 요청: ID=$id, 사용자=${userDetails?.username}")

        if (userDetails == null) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(mapOf("message" to "인증이 필요합니다."))
        }

        val existingBookmark = bookmarkService.findById(id).awaitSingleOrNull()
        if (existingBookmark == null) {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body(mapOf("message" to "수정할 북마크를 찾을 수 없습니다: ID=$id"))
        }

        if (userDetails.username != existingBookmark.userId) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).body(mapOf("message" to "이 북마크를 수정할 권한이 없습니다."))
        }

        val updatedBookmark = existingBookmark.copy(
            title = request.title ?: existingBookmark.title,
            userComment = request.userComment ?: existingBookmark.userComment,
            visibility = request.visibility ?: existingBookmark.visibility,
            category = request.category ?: existingBookmark.category,
            tags = request.tags ?: existingBookmark.tags
        )

        return try {
            val savedBookmark = bookmarkService.saveBookmark(updatedBookmark).awaitSingle()
            logService.log("DB 업데이트 성공: ID=$id")
            ResponseEntity.ok(savedBookmark)
        } catch (e: Exception) {
            logService.log("DB 업데이트 중 예외 발생: ID=$id, 오류=${e.message}")
            ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(mapOf("message" to "북마크 업데이트 중 서버 오류가 발생했습니다."))
        }
    }

    @PostMapping("/{id}/images", consumes = [MediaType.MULTIPART_FORM_DATA_VALUE])
    suspend fun addImagesToBookmark(
        @PathVariable id: String,
        @RequestPart("files") files: List<MultipartFile>,
        @AuthenticationPrincipal userDetails: UserDetails?
    ): ResponseEntity<*> {
        if (userDetails == null || uploadPath.isNullOrBlank()) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build<Unit>()
        }

        var bookmark = bookmarkService.findById(id).awaitSingleOrNull()
            ?: return ResponseEntity.notFound().build<Unit>()

        if (bookmark.userId != userDetails.username) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).build<Unit>()
        }

        if (bookmark.images.isEmpty() && bookmark.contentUrls.isNotEmpty()) {
            bookmark = bookmark.copy(
                images = bookmark.contentUrls.map { BookmarkImage(url = it, isVisible = true) },
                contentUrls = emptyList()
            )
        }

        val newImages = files.mapNotNull { file ->
            val uniqueFilename = "${UUID.randomUUID()}_${file.originalFilename}"
            val targetPath = Paths.get(uploadPath, uniqueFilename)
            try {
                file.transferTo(targetPath.toFile())
                BookmarkImage(url = "/api/images/$uniqueFilename", isVisible = true)
            } catch (e: Exception) {
                null
            }
        }

        val updatedBookmark = bookmark.copy(
            images = bookmark.images + newImages
        )

        val saved = bookmarkService.saveBookmark(updatedBookmark).awaitSingle()
        return ResponseEntity.ok(saved)
    }

    @PutMapping("/{id}/images/visibility")
    suspend fun updateImageVisibility(
        @PathVariable id: String,
        @RequestBody request: ImageVisibilityRequest,
        @AuthenticationPrincipal userDetails: UserDetails?
    ): ResponseEntity<*> {
        if (userDetails == null) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build<Unit>()
        }

        var bookmark = bookmarkService.findById(id).awaitSingleOrNull()
            ?: return ResponseEntity.notFound().build<Unit>()

        if (bookmark.userId != userDetails.username) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).build<Unit>()
        }

        if (bookmark.images.isEmpty() && bookmark.contentUrls.isNotEmpty()) {
            bookmark = bookmark.copy(
                images = bookmark.contentUrls.map { BookmarkImage(url = it, isVisible = true) },
                contentUrls = emptyList()
            )
        }

        val updatedImages = bookmark.images.map {
            if (it.url == request.imageUrl) {
                it.copy(isVisible = !it.isVisible)
            } else {
                it
            }
        }

        val updatedBookmark = bookmark.copy(images = updatedImages)
        val saved = bookmarkService.saveBookmark(updatedBookmark).awaitSingle()
        return ResponseEntity.ok(saved)
    }

    @DeleteMapping("/{id}/images")
    suspend fun removeImageFromBookmark(
        @PathVariable id: String,
        @RequestBody request: ImageUrlRequest,
        @AuthenticationPrincipal userDetails: UserDetails?
    ): ResponseEntity<Any> {
        if (userDetails == null) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build()
        }

        val bookmark = bookmarkService.findById(id).awaitSingleOrNull()
            ?: return ResponseEntity.notFound().build()

        if (bookmark.userId != userDetails.username) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).build()
        }

        try {
            val filename = request.imageUrl.substringAfterLast("/")
            val filePath = Paths.get(uploadPath, filename)
            if (filePath.toFile().exists()) {
                Files.delete(filePath)
            }
        } catch (e: Exception) {
            logService.log("Failed to delete image file: ${request.imageUrl}, Error: ${e.message}")
        }

        val updatedBookmark = bookmark.copy(
            contentUrls = bookmark.contentUrls.filter { it != request.imageUrl },
            images = bookmark.images.filter { it.url != request.imageUrl }
        )

        val saved = bookmarkService.saveBookmark(updatedBookmark).awaitSingle()
        return ResponseEntity.ok(saved)
    }
}