package com.seven.ikar.kotlin.feature.transfer

import android.content.Context
import androidx.work.Constraints
import androidx.work.ExistingWorkPolicy
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.workDataOf
import com.seven.ikar.kotlin.core.local.TransferQueueDao
import com.seven.ikar.kotlin.core.local.TransferQueueEntity
import com.seven.ikar.kotlin.core.local.TransferQueueStatus
import com.seven.ikar.kotlin.core.local.TransferQueueTypes
import com.seven.ikar.kotlin.core.model.AttachmentDto
import com.seven.ikar.kotlin.core.model.StoryVisibility
import com.seven.ikar.kotlin.feature.chat.LocalAttachmentFile
import kotlinx.coroutines.flow.Flow
import java.util.UUID

class TransferQueueRepository(
    private val dao: TransferQueueDao,
    private val scheduler: TransferWorkScheduler
) {
    fun observeActiveForChat(chatId: String): Flow<List<TransferQueueEntity>> =
        dao.observeActiveForChat(chatId)

    fun observeActiveStoryUploads(): Flow<List<TransferQueueEntity>> =
        dao.observeActiveStoryUploads()

    suspend fun enqueueUpload(
        chatId: String,
        text: String?,
        replyToMessageId: String?,
        files: List<LocalAttachmentFile>
    ): String {
        require(files.isNotEmpty()) { "Нужен хотя бы один файл." }

        val groupId = UUID.randomUUID().toString()
        val now = System.currentTimeMillis()
        dao.upsert(
            files.mapIndexed { index, file ->
                TransferQueueEntity(
                    id = UUID.randomUUID().toString(),
                    groupId = groupId,
                    type = TransferQueueTypes.Upload,
                    status = TransferQueueStatus.Queued,
                    chatId = chatId,
                    text = text?.takeIf { it.isNotBlank() },
                    replyToMessageId = replyToMessageId,
                    attachmentId = null,
                    storyVisibility = null,
                    hasProtectedContent = false,
                    ttlSeconds = null,
                    fileName = file.fileName,
                    contentType = file.contentType,
                    fileSizeBytes = file.fileSizeBytes,
                    localPath = file.localPath,
                    sortOrder = index,
                    progressPercent = 0,
                    errorMessage = null,
                    createdAtMillis = now,
                    updatedAtMillis = now
                )
            }
        )
        scheduler.enqueueUpload(groupId)
        return groupId
    }

    suspend fun enqueueStoryUpload(
        text: String?,
        visibility: StoryVisibility,
        hasProtectedContent: Boolean,
        ttlSeconds: Int,
        files: List<LocalAttachmentFile>
    ): String {
        require(files.isNotEmpty()) { "Нужен хотя бы один файл." }

        val groupId = UUID.randomUUID().toString()
        val now = System.currentTimeMillis()
        dao.upsert(
            files.mapIndexed { index, file ->
                TransferQueueEntity(
                    id = UUID.randomUUID().toString(),
                    groupId = groupId,
                    type = TransferQueueTypes.StoryUpload,
                    status = TransferQueueStatus.Queued,
                    chatId = null,
                    text = text?.takeIf { it.isNotBlank() },
                    replyToMessageId = null,
                    attachmentId = null,
                    storyVisibility = visibility.name,
                    hasProtectedContent = hasProtectedContent,
                    ttlSeconds = ttlSeconds,
                    fileName = file.fileName,
                    contentType = file.contentType,
                    fileSizeBytes = file.fileSizeBytes,
                    localPath = file.localPath,
                    sortOrder = index,
                    progressPercent = 0,
                    errorMessage = null,
                    createdAtMillis = now,
                    updatedAtMillis = now
                )
            }
        )
        scheduler.enqueueStoryUpload(groupId)
        return groupId
    }

    suspend fun enqueueDownload(chatId: String, attachment: AttachmentDto): String {
        val groupId = UUID.randomUUID().toString()
        val now = System.currentTimeMillis()
        dao.upsert(
            listOf(
                TransferQueueEntity(
                    id = UUID.randomUUID().toString(),
                    groupId = groupId,
                    type = TransferQueueTypes.Download,
                    status = TransferQueueStatus.Queued,
                    chatId = chatId,
                    text = null,
                    replyToMessageId = null,
                    attachmentId = attachment.id,
                    storyVisibility = null,
                    hasProtectedContent = false,
                    ttlSeconds = null,
                    fileName = attachment.fileName,
                    contentType = attachment.contentType,
                    fileSizeBytes = attachment.fileSizeBytes,
                    localPath = "",
                    sortOrder = attachment.sortOrder,
                    progressPercent = 0,
                    errorMessage = null,
                    createdAtMillis = now,
                    updatedAtMillis = now
                )
            )
        )
        scheduler.enqueueDownload(groupId)
        return groupId
    }

    suspend fun retry(groupId: String) {
        val type = dao.getGroup(groupId).firstOrNull()?.type
        dao.updateGroupStatus(groupId, TransferQueueStatus.Queued, 0, null, System.currentTimeMillis())
        when (type) {
            TransferQueueTypes.Download -> scheduler.enqueueDownload(groupId)
            TransferQueueTypes.StoryUpload -> scheduler.enqueueStoryUpload(groupId)
            else -> scheduler.enqueueUpload(groupId)
        }
    }

    suspend fun cancel(groupId: String) {
        scheduler.cancel(groupId)
        dao.deleteGroup(groupId)
    }
}

class TransferWorkScheduler(context: Context) {
    private val workManager = WorkManager.getInstance(context.applicationContext)
    private val networkConstraints = Constraints.Builder()
        .setRequiredNetworkType(NetworkType.CONNECTED)
        .build()

    fun enqueueUpload(groupId: String) {
        val request = OneTimeWorkRequestBuilder<AttachmentUploadWorker>()
            .setInputData(workDataOf(AttachmentUploadWorker.KeyGroupId to groupId))
            .setConstraints(networkConstraints)
            .build()

        workManager.enqueueUniqueWork(uploadWorkName(groupId), ExistingWorkPolicy.REPLACE, request)
    }

    fun enqueueStoryUpload(groupId: String) {
        val request = OneTimeWorkRequestBuilder<AttachmentUploadWorker>()
            .setInputData(workDataOf(AttachmentUploadWorker.KeyGroupId to groupId))
            .setConstraints(networkConstraints)
            .build()

        workManager.enqueueUniqueWork(storyUploadWorkName(groupId), ExistingWorkPolicy.REPLACE, request)
    }

    fun enqueueDownload(groupId: String) {
        val request = OneTimeWorkRequestBuilder<AttachmentDownloadWorker>()
            .setInputData(workDataOf(AttachmentDownloadWorker.KeyGroupId to groupId))
            .setConstraints(networkConstraints)
            .build()

        workManager.enqueueUniqueWork(downloadWorkName(groupId), ExistingWorkPolicy.REPLACE, request)
    }

    fun cancel(groupId: String) {
        workManager.cancelUniqueWork(uploadWorkName(groupId))
        workManager.cancelUniqueWork(storyUploadWorkName(groupId))
        workManager.cancelUniqueWork(downloadWorkName(groupId))
    }

    private fun uploadWorkName(groupId: String): String = "upload-$groupId"

    private fun storyUploadWorkName(groupId: String): String = "story-upload-$groupId"

    private fun downloadWorkName(groupId: String): String = "download-$groupId"
}
