package net.sergeych.toread

import android.Manifest
import android.app.AlertDialog
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Color
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.Settings
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.Modifier
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity(), AndroidLibraryDirectoryChooser {
    private lateinit var directoryLauncher: ActivityResultLauncher<Uri?>
    private lateinit var allFilesAccessLauncher: ActivityResultLauncher<Intent>
    private lateinit var readStoragePermissionLauncher: ActivityResultLauncher<String>
    private lateinit var notificationPermissionLauncher: ActivityResultLauncher<String>
    private var pendingDirectoryChoice: CompletableDeferred<String?>? = null
    private var pendingExternalFileAccess: CompletableDeferred<Boolean>? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        directoryLauncher = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri ->
            uri?.let {
                runCatching {
                    contentResolver.takePersistableUriPermission(it, Intent.FLAG_GRANT_READ_URI_PERMISSION)
                }
            }
            pendingDirectoryChoice?.complete(uri?.toString())
            pendingDirectoryChoice = null
        }
        allFilesAccessLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            pendingExternalFileAccess?.complete(hasBroadFileAccess())
            pendingExternalFileAccess = null
        }
        readStoragePermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
            pendingExternalFileAccess?.complete(granted)
            pendingExternalFileAccess = null
        }
        notificationPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) {}
        super.onCreate(savedInstanceState)
        configureEdgeToEdgeBookWindow()
        initToreadPlatform(this, this)
        rememberPlatformOpenBookIntent(intent)

        setContent {
            AndroidAppWindow {
                App()
            }
        }
        requestNotificationPermissionIfNeeded()
    }

    override fun onWindowFocusChanged(hasFocus: Boolean) {
        super.onWindowFocusChanged(hasFocus)
        if (hasFocus) hideNavigationBar()
    }

    override suspend fun chooseDirectory(): String? {
        val result = CompletableDeferred<String?>()
        runOnUiThread {
            if (pendingDirectoryChoice != null || pendingExternalFileAccess != null) {
                result.complete(null)
            } else {
                showDirectoryChoice(result)
            }
        }
        return result.await()
    }

    private fun showDirectoryChoice(result: CompletableDeferred<String?>) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            AlertDialog.Builder(this)
                .setTitle(strings.chooseLibraryFolder)
                .setItems(arrayOf(strings.downloads, strings.otherFolder)) { _, which ->
                    when (which) {
                        0 -> chooseDownloadsDirectory(result)
                        else -> launchSystemDirectoryPicker(result)
                    }
                }
                .setNegativeButton(android.R.string.cancel) { _, _ -> result.complete(null) }
                .setOnCancelListener { result.complete(null) }
                .show()
        } else {
            launchSystemDirectoryPicker(result)
        }
    }

    private fun launchSystemDirectoryPicker(result: CompletableDeferred<String?>) {
        pendingDirectoryChoice = result
        directoryLauncher.launch(null)
    }

    private fun chooseDownloadsDirectory(result: CompletableDeferred<String?>) {
        if (hasBroadFileAccess()) {
            result.complete(downloadsPath())
            return
        }

        val accessResult = CompletableDeferred<Boolean>()
        pendingExternalFileAccess = accessResult
        lifecycleScope.launch {
            val granted = accessResult.await()
            result.complete(if (granted) downloadsPath() else null)
        }
        launchAllFilesAccessSettings()
    }

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

    override suspend fun ensureExternalFileAccess(): Boolean {
        if (hasBroadFileAccess()) return true
        val result = CompletableDeferred<Boolean>()
        runOnUiThread {
            if (pendingExternalFileAccess != null) {
                result.complete(false)
                return@runOnUiThread
            }
            pendingExternalFileAccess = result
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                launchAllFilesAccessSettings()
            } else {
                readStoragePermissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
            }
        }
        return result.await()
    }

    override fun onDestroy() {
        pendingDirectoryChoice?.complete(null)
        pendingDirectoryChoice = null
        pendingExternalFileAccess?.complete(false)
        pendingExternalFileAccess = null
        super.onDestroy()
    }

    private fun hasBroadFileAccess(): Boolean =
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            Environment.isExternalStorageManager()
        } else {
            checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
        }

    private fun launchAllFilesAccessSettings() {
        val appSettings = Intent(
            Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION,
            Uri.parse("package:$packageName"),
        )
        val settingsIntent = if (appSettings.resolveActivity(packageManager) != null) {
            appSettings
        } else {
            Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
        }
        allFilesAccessLauncher.launch(settingsIntent)
    }

    private fun requestNotificationPermissionIfNeeded() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return
        if (checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) return
        notificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
    }

    private fun configureEdgeToEdgeBookWindow() {
        WindowCompat.setDecorFitsSystemWindows(window, false)
        window.navigationBarColor = Color.TRANSPARENT
        window.statusBarColor = Color.TRANSPARENT
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            window.navigationBarDividerColor = Color.TRANSPARENT
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            window.isNavigationBarContrastEnforced = false
            window.isStatusBarContrastEnforced = false
        }
        hideNavigationBar()
    }

    private fun hideNavigationBar() {
        WindowCompat.getInsetsController(window, window.decorView).apply {
            systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
            hide(WindowInsetsCompat.Type.navigationBars())
        }
    }

    private fun downloadsPath(): String =
        Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)?.absolutePath
            ?: Environment.getExternalStorageDirectory().absolutePath
}

@Composable
private fun AndroidAppWindow(content: @Composable () -> Unit) {
    androidx.compose.foundation.layout.Box(
        modifier = Modifier
            .fillMaxSize()
            .windowInsetsPadding(WindowInsets.safeDrawing.only(WindowInsetsSides.Top)),
    ) {
        content()
    }
}

@Preview
@Composable
fun AppAndroidPreview() {
    App()
}
