/*
 * Nextcloud - Android Client
 *
 * SPDX-FileCopyrightText: 2023 Alper Ozturk <alper_ozturk@proton.me>
 * SPDX-FileCopyrightText: 2023 Nextcloud GmbH
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */
package com.nextcloud.client.jobs.transfer

import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.IBinder
import com.nextcloud.client.account.User
import com.nextcloud.client.core.AsyncRunner
import com.nextcloud.client.core.LocalBinder
import com.nextcloud.client.device.PowerManagementService
import com.nextcloud.client.files.Direction
import com.nextcloud.client.files.Request
import com.nextcloud.client.jobs.download.DownloadTask
import com.nextcloud.client.jobs.upload.UploadTask
import com.nextcloud.client.logger.Logger
import com.nextcloud.client.network.ClientFactory
import com.nextcloud.client.network.ConnectivityService
import com.nextcloud.client.notifications.AppNotificationManager
import com.nextcloud.utils.ForegroundServiceHelper
import com.nextcloud.utils.extensions.getParcelableArgument
import com.owncloud.android.datamodel.FileDataStorageManager
import com.owncloud.android.datamodel.ForegroundServiceType
import com.owncloud.android.datamodel.UploadsStorageManager
import dagger.android.AndroidInjection
import javax.inject.Inject
import javax.inject.Named

class FileTransferService : Service() {

    companion object {
        const val TAG = "DownloaderService"
        const val ACTION_TRANSFER = "transfer"
        const val EXTRA_REQUEST = "request"
        const val EXTRA_USER = "user"

        fun createBindIntent(context: Context, user: User): Intent {
            return Intent(context, FileTransferService::class.java).apply {
                putExtra(EXTRA_USER, user)
            }
        }

        fun createTransferRequestIntent(context: Context, request: Request): Intent {
            return Intent(context, FileTransferService::class.java).apply {
                action = ACTION_TRANSFER
                putExtra(EXTRA_REQUEST, request)
            }
        }
    }

    /**
     * Binder forwards [TransferManager] API calls to selected instance of downloader.
     */
    class Binder(
        downloader: TransferManagerImpl,
        service: FileTransferService
    ) : LocalBinder<FileTransferService>(service),
        TransferManager by downloader

    @Inject
    lateinit var notificationsManager: AppNotificationManager

    @Inject
    lateinit var clientFactory: ClientFactory

    @Inject
    @Named("io")
    lateinit var runner: AsyncRunner

    @Inject
    lateinit var logger: Logger

    @Inject
    lateinit var uploadsStorageManager: UploadsStorageManager

    @Inject
    lateinit var connectivityService: ConnectivityService

    @Inject
    lateinit var powerManagementService: PowerManagementService

    @Inject
    lateinit var fileDataStorageManager: FileDataStorageManager

    val isRunning: Boolean get() = downloaders.any { it.value.isRunning }

    private val downloaders: MutableMap<String, TransferManagerImpl> = mutableMapOf()

    override fun onCreate() {
        AndroidInjection.inject(this)
    }

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        if (intent.action != ACTION_TRANSFER) {
            return START_NOT_STICKY
        }

        if (!isRunning) {
            ForegroundServiceHelper.startService(
                this,
                AppNotificationManager.TRANSFER_NOTIFICATION_ID,
                notificationsManager.buildDownloadServiceForegroundNotification(),
                ForegroundServiceType.DataSync
            )
        }

        val request: Request = intent.getParcelableArgument(EXTRA_REQUEST, Request::class.java)!!
        val transferManager = getTransferManager(request.user)
        transferManager.enqueue(request)

        logger.d(TAG, "Enqueued new transfer: ${request.uuid} ${request.file.remotePath}")

        return START_NOT_STICKY
    }

    override fun onBind(intent: Intent?): IBinder? {
        val user = intent?.getParcelableArgument(EXTRA_USER, User::class.java) ?: return null
        return Binder(getTransferManager(user), this)
    }

    private fun onTransferUpdate(transfer: Transfer) {
        if (!isRunning) {
            logger.d(TAG, "All downloads completed")
            notificationsManager.cancelTransferNotification()
            stopForeground(true)
            stopSelf()
        } else if (transfer.direction == Direction.DOWNLOAD) {
            notificationsManager.postDownloadTransferProgress(
                fileOwner = transfer.request.user,
                file = transfer.request.file,
                progress = transfer.progress,
                allowPreview = !transfer.request.test
            )
        } else if (transfer.direction == Direction.UPLOAD) {
            notificationsManager.postUploadTransferProgress(
                fileOwner = transfer.request.user,
                file = transfer.request.file,
                progress = transfer.progress
            )
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        logger.d(TAG, "Stopping downloader service")
    }

    private fun getTransferManager(user: User): TransferManagerImpl {
        val existingDownloader = downloaders[user.accountName]
        return if (existingDownloader != null) {
            existingDownloader
        } else {
            val downloadTaskFactory = DownloadTask.Factory(
                applicationContext,
                { clientFactory.create(user) },
                contentResolver
            )
            val uploadTaskFactory = UploadTask.Factory(
                applicationContext,
                uploadsStorageManager,
                connectivityService,
                powerManagementService,
                { clientFactory.create(user) },
                fileDataStorageManager
            )
            val newDownloader = TransferManagerImpl(runner, downloadTaskFactory, uploadTaskFactory)
            newDownloader.registerTransferListener(this::onTransferUpdate)
            downloaders[user.accountName] = newDownloader
            newDownloader
        }
    }
}
