package theorycrafter.ui

import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsFocusedAsState
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.onClick
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.input.pointer.PointerIcon
import androidx.compose.ui.input.pointer.pointerHoverIcon
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp
import compose.input.KeyShortcut
import compose.input.onKeyShortcut
import compose.utils.HSpacer
import compose.utils.VerticallyCenteredRow
import compose.utils.focusWhenClicked
import compose.utils.requestInitialFocus
import compose.widgets.*
import eve.data.ShipType
import kotlinx.coroutines.launch
import theorycrafter.fits.FitHandle
import theorycrafter.TheorycrafterContext
import theorycrafter.ui.fiteditor.isShipLegal
import theorycrafter.ui.widgets.InnerDialog
import theorycrafter.utils.ItemSelectionTextFieldProvider
import theorycrafter.utils.ShipSelectionField
import theorycrafter.utils.thenIf
import theorycrafter.utils.withIndexRemoved


/**
 * A dialog for creating a new fit.
 */
@Composable
fun NewFitDialog(
    onDismiss: () -> Unit,
    forcedShipType: ShipType? = null,
    initialName: String = "",
    initialTags: List<String> = emptyList(),
    createNewFit: (shipType: ShipType, fitName: String, tags: List<String>) -> Unit,
){
    var fitNameFieldValue by remember {
        mutableStateOf(
            TextFieldValue(
                text = initialName,
                selection = TextRange(0, initialName.length)
            )
        )
    }
    var selectedShipType: ShipType? by remember { mutableStateOf(forcedShipType) }
    var tags by remember { mutableStateOf(initialTags) }

    val canCreateNewFit by remember {
        derivedStateOf {
            (selectedShipType != null) && fitNameFieldValue.text.isNotBlank()
        }
    }

    fun onConfirm(){
        if (!canCreateNewFit)
            return

        val actualShipType = selectedShipType ?: return
        createNewFit.invoke(actualShipType, fitNameFieldValue.text, tags)
    }

    InnerDialog(
        title = "New Fit",
        confirmText = "Create",
        onDismiss = onDismiss,
        onConfirm = ::onConfirm,
        confirmEnabled = canCreateNewFit
    ) {
        Column(
            modifier = Modifier.width(280.dp),  // Must fit "Miasmos Quafe Ultramarine Edition"
            verticalArrangement = Arrangement.spacedBy(TheorycrafterTheme.spacing.small)
        ) {
            val fitNameFocusRequester = remember { FocusRequester() }
            val shipTypeFocusRequester = remember { FocusRequester() }

            ShipSelectionField(
                readOnly = forcedShipType != null,
                initialShipType = forcedShipType,
                onShipSelected = { selectedShipType = it },
                onSuggestedShipSelected = { fitNameFocusRequester.requestFocus() },
                modifier = Modifier
                    .fillMaxWidth()
                    .focusRequester(shipTypeFocusRequester),
                textFieldProvider = ItemSelectionTextFieldProvider.OutlinedTextField(
                    label = { Text("Ship Type") },
                )
            )
            TheorycrafterTheme.OutlinedTextField(
                value = fitNameFieldValue,
                onValueChange = { fitNameFieldValue = it },
                label = { Text("Fit Name") },
                singleLine = true,
                modifier = Modifier
                    .onKeyShortcut(KeyShortcut.anyEnter(), onPreview = true) {
                        if (canCreateNewFit) {
                            onConfirm()
                            onDismiss()
                        }
                    }
                    .fillMaxWidth()
                    .focusRequester(fitNameFocusRequester)
            )
            LaunchedEffect(Unit) {
                if (forcedShipType == null)
                    shipTypeFocusRequester.requestFocus()
                else
                    fitNameFocusRequester.requestFocus()
            }

            selectedShipType?.let {
                if (!TheorycrafterContext.tournaments.activeRules.isShipLegal(it)) {
                    Text(
                        text = "${it.name} is illegal in ${TheorycrafterContext.tournaments.activeTournamentDescriptor!!.shortName}",
                        style = LocalTextStyle.current.copy(color = TheorycrafterTheme.colors.base().errorContent)
                    )
                }
            }

            FitTagEditor(
                tags = tags,
                onTagsChanged = { tags = it },
                addTagLabel = "Fit Tag (optional)",
                modifier = Modifier
                    .fillMaxWidth()
                    .height(180.dp)
            )
        }
    }
}


/**
 * UI for specifying and editing the tags of a fit.
 */
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun FitTagEditor(
    tags: List<String>,
    addTagLabel: String,
    onTagsChanged: (List<String>) -> Unit,
    requestFocus: Boolean = false,
    modifier: Modifier = Modifier
) {
    var inputText by remember { mutableStateOf("") }
    fun isInputTagValid() = inputText.none { it.isWhitespace() }

    fun addInputTextAsTag() {
        if (inputText.isNotEmpty() && isInputTagValid()) {
            onTagsChanged(tags + inputText)
            inputText = ""
        }
    }

    Column(
        modifier = modifier
            .sizeIn(minWidth = 120.dp, minHeight = 100.dp),
        verticalArrangement = Arrangement.spacedBy(TheorycrafterTheme.spacing.medium)
    ) {
        // The tag input field
        val prependHashTag = rememberPrependPrefixTransformation { "#" }
        val tagNameInteractionSource = remember { MutableInteractionSource() }
        val isFocused by tagNameInteractionSource.collectIsFocusedAsState()
        TheorycrafterTheme.OutlinedTextField(
            value = inputText,
            onValueChange = { inputText = it },
            modifier = Modifier
                .fillMaxWidth()
                .padding(bottom = 8.dp)
                .thenIf(requestFocus) { requestInitialFocus() }
                .onKeyShortcut(KeyShortcut.anyEnter(), onPreview = true) {
                    addInputTextAsTag()
                },
            label = { Text(addTagLabel) },
            trailingIcon = {
                val enabled = inputText.isNotEmpty() && isInputTagValid()
                Icons.AddTag(
                    Modifier
                        .onClick(enabled = enabled) {
                            addInputTextAsTag()
                        }
                        .thenIf(enabled) {
                            tooltip("Add #$inputText")
                        }
                        .pointerHoverIcon(PointerIcon.Default)
                        .textFieldTrailingIconPadding()
                )
            },
            isError = !isInputTagValid(),
            singleLine = true,
            interactionSource = tagNameInteractionSource,
            visualTransformation = if (isFocused) prependHashTag else VisualTransformation.None,
        )

        // The list of tags
        FitTagList(
            tags = tags,
            onTagsChanged = onTagsChanged,
            modifier = Modifier
                .weight(1f)
                .fillMaxWidth()
        )
    }
}


/**
 * The list of fit tags.
 */
@Composable
private fun FitTagList(
    tags: List<String>,
    onTagsChanged: (List<String>) -> Unit,
    modifier: Modifier = Modifier,
) {
    val listState = rememberLazyListState()
    Box(modifier) {
        if (tags.isEmpty()) {
            Text("No tags specified", Modifier.align(Alignment.Center))
        } else {
            val selectionModel = rememberSingleItemSelectionModel(selectableRange = tags.indices)
            val tagListInteractionSource = remember { MutableInteractionSource() }
            LazyColumnExt(
                state = listState,
                selection = selectionModel,
                showScrollShadows = true,
                modifier = Modifier
                    .fillMaxSize()
                    .roundedFocusBorder(interactionSource = tagListInteractionSource)
                    .focusWhenClicked(interactionSource = tagListInteractionSource)
                    .moveSelectionWithKeys(selectionModel)
                    .onKeyShortcut(KeyShortcut.DeleteItem) {
                        selectionModel.selectedIndex?.let {
                            onTagsChanged(tags.withIndexRemoved(it))
                        }
                    }
            ) {
                items(tags.size) { index ->
                    val tag = tags[index]
                    key(tag, index) {
                        FitTag(
                            text = tag,
                            onDelete = { onTagsChanged(tags.withIndexRemoved(index)) },
                        )
                    }
                }
            }

            LaunchedEffect(tags.size) {
                listState.animateScrollToItem(tags.size-1)
            }
        }
    }
}


/**
 * An item in the list of fit tags.
 */
@Composable
private fun FitTag(
    text: String,
    onDelete: () -> Unit,
) {
    val iconPadding = TheorycrafterTheme.spacing.xxsmall
    VerticallyCenteredRow(
        modifier = Modifier
            .padding(
                start = TheorycrafterTheme.spacing.large,
                end = TheorycrafterTheme.spacing.large - iconPadding
            )
    ) {
        SingleLineText(
            text = "#$text",
            modifier = Modifier
                .weight(1f)
                .padding(vertical = TheorycrafterTheme.spacing.small),
        )

        HSpacer(TheorycrafterTheme.spacing.medium)

        IconButton(onClick = onDelete) {
            Icons.Delete(
                modifier = Modifier
                    .tooltip("Remove #$text")
                    .padding(iconPadding)
                    .size(16.dp)
            )
        }
    }
}


/**
 * The dialog for editing a fit's tags.
 */
@Composable
fun FitTagEditorDialog(
    fitHandle: FitHandle,
    onDismiss: () -> Unit,
) {
    val storedFit = fitHandle.storedFit ?: return
    val shipType = TheorycrafterContext.eveData.shipType(fitHandle.shipTypeId)
    val coroutineScope = rememberCoroutineScope()
    InnerDialog(
        title = "${fitHandle.name} (${shipType.shortName()})",
        confirmText = "Close",
        dismissText = null,
        onConfirm = onDismiss,
        onDismiss = onDismiss,
    ) {
        FitTagEditor(
            modifier = Modifier
                .size(240.dp, 200.dp),
            requestFocus = true,
            addTagLabel = "Add Tag",
            tags = storedFit.tags,
            onTagsChanged = { tags ->
                coroutineScope.launch {
                    TheorycrafterContext.fits.setTags(fitHandle, tags)
                }
            }
        )
    }
}
