package com.yorvana.ui.navigation

import android.content.Intent
import android.net.Uri
import androidx.activity.compose.BackHandler
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.ContentTransform
import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.CubicBezierEasing
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.BugReport
import androidx.compose.material.icons.filled.Check
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.ListItemDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.navigation3.runtime.NavBackStack
import androidx.navigation3.runtime.NavKey
import androidx.navigation3.runtime.entryProvider
import androidx.navigation3.runtime.rememberNavBackStack
import androidx.navigation3.ui.NavDisplay
import com.yorvana.R
import com.yorvana.YorvanaApplication
import com.yorvana.ui.TestTags
import com.yorvana.ui.components.PrivacyPolicyDialog
import com.yorvana.ui.components.TopBarBackButton
import com.yorvana.ui.records.AddEditRecordScreen
import com.yorvana.ui.records.ImageViewerScreen
import com.yorvana.ui.records.RecordDetailScreen
import com.yorvana.ui.records.VehicleDetailScreen
import com.yorvana.ui.reportbug.ReportBugScreen
import com.yorvana.ui.settings.CategoriesScreen
import com.yorvana.ui.settings.SettingsScreen
import com.yorvana.ui.theme.appTopAppBarColors
import com.yorvana.ui.vehicles.AddEditVehicleScreen
import com.yorvana.ui.vehicles.VehicleListScreen
import kotlinx.coroutines.launch

private const val NAV_PREFS = "yorvana_navigation"
private const val KEY_LAST_ROUTE = "last_route"
private const val ROUTE_PART_COUNT_SIMPLE = 1
private const val ROUTE_PART_COUNT_WITH_ONE_ARG = 2
private const val ROUTE_PART_COUNT_WITH_TWO_ARGS = 3
private const val ROOT_BACK_STACK_SIZE = 1

private val simpleSavedRoutes: Map<String, NavKey> =
    mapOf(
        "setup" to Setup,
        "vehicle-list" to VehicleList,
        "add-vehicle" to AddVehicle,
        "settings" to Settings,
        "categories" to Categories,
        "report-bug" to ReportBug,
    )

val LocalNavBackStack = compositionLocalOf<NavBackStack<NavKey>> { error("No backstack") }

private val MaterialEmphasizedEasing = CubicBezierEasing(0.35f, 0.0f, 0.0f, 1.0f)
private const val STANDARD_DURATION = 450

private fun <T> AnimatedContentTransitionScope<T>.modernForwardSpec(): ContentTransform =
    (
        slideInHorizontally(tween(STANDARD_DURATION, easing = MaterialEmphasizedEasing)) { it } +
            fadeIn(tween(STANDARD_DURATION, easing = MaterialEmphasizedEasing)) togetherWith
            slideOutHorizontally(tween(STANDARD_DURATION, easing = MaterialEmphasizedEasing)) { -it / 4 } +
            fadeOut(tween(STANDARD_DURATION, easing = MaterialEmphasizedEasing))
    ).apply {
        targetContentZIndex = 1f
    }

private fun <T> AnimatedContentTransitionScope<T>.modernBackwardSpec(): ContentTransform =
    (
        slideInHorizontally(tween(STANDARD_DURATION, easing = MaterialEmphasizedEasing)) { -it / 4 } +
            fadeIn(tween(STANDARD_DURATION, easing = MaterialEmphasizedEasing)) togetherWith
            slideOutHorizontally(tween(STANDARD_DURATION, easing = MaterialEmphasizedEasing)) { it } +
            fadeOut(tween(STANDARD_DURATION, easing = MaterialEmphasizedEasing))
    ).apply {
        targetContentZIndex = -1f
    }

@OptIn(ExperimentalMaterial3Api::class)
@Suppress("SpreadOperator")
@Composable
fun AppNavGraph(startDestination: NavKey) {
    val context = LocalContext.current
    val backStack = rememberNavBackStack(*startDestination.withParentBackStack().toTypedArray())

    LaunchedEffect(backStack) {
        snapshotFlow { backStack.lastOrNull() }
            .collect { key ->
                context
                    .getSharedPreferences(NAV_PREFS, android.content.Context.MODE_PRIVATE)
                    .edit()
                    .putString(KEY_LAST_ROUTE, key?.toSavedRoute())
                    .apply()
            }
    }

    CompositionLocalProvider(
        LocalNavBackStack provides backStack,
    ) {
        NavDisplay<NavKey>(
            backStack = backStack,
            modifier = Modifier.fillMaxSize().background(MaterialTheme.colorScheme.background),
            onBack = {
                backStack.popNonRoot()
            },
            transitionSpec = {
                modernForwardSpec()
            },
            popTransitionSpec = {
                modernBackwardSpec()
            },
            entryProvider =
                entryProvider {
                    entry<Setup> {
                        VanillaEntry {
                            SetupScreen()
                        }
                    }
                    entry<VehicleList> {
                        VanillaEntry {
                            VehicleListScreen(backStack = backStack)
                        }
                    }
                    entry<AddVehicle> {
                        VanillaEntry {
                            AddEditVehicleScreen(vehicleId = null, backStack = backStack)
                        }
                    }
                    entry<EditVehicle> { key ->
                        VanillaEntry {
                            AddEditVehicleScreen(vehicleId = key.vehicleId, backStack = backStack)
                        }
                    }
                    entry<VehicleDetail> { key ->
                        VanillaEntry {
                            VehicleDetailScreen(vehicleId = key.vehicleId, backStack = backStack)
                        }
                    }
                    entry<AddRecord> { key ->
                        VanillaEntry {
                            AddEditRecordScreen(vehicleId = key.vehicleId, recordId = null, backStack = backStack)
                        }
                    }
                    entry<EditRecord> { key ->
                        VanillaEntry {
                            AddEditRecordScreen(vehicleId = key.vehicleId, recordId = key.recordId, backStack = backStack)
                        }
                    }
                    entry<RecordDetail> { key ->
                        VanillaEntry {
                            RecordDetailScreen(vehicleId = key.vehicleId, recordId = key.recordId, backStack = backStack)
                        }
                    }
                    entry<Settings> {
                        VanillaEntry {
                            SettingsScreen(backStack = backStack)
                        }
                    }
                    entry<Categories> {
                        VanillaEntry {
                            CategoriesScreen(backStack = backStack)
                        }
                    }
                    entry<ReportBug> {
                        VanillaEntry {
                            ReportBugScreen(backStack = backStack)
                        }
                    }
                    entry<ImageViewer> { key ->
                        VanillaEntry {
                            ImageViewerScreen(uriString = key.uriString, backStack = backStack)
                        }
                    }
                },
        )
    }
}

internal fun NavKey.withParentBackStack(): List<NavKey> =
    when (this) {
        is EditVehicle -> listOf(VehicleList, this)
        is VehicleDetail -> listOf(VehicleList, this)
        is AddRecord -> listOf(VehicleList, VehicleDetail(vehicleId), this)
        is EditRecord -> listOf(VehicleList, VehicleDetail(vehicleId), RecordDetail(vehicleId, recordId), this)
        is RecordDetail -> listOf(VehicleList, VehicleDetail(vehicleId), this)
        is ImageViewer -> listOf(VehicleList, this)
        AddVehicle -> listOf(VehicleList, this)
        Settings -> listOf(VehicleList, this)
        Categories -> listOf(VehicleList, this)
        ReportBug -> listOf(VehicleList, this)
        else -> listOf(this)
    }

internal fun NavBackStack<NavKey>.popNonRoot(): Boolean =
    if (size > ROOT_BACK_STACK_SIZE) {
        removeLastOrNull()
        true
    } else {
        false
    }

fun android.content.Context.restoreSavedNavRoute(): NavKey? =
    getSharedPreferences(NAV_PREFS, android.content.Context.MODE_PRIVATE)
        .getString(KEY_LAST_ROUTE, null)
        ?.toNavKey()

internal fun NavKey.toSavedRoute(): String? =
    when (this) {
        Setup -> "setup"
        VehicleList -> "vehicle-list"
        AddVehicle -> "add-vehicle"
        is EditVehicle -> "edit-vehicle|${vehicleId.encodeRoutePart()}"
        is VehicleDetail -> "vehicle-detail|${vehicleId.encodeRoutePart()}"
        is AddRecord -> "add-record|${vehicleId.encodeRoutePart()}"
        is EditRecord -> "edit-record|${vehicleId.encodeRoutePart()}|${recordId.encodeRoutePart()}"
        is RecordDetail -> "record-detail|${vehicleId.encodeRoutePart()}|${recordId.encodeRoutePart()}"
        Settings -> "settings"
        Categories -> "categories"
        ReportBug -> "report-bug"
        is ImageViewer -> "image-viewer|${uriString.encodeRoutePart()}"
        else -> null
    }

internal fun String.toNavKey(): NavKey? {
    val parts = split("|")
    return when (parts.firstOrNull()) {
        in simpleSavedRoutes -> simpleSavedRoutes[parts.first()].takeIf { parts.size == ROUTE_PART_COUNT_SIMPLE }
        "edit-vehicle" -> parts.oneArgRoute(::EditVehicle)
        "vehicle-detail" -> parts.oneArgRoute(::VehicleDetail)
        "add-record" -> parts.oneArgRoute(::AddRecord)
        "edit-record" ->
            if (parts.size == ROUTE_PART_COUNT_WITH_TWO_ARGS) {
                EditRecord(parts[1].decodeRoutePart(), parts[2].decodeRoutePart())
            } else {
                null
            }
        "record-detail" ->
            if (parts.size == ROUTE_PART_COUNT_WITH_TWO_ARGS) {
                RecordDetail(parts[1].decodeRoutePart(), parts[2].decodeRoutePart())
            } else {
                null
            }
        "image-viewer" -> parts.oneArgRoute(::ImageViewer)
        else -> null
    }
}

private fun List<String>.oneArgRoute(factory: (String) -> NavKey): NavKey? =
    if (size == ROUTE_PART_COUNT_WITH_ONE_ARG) {
        this[1].decodeRoutePart().let(factory)
    } else {
        null
    }

private fun String.encodeRoutePart(): String = Uri.encode(this)

private fun String.decodeRoutePart(): String = Uri.decode(this)

@Composable
private fun VanillaEntry(content: @Composable () -> Unit) {
    Surface(
        modifier = Modifier.fillMaxSize(),
        color = MaterialTheme.colorScheme.background,
    ) {
        content()
    }
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
internal fun SetupStep2Scaffold(
    onBack: () -> Unit,
    content: @Composable () -> Unit,
) {
    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text(stringResource(R.string.setup_step_2_of_2)) },
                navigationIcon = {
                    TopBarBackButton(onClick = onBack)
                },
                colors = appTopAppBarColors(),
            )
        },
    ) { padding ->
        Box(modifier = Modifier.padding(padding)) {
            content()
        }
    }
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
internal fun SetupScreen() {
    val context = LocalContext.current
    val application = context.applicationContext as YorvanaApplication
    val scope = rememberCoroutineScope()

    var step by rememberSaveable { mutableStateOf(1) }
    var selectedUri by rememberSaveable { mutableStateOf<String?>(null) }
    var crashReportingEnabled by rememberSaveable { mutableStateOf(true) }

    BackHandler(enabled = step == 2) { step = 1 }

    val folderPicker =
        rememberLauncherForActivityResult(
            ActivityResultContracts.OpenDocumentTree(),
        ) { uri ->
            if (uri == null) return@rememberLauncherForActivityResult
            context.contentResolver.takePersistableUriPermission(
                uri,
                Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
            )
            selectedUri = uri.toString()
            step = 2
        }

    Crossfade(targetState = step, label = "SetupStep") { currentStep ->
        when (currentStep) {
            1 ->
                Scaffold { padding ->
                    Box(modifier = Modifier.padding(padding)) {
                        SetupStepFolder(onChooseFolder = { folderPicker.launch(null) })
                    }
                }

            2 ->
                SetupStep2Scaffold(onBack = { step = 1 }) {
                    SetupStepSentry(
                        enabled = crashReportingEnabled,
                        onEnabledChange = { crashReportingEnabled = it },
                        onComplete = {
                            scope.launch {
                                application.preferences.recordConsentDecision(crashReportingEnabled)
                                application.preferences.setVaultUri(selectedUri ?: "")
                                if (crashReportingEnabled) application.enableSentryNow()
                            }
                        },
                    )
                }
        }
    }
}

@Composable
private fun SetupStepFolder(onChooseFolder: () -> Unit) {
    Column(
        modifier =
            Modifier
                .fillMaxSize()
                .padding(32.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center,
    ) {
        Text(
            text = stringResource(R.string.setup_title),
            style = MaterialTheme.typography.headlineMedium,
            textAlign = TextAlign.Center,
        )
        Spacer(Modifier.height(16.dp))
        Text(
            text = stringResource(R.string.setup_description),
            style = MaterialTheme.typography.bodyLarge,
            color = MaterialTheme.colorScheme.onSurfaceVariant,
            textAlign = TextAlign.Center,
        )
        Spacer(Modifier.height(32.dp))
        Button(onClick = onChooseFolder) {
            Text(stringResource(R.string.setup_choose_folder))
        }
    }
}

@Composable
internal fun SetupStepSentry(
    enabled: Boolean,
    onEnabledChange: (Boolean) -> Unit,
    onComplete: () -> Unit,
) {
    var showPrivacyDialog by rememberSaveable { mutableStateOf(false) }

    if (showPrivacyDialog) {
        PrivacyPolicyDialog(onDismissRequest = { showPrivacyDialog = false })
    }

    Column(
        modifier =
            Modifier
                .fillMaxSize()
                .padding(24.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        Spacer(Modifier.height(32.dp))
        Icon(
            imageVector = Icons.Default.BugReport,
            contentDescription = null,
            modifier = Modifier.size(64.dp),
            tint = MaterialTheme.colorScheme.primary,
        )
        Spacer(Modifier.height(16.dp))
        Text(
            text = stringResource(R.string.settings_crash_reporting),
            style = MaterialTheme.typography.headlineSmall,
            textAlign = TextAlign.Center,
        )
        Spacer(Modifier.height(8.dp))
        Text(
            text = stringResource(R.string.settings_crash_reporting_subtitle),
            style = MaterialTheme.typography.bodyMedium,
            color = MaterialTheme.colorScheme.onSurfaceVariant,
            textAlign = TextAlign.Center,
        )

        Spacer(Modifier.height(32.dp))

        Card(
            modifier = Modifier.fillMaxWidth(),
            colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceContainerLow),
        ) {
            Column {
                ListItem(
                    headlineContent = { Text(stringResource(R.string.settings_crash_reporting)) },
                    trailingContent = {
                        Switch(
                            checked = enabled,
                            onCheckedChange = onEnabledChange,
                            modifier = Modifier.testTag(TestTags.SETUP_CRASH_REPORTING_TOGGLE),
                        )
                    },
                    colors = ListItemDefaults.colors(containerColor = Color.Transparent),
                )

                TextButton(
                    onClick = { showPrivacyDialog = true },
                    modifier = Modifier.padding(start = 16.dp, bottom = 8.dp).testTag(TestTags.PRIVACY_LEARN_MORE),
                ) {
                    Text(stringResource(R.string.action_learn_more))
                }
            }
        }

        Spacer(Modifier.weight(1f))
        Spacer(Modifier.height(32.dp))

        Button(
            onClick = onComplete,
            modifier = Modifier.fillMaxWidth(),
        ) {
            Icon(Icons.Default.Check, contentDescription = null)
            Spacer(Modifier.width(8.dp))
            Text(stringResource(R.string.setup_complete))
        }
    }
}
