package com.pixelized.biblib.ui.screen.authentication

import android.content.res.Configuration
import androidx.activity.compose.BackHandler
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.Image
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.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.SnackbarResult
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.BlurredEdgeTreatment
import androidx.compose.ui.draw.blur
import androidx.compose.ui.focus.FocusManager
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.biblib.R
import com.pixelized.biblib.network.client.IBibLibClient
import com.pixelized.biblib.ui.LocalSnackHostState
import com.pixelized.biblib.ui.navigation.LocalScreenNavHostController
import com.pixelized.biblib.ui.navigation.navigateToHome
import com.pixelized.biblib.ui.screen.authentication.composable.CredentialRemember
import com.pixelized.biblib.ui.screen.authentication.composable.LoginField
import com.pixelized.biblib.ui.screen.authentication.composable.PasswordField
import com.pixelized.biblib.ui.screen.authentication.composable.googleStringResource
import com.pixelized.biblib.ui.screen.authentication.viewModel.AuthenticationFormViewModel
import com.pixelized.biblib.ui.screen.authentication.viewModel.AuthenticationViewModel
import com.pixelized.biblib.ui.theme.BibLibTheme
import com.pixelized.biblib.ui.theme.color.ShadowPalette
import com.pixelized.biblib.utils.extention.bibLib
import com.pixelized.biblib.utils.extention.modifier.unclickable
import kotlinx.coroutines.launch

@Stable
@Immutable
sealed class AuthenticationUio {
    @Stable
    @Immutable
    object Progress : AuthenticationUio()

    @Stable
    @Immutable
    object Done : AuthenticationUio()
}

@Stable
@Immutable
sealed class AuthenticationErrorUio(
    @StringRes val message: Int = R.string.error_authentication_message,
    @StringRes val action: Int = R.string.error_authentication_action,
) {
    @Stable
    @Immutable
    class Login : AuthenticationErrorUio()

    @Stable
    @Immutable
    class GoogleLogin : AuthenticationErrorUio()
}

@Composable
fun AuthenticationScreen(
    authenticationViewModel: AuthenticationViewModel = hiltViewModel(),
    formViewModel: AuthenticationFormViewModel = hiltViewModel(),
) {
    val navHostController = LocalScreenNavHostController.current
    val snackBarHostState = LocalSnackHostState.current
    val uriHandler = LocalUriHandler.current
    val context = LocalContext.current
    val focusManager = LocalFocusManager.current
    val scope = rememberCoroutineScope()

    val background = rememberSaveable {
        val tmp = listOf(
            R.drawable.biblib_background_1,
            R.drawable.biblib_background_2,
            R.drawable.biblib_background_3,
        )
        mutableIntStateOf(tmp.random())
    }
    val displayBibLibSignIn = rememberSaveable { mutableStateOf(false) }
    val blur = remember(displayBibLibSignIn) {
        derivedStateOf { if (displayBibLibSignIn.value) 8.dp else 0.dp }
    }

    Surface(
        modifier = Modifier.fillMaxSize(),
    ) {
        AuthenticationScreenContent(
            modifier = Modifier.fillMaxSize(),
            background = background.intValue,
            blur = blur,
            onGoogleSignIn = authenticationViewModel::loginWithGoogle,
            onBibLibSingIn = { displayBibLibSignIn.value = true },
        )

        AuthenticationSignDialog(
            focusManager = focusManager,
            visibility = displayBibLibSignIn,
            login = formViewModel.form.login,
            password = formViewModel.form.password,
            rememberPassword = formViewModel.form.remember,
            onLoginChange = formViewModel::onLoginChange,
            onPasswordChange = formViewModel::onPasswordChange,
            onRememberPasswordChange = formViewModel::onRememberChange,
            onSignIn = {
                authenticationViewModel.login(
                    login = formViewModel.form.login,
                    password = formViewModel.form.password,
                )
            },
            onRegister = {
                uriHandler.openUri(uri = IBibLibClient.REGISTER_URL)
            },
            onDismissRequest = {
                displayBibLibSignIn.value = false
            },
        )
    }

    BackHandler(
        enabled = displayBibLibSignIn.value
    ) {
        displayBibLibSignIn.value = false
    }

    AuthenticationHandler(onFailure = {
        scope.launch {
            val result = snackBarHostState.showSnackbar(
                message = context.getString(it.message),
                actionLabel = context.getString(it.action),
            )
            if (result == SnackbarResult.ActionPerformed) {
                when (it) {
                    is AuthenticationErrorUio.GoogleLogin -> {
                        authenticationViewModel.loginWithGoogle()
                    }

                    is AuthenticationErrorUio.Login -> {
                        authenticationViewModel.login(
                            login = formViewModel.form.login,
                            password = formViewModel.form.password,
                        )
                    }
                }
            }
        }
    }, onSuccess = {
        navHostController.navigateToHome()
    })

    AuthenticationProgress()
}

@Composable
private fun AuthenticationScreenContent(
    modifier: Modifier = Modifier,
    blur: State<Dp>,
    @DrawableRes background: Int,
    onGoogleSignIn: () -> Unit,
    onBibLibSingIn: () -> Unit,
) {
    val colorScheme = MaterialTheme.colors
    val animatedBlur = animateDpAsState(
        targetValue = blur.value,
        label = "AnimatedContentBlur",
    )

    Column(
        modifier = modifier.blur(
            radius = animatedBlur.value,
            edgeTreatment = BlurredEdgeTreatment.Unbounded,
        ),
    ) {
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .weight(1f),
        ) {
            Image(
                modifier = Modifier.matchParentSize(),
                contentScale = ContentScale.FillHeight,
                painter = painterResource(id = background),
                contentDescription = null,
            )
            Box(
                modifier = Modifier
                    .matchParentSize()
                    .background(brush = remember {
                        Brush.verticalGradient(
                            listOf(
                                colorScheme.surface.copy(alpha = 0.0f),
                                colorScheme.surface.copy(alpha = 0.0f),
                                colorScheme.surface.copy(alpha = 0.0f),
                                colorScheme.surface.copy(alpha = 0.3f),
                                colorScheme.surface.copy(alpha = 1.0f),
                            )
                        )
                    }),
            )
        }
        Box(
            modifier = Modifier.navigationBarsPadding(),
        ) {
            Column(
                verticalArrangement = Arrangement.spacedBy(
                    space = 8.dp, alignment = Alignment.Bottom
                ),
            ) {
                Button(
                    modifier = Modifier
                        .padding(top = 16.dp)
                        .padding(horizontal = 16.dp)
                        .fillMaxWidth()
                        .height(height = 48.dp),
                    colors = ButtonDefaults.outlinedButtonColors(),
                    onClick = onGoogleSignIn,
                ) {
                    Text(text = googleStringResource())
                }
                TextButton(
                    modifier = Modifier
                        .padding(bottom = 16.dp)
                        .padding(horizontal = 16.dp)
                        .fillMaxWidth()
                        .height(height = 48.dp),
                    colors = ButtonDefaults.textButtonColors(),
                    onClick = onBibLibSingIn,
                ) {
                    Text(
                        maxLines = 1,
                        overflow = TextOverflow.Ellipsis,
                        text = stringResource(id = R.string.authentication_sign_in_with_biblib),
                    )
                }
            }
        }
    }
}

@Composable
private fun AuthenticationSignDialog(
    focusManager: FocusManager = LocalFocusManager.current,
    visibility: State<Boolean>,
    login: String,
    onLoginChange: (String) -> Unit,
    password: String,
    onPasswordChange: (String) -> Unit,
    rememberPassword: Boolean,
    onRememberPasswordChange: (Boolean) -> Unit,
    onSignIn: () -> Unit,
    onRegister: () -> Unit,
    onDismissRequest: () -> Unit,
) {
    val density = LocalDensity.current
    val colorScheme = MaterialTheme.colors
    val slideOffset = remember { with(density) { 16.dp.roundToPx() } }

    Box(
        modifier = Modifier
            .unclickable(disable = visibility.value)
            .fillMaxSize()
            .background(
                color = animateColorAsState(
                    targetValue = when (visibility.value) {
                        true -> colorScheme.surface.copy(alpha = 0.5f)
                        else -> Color.Transparent
                    },
                    label = "AnimatedSignInScrim"
                ).value
            ),
    ) {
        AnimatedVisibility(
            modifier = Modifier.fillMaxSize(),
            enter = fadeIn() + slideInVertically { slideOffset },
            exit = fadeOut() + slideOutVertically { slideOffset },
            visible = visibility.value,
        ) {
            Box(
                modifier = Modifier.fillMaxSize(),
            ) {
                IconButton(
                    modifier = Modifier
                        .statusBarsPadding()
                        .align(alignment = Alignment.TopEnd),
                    onClick = onDismissRequest,
                ) {
                    Icon(
                        imageVector = Icons.Default.Close,
                        contentDescription = null,
                    )
                }
                Column(
                    modifier = Modifier.align(alignment = Alignment.Center),
                    verticalArrangement = Arrangement.spacedBy(
                        space = 24.dp,
                        alignment = Alignment.CenterVertically,
                    ),
                ) {
                    Column(
                        modifier = Modifier.padding(horizontal = 16.dp),
                    ) {
                        LoginField(
                            modifier = Modifier.fillMaxWidth(),
                            value = login,
                            onValueChange = onLoginChange,
                            keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
                        )
                        PasswordField(
                            modifier = Modifier.fillMaxWidth(),
                            value = password,
                            onValueChange = onPasswordChange,
                            keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
                            keyboardActions = KeyboardActions { focusManager.clearFocus() },
                        )
                    }
                    CredentialRemember(
                        modifier = Modifier.padding(start = 24.dp, end = 16.dp),
                        value = rememberPassword,
                        onValueChange = onRememberPasswordChange,
                    )
                    Row(
                        modifier = Modifier
                            .fillMaxWidth()
                            .padding(horizontal = 16.dp),
                        horizontalArrangement = Arrangement.spacedBy(
                            space = 16.dp, alignment = Alignment.End
                        ),
                    ) {
                        TextButton(
                            modifier = Modifier.height(height = 48.dp),
                            colors = ButtonDefaults.textButtonColors(),
                            onClick = onRegister,
                        ) {
                            Text(text = stringResource(id = R.string.action_register))
                        }
                        Button(
                            modifier = Modifier.height(height = 48.dp),
                            colors = ButtonDefaults.buttonColors(),
                            onClick = onSignIn,
                        ) {
                            Text(text = stringResource(id = R.string.action_login))
                        }
                    }
                }
            }
        }
    }
}

@Composable
private fun AuthenticationHandler(
    viewModel: AuthenticationViewModel = hiltViewModel(),
    onFailure: (AuthenticationErrorUio) -> Unit = { },
    onSuccess: (AuthenticationUio.Done) -> Unit = { },
) {
    viewModel.PrepareLoginWithGoogle()

    val state = viewModel.authenticationProcess
    LaunchedEffect(key1 = state.value) {
        when (val authentication = state.value) {
            is AuthenticationUio.Done -> onSuccess(authentication)
            else -> Unit
        }
    }

    LaunchedEffect("AuthenticationHandlerError") {
        viewModel.authenticationError.collect {
            onFailure(it)
        }
    }
}

@Composable
fun AuthenticationProgress(
    viewModel: AuthenticationViewModel = hiltViewModel(),
) {
    AnimatedVisibility(
        visible = viewModel.isLoading,
        enter = fadeIn(),
        exit = fadeOut(),
    ) {
        Box(
            modifier = Modifier
                .fillMaxSize()
                .background(color = ShadowPalette.scrim),
            contentAlignment = Alignment.Center,
        ) {
            CircularProgressIndicator(
                modifier = Modifier.size(MaterialTheme.bibLib.dimen.progress.iconSize),
            )
        }
    }
}

@Composable
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun AuthenticationScreenContentPreview(
    @PreviewParameter(AuthenticationPreviewProvider::class) preview: State<Boolean>,
) {
    BibLibTheme {
        Surface {
            AuthenticationScreenContent(
                modifier = Modifier.fillMaxSize(),
                blur = remember { mutableStateOf(if (preview.value) 8.dp else 0.dp) },
                background = R.drawable.biblib_background_3,
                onGoogleSignIn = { },
                onBibLibSingIn = { },
            )
            AuthenticationSignDialog(
                visibility = preview,
                login = "",
                onLoginChange = { },
                password = "",
                onPasswordChange = { },
                rememberPassword = true,
                onRememberPasswordChange = { },
                onSignIn = { },
                onRegister = { },
                onDismissRequest = { },
            )
        }
    }
}

private class AuthenticationPreviewProvider : PreviewParameterProvider<State<Boolean>> {
    override val values: Sequence<State<Boolean>> = sequenceOf(
        mutableStateOf(false),
        mutableStateOf(true),
    )
}