package de.sebse.fuplanner2.worker

import android.accounts.AccountManager
import android.content.Context
import androidx.work.*
import de.sebse.fuplanner2.auth.AppAccounts
import de.sebse.fuplanner2.database.AppDatabase
import de.sebse.fuplanner2.database.Course
import de.sebse.fuplanner2.utils.Notifications
import de.sebse.fuplanner2.utils.Updatable
import de.sebse.fuplanner2.utils.UpdateResult
import de.sebse.fuplanner2.utils.mergeUpdatable

class SyncWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {

    companion object {
        const val KEY_ACCOUNT_NAME = AccountManager.KEY_ACCOUNT_NAME
        const val OUT_ERROR_CODE = "OUT_CODE"
    }

    enum class ErrorCodes {
        ERR_NO_ACCOUNT_NAME, ERR_TOO_MANY_RETRIES, ERR_INVALID_PASSWORD, ERR_NETWORK_ERROR
    }

    override suspend fun doWork(): Result {
        if (runAttemptCount > 2) {
            return Result.failure(workDataOf(OUT_ERROR_CODE to ErrorCodes.ERR_TOO_MANY_RETRIES.ordinal))
        }
        val accountName = inputData.getString(KEY_ACCOUNT_NAME)
        val accounts = AppAccounts.getInstance()
        val account = accounts.getAccount(accountName)
            ?: return Result.failure(workDataOf(OUT_ERROR_CODE to ErrorCodes.ERR_NO_ACCOUNT_NAME.ordinal))

        when (accounts.refreshSuspended(account)) {
            AppAccounts.RefreshResults.UNSPECIFIED_ERROR,
            AppAccounts.RefreshResults.NETWORK_ERROR -> {
                return Result.retry()
            }
            AppAccounts.RefreshResults.SUCCESS -> {
                val database = AppDatabase.getInstance()
                database.userDao().findByUsername(account.name)?.also {
                    try {
                        val updates: UpdateResult<Course> = CourseWorker.work(applicationContext, database, it)
                        val courseUpdates = updates.updated
                        val courseCreations = updates.added
                        val notifications = mergeUpdatable(UpdateResult(), updates)
                        courseUpdates.forEach { course ->
                            mergeUpdatable(notifications, EventWorker.work(applicationContext, database, it, course))
                            mergeUpdatable(notifications, AnnouncementWorker.work(applicationContext, database, it, course))
                        }
                        val latestSemester = database.courseDao().getLatestSemesterName()
                        courseCreations.forEach { course ->
                            if (course.isSummerSemester == latestSemester.semester && course.year == latestSemester.year) {
                                EventWorker.work(applicationContext, database, it, course)
                            }
                        }
                        Notifications.courseUpdates(notifications, database,  applicationContext)
                        return@doWork Result.success(workDataOf(KEY_ACCOUNT_NAME to accountName))
                    } catch (e: FetchResourceException) {
                        if (e.type == FetchResourceErrorType.ERR_AUTHORIZATION)
                            return@doWork Result.failure(workDataOf(OUT_ERROR_CODE to ErrorCodes.ERR_INVALID_PASSWORD.ordinal))
                        if (e.type == FetchResourceErrorType.ERR_NETWORK_ERROR && e.statusCode >= 500)
                            return@doWork Result.failure(workDataOf(OUT_ERROR_CODE to ErrorCodes.ERR_NETWORK_ERROR.ordinal))
                        return@doWork Result.retry()
                    }
                }
                return Result.failure(workDataOf(OUT_ERROR_CODE to ErrorCodes.ERR_NO_ACCOUNT_NAME.ordinal))
            }
            AppAccounts.RefreshResults.INVALID_PASSWORD -> {
                return Result.success(workDataOf(OUT_ERROR_CODE to ErrorCodes.ERR_INVALID_PASSWORD.ordinal))
            }
        }
    }
}
