package theorycrafter.ui.widgets

import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsFocusedAsState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.onClick
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.pointer.*
import androidx.compose.ui.text.input.TextFieldValue
import compose.input.KeyShortcut
import compose.input.onKeyShortcut
import compose.utils.LocalKeyShortcutsManager
import compose.widgets.SingleLineText
import theorycrafter.ui.Icons
import theorycrafter.ui.OutlinedTextField
import theorycrafter.ui.TheorycrafterTheme
import theorycrafter.ui.textFieldTrailingIconPadding
import theorycrafter.utils.withAllTextSelected


/**
 * A text field for searching/filtering.
 */
@OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class)
@Composable
fun SearchField(
    modifier: Modifier,
    textFieldModifier: Modifier = Modifier,
    searchText: String? = null,
    placeholderText: String,
    focusKeyShortcut: KeyShortcut? = null,
    onSearched: (String) -> Unit
) {
    var textFieldValue by remember { mutableStateOf(TextFieldValue()) }.also {
        // Allow clearing the text from outside
        if ((searchText != null) && (it.value.text != searchText))
            it.value = TextFieldValue(searchText)
    }

    val focusRequester = remember { FocusRequester() }
    val interactionSource = remember { MutableInteractionSource() }

    fun clearSearch() {
        if (textFieldValue.text.isNotEmpty()) {
            textFieldValue = TextFieldValue()
            onSearched("")
        }
    }

    val isFocused by interactionSource.collectIsFocusedAsState()

    // Selects the text on mouse-release
    var selectAllOnRelease by remember { mutableStateOf(false) }
    val selectAllOnReleaseModifier = Modifier
        .onPointerEvent(PointerEventType.Press, pass = PointerEventPass.Initial) {
            selectAllOnRelease = !isFocused
        }
        .onPointerEvent(PointerEventType.Move) {
            // This should take mouse slop into account, but we can't easily because onDrag and onClick are consumed
            // inside the textfield.
            selectAllOnRelease = false
        }
        .onPointerEvent(PointerEventType.Release, pass = PointerEventPass.Final) {
            if (selectAllOnRelease) {
                textFieldValue = textFieldValue.withAllTextSelected()
            }
        }

    // We need to apply modifier here and not on the SearchField, because sizing and padding modifiers need to be
    // outside the TextFieldScaleHack
    Box(modifier = modifier, propagateMinConstraints = true) {
        TheorycrafterTheme.OutlinedTextField(
            value = textFieldValue,
            onValueChange = {
                val textChanged = it.text != textFieldValue.text
                textFieldValue = it
                if (textChanged)
                    onSearched(it.text)
            },
            modifier = textFieldModifier
                .focusRequester(focusRequester)
                .onKeyShortcut(Key.Escape) { clearSearch() }
                .then(selectAllOnReleaseModifier),
            leadingIcon = {
                Icons.Search(
                    contentDescription = "Search",
                    modifier = Modifier
                        .padding(
                            start = TheorycrafterTheme.spacing.largeish,
                            end = TheorycrafterTheme.spacing.xxsmall
                        )
                )
            },
            interactionSource = interactionSource,
            trailingIcon = {
                if (textFieldValue.text.isNotEmpty()) {
                    Icons.Cancel(
                        modifier = Modifier
                            .onClick {
                                clearSearch()
                                focusRequester.requestFocus()
                            }
                            .pointerHoverIcon(PointerIcon.Default)
                            .textFieldTrailingIconPadding()
                    )
                } else if (!isFocused && (focusKeyShortcut != null)) {
                    Text(
                        text = focusKeyShortcut.toString(),
                        style = LocalTextStyle.current.let {
                            it.copy(
                                fontSize = it.fontSize * 0.8
                            )
                        },
                        modifier = Modifier
                            .textFieldTrailingIconPadding()
                    )
                }
            },
            placeholder = { SingleLineText(placeholderText) },
            singleLine = true,
            colors = TheorycrafterTheme.colors.textFieldColors(
                unfocusedIndicatorColor = Color.Transparent,
            )
        )
    }

    // This doesn't work due to https://youtrack.jetbrains.com/issue/CMP-1613/Unable-to-select-in-TextField-on-focus,
    // and maybe in general isn't such a good idea because we don't want to select all on mouse-down; we want it on
    // tap/click, to avoid interfering with the user to selecting text himself.
//    interactionSource.onGainedFocus {
//        textFieldValue = textFieldValue.withAllTextSelected()
//    }

    if (focusKeyShortcut != null) {
        LocalKeyShortcutsManager.current.register(focusKeyShortcut) {
            focusRequester.requestFocus()
            textFieldValue = textFieldValue.withAllTextSelected()
        }
    }
}