package de.sebse.fuplanner2.worker

import android.accounts.Account
import android.accounts.AccountManager
import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.Data
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import de.sebse.fuplanner2.auth.AppAccounts
import de.sebse.fuplanner2.database.AppDatabase
import de.sebse.fuplanner2.database.User

abstract class AbstractAccountWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {

    companion object {
        const val KEY_ACCOUNT_NAME = AccountManager.KEY_ACCOUNT_NAME
        const val KEY_COURSE_ID = "KEY_COURSE_ID"
        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))

        val database = AppDatabase.getInstance()
        database.userDao().findByUsername(account.name)?.let {
            try {
                val success = doActualWork(database, account, it)
                return Result.success(success)
            } catch (e: FetchResourceException) {
                if (e.type == FetchResourceErrorType.ERR_AUTHORIZATION)
                    return Result.failure(workDataOf(OUT_ERROR_CODE to ErrorCodes.ERR_INVALID_PASSWORD.ordinal))
                if (e.type == FetchResourceErrorType.ERR_NETWORK_ERROR && e.statusCode >= 500)
                    return Result.failure(workDataOf(OUT_ERROR_CODE to ErrorCodes.ERR_NETWORK_ERROR.ordinal))
                return Result.retry()
            }
        }
        return Result.success(workDataOf(OUT_ERROR_CODE to ErrorCodes.ERR_NO_ACCOUNT_NAME.ordinal))
    }

    @Throws(FetchResourceException::class)
    abstract suspend fun doActualWork(database: AppDatabase, account: Account, user: User): Data
}
