package com.swoosh.microblog.worker

import android.content.Context
import android.net.Uri
import androidx.work.*
import com.swoosh.microblog.data.MobiledocBuilder
import com.swoosh.microblog.data.db.Converters
import com.swoosh.microblog.data.model.GhostPost
import com.swoosh.microblog.data.model.GhostTag
import com.swoosh.microblog.data.model.QueueStatus
import com.swoosh.microblog.data.repository.PostRepository
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import java.util.concurrent.TimeUnit

class PostUploadWorker(
    context: Context,
    workerParams: WorkerParameters
) : CoroutineWorker(context, workerParams) {

    private val gson = Gson()

    override suspend fun doWork(): Result {
        val repository = PostRepository(applicationContext)
        val queuedPosts = repository.getQueuedPosts()

        if (queuedPosts.isEmpty()) return Result.success()

        var allSuccess = true

        for (post in queuedPosts) {
            repository.updateQueueStatus(post.localId, QueueStatus.UPLOADING)

            // Upload multiple images if needed
            val allImageUrls = mutableListOf<String>()

            // Handle legacy single image field
            var featureImage = post.uploadedImageUrl
            if (featureImage == null && post.imageUri != null) {
                val imageResult = repository.uploadImage(Uri.parse(post.imageUri))
                if (imageResult.isFailure) {
                    repository.updateQueueStatus(post.localId, QueueStatus.FAILED)
                    allSuccess = false
                    continue
                }
                featureImage = imageResult.getOrNull()
            }

            // Handle multi-image fields
            val existingUploadedUrls = Converters.jsonToStringList(post.uploadedImageUrls)
            val pendingUris = Converters.jsonToStringList(post.imageUris)

            if (existingUploadedUrls.isNotEmpty()) {
                allImageUrls.addAll(existingUploadedUrls)
            } else if (pendingUris.isNotEmpty()) {
                val uris = pendingUris.map { Uri.parse(it) }
                val imagesResult = repository.uploadImages(uris)
                if (imagesResult.isFailure) {
                    repository.updateQueueStatus(post.localId, QueueStatus.FAILED)
                    allSuccess = false
                    continue
                }
                allImageUrls.addAll(imagesResult.getOrThrow())
            }

            // Use first image as feature image if no legacy feature image
            if (featureImage == null && allImageUrls.isNotEmpty()) {
                featureImage = allImageUrls.first()
            }

            // Upload video if needed
            var videoUrl = post.uploadedVideoUrl
            if (videoUrl == null && post.videoUri != null) {
                val videoResult = repository.uploadMediaFile(Uri.parse(post.videoUri))
                if (videoResult.isFailure) {
                    repository.updateQueueStatus(post.localId, QueueStatus.FAILED)
                    allSuccess = false
                    continue
                }
                videoUrl = videoResult.getOrNull()
            }

            // Upload audio if needed
            var audioUrl = post.uploadedAudioUrl
            if (audioUrl == null && post.audioUri != null) {
                val audioResult = repository.uploadMediaFile(Uri.parse(post.audioUri))
                if (audioResult.isFailure) {
                    repository.updateQueueStatus(post.localId, QueueStatus.FAILED)
                    allSuccess = false
                    continue
                }
                audioUrl = audioResult.getOrNull()
            }

            // Upload file if needed
            var uploadedFileUrl = post.uploadedFileUrl
            if (uploadedFileUrl == null && post.fileUri != null) {
                val fileResult = repository.uploadFile(Uri.parse(post.fileUri))
                if (fileResult.isFailure) {
                    repository.updateQueueStatus(post.localId, QueueStatus.FAILED)
                    allSuccess = false
                    continue
                }
                uploadedFileUrl = fileResult.getOrNull()
            }

            val mobiledoc = MobiledocBuilder.build(
                text = post.content,
                imageUrls = allImageUrls,
                linkUrl = post.linkUrl,
                linkTitle = post.linkTitle,
                linkDescription = post.linkDescription,
                imageAlt = post.imageAlt,
                videoUrl = videoUrl,
                audioUrl = audioUrl,
                fileUrl = uploadedFileUrl,
                fileName = post.fileName,
                fileSize = 0 // Size not stored in LocalPost; Ghost only needs src/fileName
            )

            // Parse tags from JSON stored in LocalPost
            val tagNames: List<String> = try {
                gson.fromJson(post.tags, object : TypeToken<List<String>>() {}.type) ?: emptyList()
            } catch (e: Exception) {
                emptyList()
            }
            val ghostTags = tagNames.map { GhostTag(name = it) }

            val isEmailOnly = post.queueStatus == QueueStatus.QUEUED_EMAIL_ONLY

            val ghostPost = GhostPost(
                title = post.title,
                mobiledoc = mobiledoc,
                status = when (post.queueStatus) {
                    QueueStatus.QUEUED_PUBLISH -> "published"
                    QueueStatus.QUEUED_SCHEDULED -> "scheduled"
                    QueueStatus.QUEUED_EMAIL_ONLY -> "published"
                    else -> "draft"
                },
                featured = post.featured,
                feature_image = featureImage,
                feature_image_alt = post.imageAlt,
                published_at = post.scheduledAt,
                visibility = "public",
                tags = ghostTags.ifEmpty { null },
                email_only = if (isEmailOnly) true else null
            )

            // Determine newsletter slug for email-only or newsletter posts
            val newsletterSlug = post.newsletterSlug

            val result = if (post.ghostId != null) {
                repository.updatePost(post.ghostId, ghostPost, newsletter = newsletterSlug)
            } else {
                repository.createPost(ghostPost, newsletter = newsletterSlug)
            }

            result.fold(
                onSuccess = { created ->
                    repository.markUploaded(post.localId, created.id ?: "")
                    // Delete local copy after successful upload
                    repository.deleteLocalPost(post.localId)
                },
                onFailure = {
                    repository.updateQueueStatus(post.localId, QueueStatus.FAILED)
                    allSuccess = false
                }
            )
        }

        return if (allSuccess) Result.success() else Result.retry()
    }

    companion object {
        private const val WORK_NAME = "post_upload"
        private const val PERIODIC_WORK_NAME = "post_upload_periodic"

        fun enqueue(context: Context) {
            val constraints = Constraints.Builder()
                .setRequiredNetworkType(NetworkType.CONNECTED)
                .build()

            val request = OneTimeWorkRequestBuilder<PostUploadWorker>()
                .setConstraints(constraints)
                .setBackoffCriteria(
                    BackoffPolicy.EXPONENTIAL,
                    WorkRequest.MIN_BACKOFF_MILLIS,
                    TimeUnit.MILLISECONDS
                )
                .build()

            WorkManager.getInstance(context)
                .enqueueUniqueWork(WORK_NAME, ExistingWorkPolicy.REPLACE, request)
        }

        fun enqueuePeriodicCheck(context: Context) {
            val constraints = Constraints.Builder()
                .setRequiredNetworkType(NetworkType.CONNECTED)
                .build()

            val request = PeriodicWorkRequestBuilder<PostUploadWorker>(
                15, TimeUnit.MINUTES
            )
                .setConstraints(constraints)
                .build()

            WorkManager.getInstance(context)
                .enqueueUniquePeriodicWork(
                    PERIODIC_WORK_NAME,
                    ExistingPeriodicWorkPolicy.KEEP,
                    request
                )
        }
    }
}
