package app.cclauncher.data.repository

import android.content.ComponentName
import android.content.Context
import android.content.pm.LauncherApps
import android.os.Build
import android.util.Log
import androidx.compose.ui.graphics.asImageBitmap
import app.cclauncher.data.AppShortcut
import app.cclauncher.data.AppModel
import app.cclauncher.data.AppKey
import app.cclauncher.settings.AppKeyMigration
import app.cclauncher.helper.BitmapUtils
import app.cclauncher.settings.AppSettingsRepository
import app.cclauncher.helper.PrivateSpaceHelper
import app.cclauncher.helper.getAppsList
import app.cclauncher.data.Constants
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

/**
 * Repository for app-related operations
 */
class AppRepository(
    private val context: Context,
    private val settingsRepository: AppSettingsRepository,
    coroutineScope: CoroutineScope
) {
    private val launcherApps = context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps

    private val loadMutex = Mutex()

    private val _appListAll = MutableStateFlow<List<AppModel>>(emptyList())
    val appListAll: StateFlow<List<AppModel>> = _appListAll.asStateFlow()

    private val _appList = MutableStateFlow<List<AppModel>>(emptyList())
    val appList: StateFlow<List<AppModel>> = _appList.asStateFlow()

    private val _hiddenApps = MutableStateFlow<List<AppModel>>(emptyList())
    val hiddenApps: StateFlow<List<AppModel>> = _hiddenApps.asStateFlow()


    init {
        // Reload apps when icon pack changes
        coroutineScope.launch {
            settingsRepository.settings
                .map { it.selectedIconPack }
                .distinctUntilChanged()
                .drop(1) // Skip initial value
                .collect {
                    // Reload apps to get new icons
                    loadApps(forceEmit = true)
                }
        }
    }

    /**
     * Load all visible apps
     */
    suspend fun loadApps(forceEmit: Boolean = false): Boolean = loadMutex.withLock {
        withContext(Dispatchers.IO) {
            try {
                val settings = settingsRepository.settings.first()
                val sortOrder = settings.searchSortOrder

                val allMobileApps = getAppsList(context, settingsRepository, includeRegularApps = true, includeHiddenApps = true)
                
                val visibleMobileApps = allMobileApps.filter { !it.isHidden }
                val hiddenMobileApps = allMobileApps.filter { it.isHidden }
                
                val systemShortcuts = if (settings.showPinnedShortcuts) {
                    loadSystemShortcuts(settings.renamedApps)
                } else {
                    emptyList()
                }

                val combinedVisible = visibleMobileApps + systemShortcuts
                val combinedAll = allMobileApps + systemShortcuts

                val visibleList = sortApps(combinedVisible, sortOrder)
                val fullList = sortApps(combinedAll, sortOrder)

                Log.d("AppRepository", "Loaded ${visibleList.size} visible, ${hiddenMobileApps.size} hidden")

                var finalVisibleList = visibleList
                var finalFullList = fullList

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
                    val privateSpaceHelper = PrivateSpaceHelper(context)
                    if (privateSpaceHelper.isPrivateSpaceLocked()) {
                        val privateSpaceUser = privateSpaceHelper.getPrivateSpaceUser()
                        if (privateSpaceUser != null) {
                            finalVisibleList = visibleList.filter { it.user != privateSpaceUser }
                            finalFullList = fullList.filter { it.user != privateSpaceUser }
                        }
                    }
                }

                var changed = false

                if (forceEmit || !sameAppList(_appList.value, finalVisibleList)) {
                    _appList.value = finalVisibleList
                    changed = true
                }

                if (forceEmit || !sameAppList(_appListAll.value, finalFullList)) {
                    _appListAll.value = finalFullList
                    changed = true
                }

                if (forceEmit || !sameAppList(_hiddenApps.value, hiddenMobileApps)) {
                    _hiddenApps.value = hiddenMobileApps
                    changed = true
                }

                changed
                
            } catch (e: Exception) {
                Log.e("AppRepository", "Error loading apps", e)
                e.printStackTrace()
                false
            }
        }
    }

    private fun List<AppModel>.uiSignature(): List<String> =
        map { app ->
            buildString {
                append(app.getKey())
                append('|')
                append(app.appLabel)
                append('|')
                append(app.isHidden)
                append('|')
                append(app.lastLaunchTime)
                append('|')
                append(app.isNew)
                append('|')
                append(app.isSystemShortcut)
            }
        }

    private fun sameAppList(
        oldList: List<AppModel>,
        newList: List<AppModel>
    ): Boolean = oldList.uiSignature() == newList.uiSignature()

    private fun sortApps(list: List<AppModel>, sortOrder: Int): List<AppModel> {
        return when (sortOrder) {
            Constants.SortOrder.REVERSE_ALPHABETICAL -> 
                list.sortedByDescending { it.appLabel.lowercase() }
            
            Constants.SortOrder.RECENT_FIRST -> 
                list.sortedWith(
                    compareByDescending<AppModel> { it.lastLaunchTime }
                        .thenBy { it.appLabel.lowercase() }
                )
            
            else -> // ALPHABETICAL
                list.sortedBy { it.appLabel.lowercase() }
        }
    }

    private fun loadSystemShortcuts(renamedApps: Map<String, String>): List<AppModel> {
        val list = mutableListOf<AppModel>()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
            try {
                val launcherApps = context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps

                if (!launcherApps.hasShortcutHostPermission()) {
                    Log.d("AppRepository", "No shortcut host permission (not default launcher?)")
                    return emptyList()
                }

                val query = LauncherApps.ShortcutQuery()
                query.setQueryFlags(LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED)

                val userManager = context.getSystemService(Context.USER_SERVICE) as android.os.UserManager
                for (user in userManager.userProfiles) {
                    try {
                        val shortcuts = launcherApps.getShortcuts(query, user) ?: emptyList()

                        for (shortcut in shortcuts) {
                            val userString = user.toString()
                            val appKey = AppKey.shortcutKey(shortcut.`package`, shortcut.id, userString)
                            val legacyKey = AppKey.legacyShortcutKey(shortcut.`package`, shortcut.id, user.hashCode())
                            val label = shortcut.shortLabel?.toString() ?: shortcut.id
                            val shownLabel = listOf(appKey, legacyKey)
                                .firstNotNullOfOrNull { renamedApps[it] }
                                ?: label
                            val iconDrawable = launcherApps.getShortcutIconDrawable(shortcut, context.resources.displayMetrics.densityDpi)
                            val iconBitmap = BitmapUtils.drawableToBitmap(iconDrawable)?.asImageBitmap()

                            list.add(
                                AppModel(
                                    appLabel = shownLabel,
                                    appPackage = shortcut.`package`,
                                    activityClassName = null,
                                    user = user,
                                    appIcon = iconBitmap,
                                    isSystemShortcut = true,
                                    systemShortcutId = shortcut.id,
                                    systemShortcutPackage = shortcut.`package`,
                                )
                            )
                        }
                    } catch (_: SecurityException) {
                        // if not the default launcher
                    }
                }
            } catch (e: Exception) {
                Log.e("AppRepository", "Error loading system shortcuts", e)
            }
        }
        return list
    }

    suspend fun getAppShortcuts(app: AppModel): List<AppShortcut> {
        return withContext(Dispatchers.IO) {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) return@withContext emptyList()
            if (app.isSystemShortcut) return@withContext emptyList()
            if (!launcherApps.hasShortcutHostPermission()) return@withContext emptyList()

            try {
                val query = LauncherApps.ShortcutQuery()
                    .setPackage(app.appPackage)
                    .setQueryFlags(
                        LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC or
                            LauncherApps.ShortcutQuery.FLAG_MATCH_MANIFEST or
                            LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED
                    )

                val shortcuts = launcherApps.getShortcuts(query, app.user).orEmpty()

                shortcuts.map { shortcut ->
                    val label = shortcut.shortLabel?.toString()
                        ?: shortcut.longLabel?.toString()
                        ?: shortcut.id

                    val iconDrawable = launcherApps.getShortcutIconDrawable(
                        shortcut,
                        context.resources.displayMetrics.densityDpi
                    )
                    val iconBitmap = BitmapUtils.drawableToBitmap(iconDrawable)?.asImageBitmap()

                    AppShortcut(
                        id = shortcut.id,
                        packageName = shortcut.`package`,
                        label = label,
                        user = app.user,
                        icon = iconBitmap
                    )
                }
            } catch (e: Exception) {
                Log.e("AppRepository", "Error loading shortcuts for ${app.appPackage}", e)
                emptyList()
            }
        }
    }

    fun getDefaultAppLabel(app: AppModel): String? {
        if (app.isSystemShortcut) return null
        return try {
            val launcherApps = context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
            val activities = launcherApps.getActivityList(app.appPackage, app.user)
            val target = activities.firstOrNull {
                it.componentName.className == app.activityClassName
            } ?: activities.firstOrNull()
            target?.label?.toString()
        } catch (_: Exception) {
            null
        }
    }

    /**
     * Load hidden apps
     */
    suspend fun loadHiddenApps() {
        withContext(Dispatchers.IO) {
            try {
                val hiddenApps = getAppsList(context, settingsRepository, includeRegularApps = false, includeHiddenApps = true)
                _hiddenApps.value = hiddenApps
            } catch (e: Exception) {
                throw e
            }
        }
    }

    /**
     * Toggle app hidden state
     */
    suspend fun toggleAppHidden(app: AppModel) {
        withContext(Dispatchers.IO) {
            try {
                val appKey = app.getKey()
                val legacyMoveKeys = AppKey.legacyMoveKeysForApp(app)
                val legacyCopyKeys = AppKey.legacyCopyKeysForApp(app)
                val legacyKeys = legacyMoveKeys + legacyCopyKeys

                settingsRepository.toggleAppHidden(appKey, legacyKeys)

                if (legacyMoveKeys.isNotEmpty() || legacyCopyKeys.isNotEmpty()) {
                    val settings = settingsRepository.settings.first()
                    val hasNewRename = settings.renamedApps.containsKey(appKey)
                    val hasNewHidden = settings.hiddenApps.contains(appKey)
                    val newHistory = settings.recentAppHistory[appKey]

                    val legacyRename = legacyCopyKeys.firstNotNullOfOrNull { settings.renamedApps[it] }
                    val legacyHidden = legacyCopyKeys.any { settings.hiddenApps.contains(it) }
                    val legacyHistory = legacyCopyKeys.mapNotNull { settings.recentAppHistory[it] }.maxOrNull()

                    val shouldCopy = (!hasNewRename && legacyRename != null) ||
                        (!hasNewHidden && legacyHidden) ||
                        (legacyHistory != null && (newHistory == null || legacyHistory > newHistory))

                    val copyKeys = if (shouldCopy) legacyCopyKeys else emptySet()

                    settingsRepository.migrateAppKeys(
                        listOf(
                            AppKeyMigration(
                                newKey = appKey,
                                moveKeys = legacyMoveKeys,
                                copyKeys = copyKeys
                            )
                        )
                    )
                }

                loadApps(forceEmit = true)
            } catch (e: Exception) {
                println("Error toggling app hidden state: ${e.message}")
                e.printStackTrace()
                throw e
            }
        }
    }

    /**
     * Launch an app
     */
    suspend fun launchApp(appModel: AppModel) {
        withContext(Dispatchers.Main) {
            try {
                if (appModel.isSystemShortcut) {
                    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) {
                        throw AppLaunchException("Shortcuts require Android 7.1 or higher")
                    }
                    if (!launcherApps.hasShortcutHostPermission()) {
                        throw AppLaunchException("Set CCLauncher as the default launcher to open shortcuts")
                    }

                    val shortcutId = appModel.systemShortcutId
                    val shortcutPackage = appModel.systemShortcutPackage
                    if (shortcutId.isNullOrBlank() || shortcutPackage.isNullOrBlank()) {
                        throw AppLaunchException("Shortcut not available")
                    }

                    launcherApps.startShortcut(shortcutPackage, shortcutId, null, null, appModel.user)
                    return@withContext
                }

                val activityClassName = appModel.activityClassName
                if (activityClassName.isNullOrBlank()) {
                    throw AppLaunchException("App component not found for ${appModel.appLabel}")
                }

                val component = ComponentName(
                    appModel.appPackage,
                    activityClassName
                )
                launcherApps.startMainActivity(component, appModel.user, null, null)
            } catch (e: SecurityException) {
                throw AppLaunchException("Security error launching ${appModel.appLabel}", e)
            } catch (e: NullPointerException) {
                throw AppLaunchException("App component not found for ${appModel.appLabel}", e)
            } catch (e: Exception) {
                throw AppLaunchException("Failed to launch ${appModel.appLabel}", e)
            }
        }
    }

    fun deletePinnedShortcut(packageName: String, shortcutId: String, user: android.os.UserHandle) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) return

        if (!launcherApps.hasShortcutHostPermission()) {
            Log.w("AppRepository", "No shortcut host permission")
            return
        }

        try {
            val query = LauncherApps.ShortcutQuery()
                .setQueryFlags(LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED)
                .setPackage(packageName)

            val pinned = launcherApps.getShortcuts(query, user).orEmpty()
            val remainingIds = pinned.mapNotNull { it.id }.filter { it != shortcutId }.toMutableList()

            launcherApps.pinShortcuts(packageName, remainingIds, user)
            Log.d("AppRepository", "Deleted pinned shortcut: $shortcutId from $packageName")
        } catch (e: Exception) {
            Log.e("AppRepository", "Error deleting pinned shortcut", e)
            throw e
        }
    }

    /**
     * Exception for app launch failures
     */
    class AppLaunchException(message: String, cause: Throwable? = null) : Exception(message, cause)
}
