package bou.amine.apps.readerforselfossv2.android

import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Build
import android.widget.ImageView
import android.widget.Toast
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.multidex.MultiDexApplication
import bou.amine.apps.readerforselfossv2.DI.networkModule
import bou.amine.apps.readerforselfossv2.android.viewmodel.AppViewModel
import bou.amine.apps.readerforselfossv2.dao.DriverFactory
import bou.amine.apps.readerforselfossv2.dao.ReaderForSelfossDB
import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.github.ln_12.library.ConnectivityStatus
import io.github.aakira.napier.DebugAntilog
import io.github.aakira.napier.Napier
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import org.acra.ACRA
import org.acra.ReportField
import org.acra.config.httpSender
import org.acra.config.toast
import org.acra.data.StringFormat
import org.acra.ktx.initAcra
import org.acra.sender.HttpSender
import org.kodein.di.*

class MyApp : MultiDexApplication(), DIAware {

    override val di by DI.lazy {
        import(networkModule)
        bind<DriverFactory>() with singleton { DriverFactory(applicationContext) }
        bind<ReaderForSelfossDB>() with singleton { ReaderForSelfossDB(driverFactory.createDriver()) }
        bind<Repository>() with singleton { Repository(instance(), instance(), isConnectionAvailable, instance()) }
        bind<ConnectivityStatus>() with singleton { ConnectivityStatus(applicationContext) }
        bind<AppViewModel>() with singleton { AppViewModel(repository = instance()) }
    }

    private val repository: Repository by instance()
    private val viewModel: AppViewModel by instance()
    private val connectivityStatus: ConnectivityStatus by instance()
    private val driverFactory: DriverFactory by instance()

    // TODO: handle with the "previous" way
    private val isConnectionAvailable: MutableStateFlow<Boolean> = MutableStateFlow(true)

    override fun onCreate() {
        super.onCreate()
        Napier.base(DebugAntilog())

        if (!ACRA.isACRASenderServiceProcess()) {
            tryToHandleBug()

            handleNotificationChannels()

            ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifeCycleObserver(connectivityStatus, repository))

            CoroutineScope(Dispatchers.Main).launch {
                viewModel.networkAvailableProvider.collect { networkAvailable ->
                    val toastMessage = if (networkAvailable) {
                        repository.handleDBActions()
                        R.string.network_connectivity_retrieved
                    } else {
                        R.string.network_connectivity_lost
                    }

                    Toast.makeText(
                        applicationContext,
                        toastMessage,
                        Toast.LENGTH_SHORT
                    ).show()
                }
            }
        }
    }

    override fun attachBaseContext(base: Context?) {
        super.attachBaseContext(base)

        initAcra {
            reportFormat = StringFormat.JSON
            reportContent = listOf(
                ReportField.REPORT_ID, ReportField.INSTALLATION_ID,
                ReportField.APP_VERSION_CODE, ReportField.APP_VERSION_NAME,
                ReportField.BUILD, ReportField.ANDROID_VERSION, ReportField.BRAND, ReportField.PHONE_MODEL,
                ReportField.AVAILABLE_MEM_SIZE, ReportField.TOTAL_MEM_SIZE,
                ReportField.STACK_TRACE, ReportField.APPLICATION_LOG, ReportField.LOGCAT,
                ReportField.INITIAL_CONFIGURATION, ReportField.CRASH_CONFIGURATION, ReportField.IS_SILENT,
                ReportField.USER_APP_START_DATE, ReportField.USER_COMMENT, ReportField.USER_CRASH_DATE, ReportField.USER_EMAIL, ReportField.CUSTOM_DATA)
            toast {
                //required
                text = getString(R.string.crash_toast_text)
                length = Toast.LENGTH_SHORT
            }
            httpSender {
                uri = "https://bugs.amine-louveau.fr/report" /*best guess, you may need to adjust this*/
                basicAuthLogin = "LMTlLZuazADohTCm"
                basicAuthPassword = "he6ghHp83F0PYPfh"
                httpMethod = HttpSender.Method.POST
            }
        }
    }

    private fun handleNotificationChannels() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager

            val name = getString(R.string.notification_channel_sync)
            val importance = NotificationManager.IMPORTANCE_LOW
            val mChannel = NotificationChannel(AppSettingsService.syncChannelId, name, importance)

            val newItemsChannelname = getString(R.string.new_items_channel_sync)
            val newItemsChannelimportance = NotificationManager.IMPORTANCE_DEFAULT
            val newItemsChannelmChannel = NotificationChannel(AppSettingsService.newItemsChannelId, newItemsChannelname, newItemsChannelimportance)

            notificationManager.createNotificationChannel(mChannel)
            notificationManager.createNotificationChannel(newItemsChannelmChannel)
        }
    }

    private fun tryToHandleBug() {
        val oldHandler = Thread.getDefaultUncaughtExceptionHandler()

        Thread.setDefaultUncaughtExceptionHandler { thread, e ->
            if (e is NoClassDefFoundError && e.stackTrace.asList().any {
                    it.toString().contains("android.view.ViewDebug")
                }) {
            } else {
                oldHandler.uncaughtException(thread, e)
            }
        }
    }

    class AppLifeCycleObserver(val connectivityStatus: ConnectivityStatus, val repository: Repository) : DefaultLifecycleObserver {

        override fun onResume(owner: LifecycleOwner) {
            super.onResume(owner)
            repository.connectionMonitored = true
            connectivityStatus.start()
        }

        override fun onPause(owner: LifecycleOwner) {
            repository.connectionMonitored = false
            connectivityStatus.stop()
            super.onPause(owner)
        }
    }
}