package app.lonecloud.prism.services

import android.content.pm.PackageManager
import android.os.Bundle
import android.util.Log
import androidx.core.graphics.drawable.toBitmap
import app.lonecloud.prism.BuildConfig
import app.lonecloud.prism.DatabaseFactory
import app.lonecloud.prism.Distributor
import app.lonecloud.prism.PrismPreferences
import app.lonecloud.prism.api.ApiUrlCandidate
import app.lonecloud.prism.utils.TAG
import org.unifiedpush.android.distributor.Database
import org.unifiedpush.android.distributor.MigrationManager
import org.unifiedpush.android.distributor.SourceManager
import org.unifiedpush.android.distributor.UnifiedPushDistributor
import org.unifiedpush.android.distributor.WorkerCompanion
import org.unifiedpush.android.distributor.data.App
import org.unifiedpush.android.distributor.data.Description
import org.unifiedpush.android.distributor.ipc.ACTION_REFRESH_API_URL
import org.unifiedpush.android.distributor.ipc.handler.IAccount
import org.unifiedpush.android.distributor.ipc.handler.IApi
import org.unifiedpush.android.distributor.ipc.handler.IRegistrations
import org.unifiedpush.android.distributor.ipc.sendUiAction
import org.unifiedpush.android.distributor.service.ForegroundServiceFactory
import org.unifiedpush.android.distributor.service.InternalService
import org.unifiedpush.android.distributor.utils.getApplicationIcon

class PrismInternalService : InternalService() {
    override val sourceManager: SourceManager<*> = SourceManager
    override val restartWorker: WorkerCompanion = RestartWorker
    override val startService: ForegroundServiceFactory = FgService
    override val migrationManager: org.unifiedpush.android.distributor.MigrationManager = app.lonecloud.prism.services.MigrationManager()
    override val distributor: UnifiedPushDistributor = Distributor
    override val db: Database by lazy { DatabaseFactory.getDb(this) }

    private val appStore by lazy { PrismPreferences(this) }
    private val hiddenDescription = Description.StringDescription("")

    override var themeDynamicColors: Boolean
        get() = appStore.dynamicColors
        set(value) {
            appStore.dynamicColors = value
        }

    override var showToasts: Boolean
        get() = appStore.showToasts
        set(value) {
            appStore.showToasts = value
        }

    override fun getDebugInfo(): String = "Prism Distributor"

    override fun runAppMigration() = Unit

    override fun account(): IAccount = object : IAccount {
        override fun get(): String? = null
        override fun logout() = Unit
        override fun login(data: Bundle) = Unit
    }

    override fun api(): IApi = object : IApi {
        override fun newPushServer(url: String?) {
            url?.let {
                ApiUrlCandidate.test(context, url)
            } ?: run {
                PrismPreferences(context).apiUrl = BuildConfig.DEFAULT_API_URL
                restartWorker().restart()
                sendUiAction(context, ACTION_REFRESH_API_URL)
            }
        }

        override fun getUrl(): String = appStore.apiUrl
    }

    override fun registrations() = object : IRegistrations {
        override fun delete(registrations: List<String>) {
            registrations.forEach { token ->
                distributor.deleteApp(this@PrismInternalService, token)
            }
        }

        override fun list(): List<App> = db
            .listApps()
            .filterNot { app ->
                app.connectorToken.startsWith("manual_app_") &&
                    appStore.isPendingManualToken(app.connectorToken)
            }
            .map {
                val pm = this@PrismInternalService.packageManager

                val targetPackage = it.description
                    ?.takeIf { description -> description.startsWith("target:") }
                    ?.substringAfter("target:")
                    ?.substringBefore("|")
                    ?.takeIf { pkg -> pkg.isNotBlank() }

                val packageToResolve = targetPackage ?: it.packageName
                val appName = try {
                    val appInfo = pm.getApplicationInfo(packageToResolve, PackageManager.GET_META_DATA)
                    pm.getApplicationLabel(appInfo).toString()
                } catch (e: PackageManager.NameNotFoundException) {
                    Log.w(TAG, "Package label not found for $packageToResolve, using package name", e)
                    packageToResolve
                }

                val displayTitle = it.title ?: appName

                App(
                    connectorToken = it.connectorToken,
                    packageName = it.packageName,
                    endpoint = it.endpoint,
                    vapidKey = it.vapidKey,
                    title = displayTitle,
                    msgCount = it.msgCount,
                    description = hiddenDescription,
                    icon = getApplicationIcon(packageToResolve)?.toBitmap(),
                    isLocal = false
                )
            }

        override fun copyEndpoint(token: String?) = Unit

        override fun addLocal(title: String) = Unit
    }
}
