package com.seven.ikar.kotlin

import android.Manifest
import android.app.Activity
import android.app.KeyguardManager
import android.content.Intent
import android.graphics.Color
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import androidx.activity.ComponentActivity
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.compose.setContent
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.lifecycle.lifecycleScope
import com.seven.ikar.kotlin.app.IkarApp
import com.seven.ikar.kotlin.app.IkarNativeApp
import com.seven.ikar.kotlin.core.push.AndroidAppVisibilityState
import com.seven.ikar.kotlin.core.push.IncomingCallFullScreenSetupHelper
import com.seven.ikar.kotlin.core.push.IncomingCallFullScreenSetupResult
import com.seven.ikar.kotlin.core.push.InitialCallPermissionPromptStore
import com.seven.ikar.kotlin.core.push.endedCallIdFromIntent
import com.seven.ikar.kotlin.core.push.pendingIncomingCallInviteFromIntent
import com.seven.ikar.kotlin.core.push.PushDataKeys
import com.seven.ikar.kotlin.core.push.PushDataKinds
import com.seven.ikar.kotlin.feature.share.PendingExternalShare
import com.seven.ikar.kotlin.ui.SecureWindowFlagController
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {
    private val applicationContainer
        get() = (application as IkarNativeApp).container
    private val initialCallPermissionPromptStore by lazy { InitialCallPermissionPromptStore(this) }
    private var lastInitialCallPermissionRequirement: String? = null
    private var isUnlockedForVisibleSession = false
    private var isUnlockChallengeInProgress = false
    private val appUnlockLauncher = registerForActivityResult(
        ActivityResultContracts.StartActivityForResult()
    ) { result ->
        isUnlockChallengeInProgress = false
        if (result.resultCode == Activity.RESULT_OK) {
            isUnlockedForVisibleSession = true
            applicationContainer.appLockState.setLocked(false)
            SecureWindowFlagController.setSecure(window, AppLockSecureToken, false)
        } else {
            applicationContainer.appLockState.setLocked(true)
            SecureWindowFlagController.setSecure(window, AppLockSecureToken, true)
        }
    }
    private val initialCallPermissionSettingsLauncher = registerForActivityResult(
        ActivityResultContracts.StartActivityForResult()
    ) {
        continueInitialCallPermissionSetup(fromSettingsReturn = true)
    }
    private val initialCallNotificationPermissionLauncher = registerForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) { granted ->
        if (granted) {
            continueInitialCallPermissionSetup()
        } else {
            lastInitialCallPermissionRequirement = InitialRequirementAppNotifications
            initialCallPermissionSettingsLauncher.launch(buildAppNotificationSettingsIntent())
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        WindowCompat.setDecorFitsSystemWindows(window, false)
        window.statusBarColor = Color.TRANSPARENT
        window.navigationBarColor = Color.TRANSPARENT
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            window.isNavigationBarContrastEnforced = false
            window.isStatusBarContrastEnforced = false
        }
        WindowInsetsControllerCompat(window, window.decorView).apply {
            isAppearanceLightStatusBars = true
            isAppearanceLightNavigationBars = true
        }
        handleIncomingIntent(intent)
        setContent {
            IkarApp()
        }
        enforceAppLockIfNeeded()
        continueInitialCallPermissionSetup()
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        setIntent(intent)
        handleIncomingIntent(intent)
    }

    override fun onStart() {
        super.onStart()
        AndroidAppVisibilityState.setVisible(true)
        enforceAppLockIfNeeded()
    }

    override fun onStop() {
        if (!isUnlockChallengeInProgress) {
            isUnlockedForVisibleSession = false
        }
        AndroidAppVisibilityState.setVisible(false)
        super.onStop()
    }

    private fun enforceAppLockIfNeeded() {
        val privacy = applicationContainer.privacyRuntimeSettings.loadSnapshot()
        if (!privacy.passcodeLockEnabled) {
            applicationContainer.appLockState.setLocked(false)
            SecureWindowFlagController.setSecure(window, AppLockSecureToken, false)
            return
        }
        if (isUnlockedForVisibleSession || isUnlockChallengeInProgress) {
            return
        }

        applicationContainer.appLockState.setLocked(true)
        SecureWindowFlagController.setSecure(window, AppLockSecureToken, true)
        val keyguardManager = getSystemService(KeyguardManager::class.java)
        val unlockIntent = if (keyguardManager.isDeviceSecure) {
            keyguardManager.createConfirmDeviceCredentialIntent("Икар", "Разблокируйте устройство, чтобы продолжить")
        } else {
            null
        }

        if (unlockIntent == null) {
            isUnlockedForVisibleSession = true
            applicationContainer.appLockState.setLocked(false)
            SecureWindowFlagController.setSecure(window, AppLockSecureToken, false)
            return
        }

        isUnlockChallengeInProgress = true
        appUnlockLauncher.launch(unlockIntent)
    }

    private fun continueInitialCallPermissionSetup(fromSettingsReturn: Boolean = false) {
        if (initialCallPermissionPromptStore.wasPrompted()) {
            return
        }

        when (val result = IncomingCallFullScreenSetupHelper.evaluate(this)) {
            IncomingCallFullScreenSetupResult.Ready -> {
                initialCallPermissionPromptStore.markPrompted()
                lastInitialCallPermissionRequirement = null
            }

            IncomingCallFullScreenSetupResult.NeedsPostNotificationsPermission -> {
                if (fromSettingsReturn && lastInitialCallPermissionRequirement == InitialRequirementPostNotifications) {
                    initialCallPermissionPromptStore.markPrompted()
                    lastInitialCallPermissionRequirement = null
                    return
                }

                lastInitialCallPermissionRequirement = InitialRequirementPostNotifications
                initialCallNotificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
            }

            is IncomingCallFullScreenSetupResult.NeedsSystemSettings -> {
                val requirement = requirementKeyFor(result)
                if (fromSettingsReturn && lastInitialCallPermissionRequirement == requirement) {
                    initialCallPermissionPromptStore.markPrompted()
                    lastInitialCallPermissionRequirement = null
                    return
                }

                lastInitialCallPermissionRequirement = requirement
                initialCallPermissionSettingsLauncher.launch(result.intent)
            }
        }
    }

    private fun handleIncomingIntent(intent: Intent?) {
        val incomingIntent = intent ?: return
        if (incomingIntent.action == Intent.ACTION_VIEW) {
            val archiveUri = incomingIntent.data ?: incomingIntent.readSingleStream()
            if (archiveUri != null) {
                lifecycleScope.launch {
                    runCatching { applicationContainer.archiveFileService.importFromUri(archiveUri) }
                        .onSuccess(applicationContainer.incomingArchiveStore::setPending)
                        .onFailure { error ->
                            applicationContainer.incomingArchiveStore.setError(
                                message = error.message ?: "Не удалось открыть архив.",
                                fileName = incomingIntent.getStringExtra(Intent.EXTRA_TITLE)
                                    ?: archiveUri.lastPathSegment
                            )
                        }
                }
                return
            }
        }

        if (incomingIntent.action == Intent.ACTION_SEND || incomingIntent.action == Intent.ACTION_SEND_MULTIPLE) {
            lifecycleScope.launch {
                runCatching { extractPendingShare(incomingIntent) }
                    .onSuccess(applicationContainer.incomingShareStore::setPending)
                    .onFailure { applicationContainer.incomingShareStore.clear() }
            }
            return
        }

        val kind = incomingIntent.getStringExtra(PushDataKeys.Kind)
        when (kind) {
            PushDataKinds.AudioCallInvite,
            PushDataKinds.VideoCallInvite -> {
                val invite = pendingIncomingCallInviteFromIntent(incomingIntent)
                if (invite != null) {
                    applicationContainer.incomingCallInviteStore.setPending(
                        invite
                    )
                }
                return
            }

            PushDataKinds.AudioCallEnded,
            PushDataKinds.VideoCallEnded -> {
                val callId = endedCallIdFromIntent(incomingIntent)
                applicationContainer.incomingCallInviteStore.clearByCallId(callId)
                applicationContainer.callManager.dismissCallIfMatches(callId)
                return
            }
        }

        val chatId = incomingIntent.getStringExtra(PushDataKeys.ChatId)
        if ((kind == PushDataKinds.ChatMessage || kind == PushDataKinds.MessageReaction) && !chatId.isNullOrBlank()) {
            applicationContainer.incomingPushNavigationStore.setPendingChat(
                chatId = chatId,
                messageId = incomingIntent.getStringExtra(PushDataKeys.MessageId)
            )
        }
    }

    private suspend fun extractPendingShare(intent: Intent): PendingExternalShare? {
        val sharedText = intent.getStringExtra(Intent.EXTRA_TEXT)?.trim()?.takeIf { it.isNotBlank() }
        val attachmentUris = when (intent.action) {
            Intent.ACTION_SEND -> intent.readSingleStream()?.let(::listOf) ?: emptyList()
            Intent.ACTION_SEND_MULTIPLE -> intent.readMultipleStreams()
            else -> emptyList()
        }
        val attachments = buildList {
            attachmentUris.forEach { uri ->
                add(applicationContainer.attachmentFileService.importFromUri(uri))
            }
        }
        if (sharedText == null && attachments.isEmpty()) {
            return null
        }

        return PendingExternalShare(
            text = sharedText,
            attachments = attachments
        )
    }

    private fun Intent.readSingleStream(): Uri? {
        val directStream = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java)
        } else {
            @Suppress("DEPRECATION")
            getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
        }
        return directStream ?: clipData?.takeIf { it.itemCount > 0 }?.getItemAt(0)?.uri
    }

    private fun Intent.readMultipleStreams(): List<Uri> {
        val extraStreams = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            getParcelableArrayListExtra(Intent.EXTRA_STREAM, Uri::class.java).orEmpty()
        } else {
            @Suppress("DEPRECATION")
            getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM).orEmpty()
        }
        val clipDataStreams = buildList {
            val currentClipData = clipData ?: return@buildList
            repeat(currentClipData.itemCount) { index ->
                currentClipData.getItemAt(index).uri?.let(::add)
            }
        }
        return (extraStreams + clipDataStreams).distinct()
    }

    private fun buildAppNotificationSettingsIntent(): Intent =
        Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply {
            putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
            putExtra("android.provider.extra.APP_PACKAGE", packageName)
            data = Uri.parse("package:$packageName")
        }

    private fun requirementKeyFor(result: IncomingCallFullScreenSetupResult.NeedsSystemSettings): String =
        when (result.intent.action) {
            Settings.ACTION_APP_NOTIFICATION_SETTINGS -> InitialRequirementAppNotifications
            Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS -> InitialRequirementCallsChannel
            Settings.ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT -> InitialRequirementFullScreenIntent
            else -> InitialRequirementAppDetails
        }

    private companion object {
        const val InitialRequirementPostNotifications = "post_notifications"
        const val InitialRequirementAppNotifications = "app_notifications"
        const val InitialRequirementCallsChannel = "calls_channel"
        const val InitialRequirementFullScreenIntent = "full_screen_intent"
        const val InitialRequirementAppDetails = "app_details"
        const val AppLockSecureToken = "app-lock"
    }
}
