package theorycrafter.ui.widgets

import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.shape.ZeroCornerSize
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material.TextFieldColors
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.VisualTransformation
import compose.widgets.ConstrainedDecimalIntConverter
import compose.widgets.StringConverter
import compose.widgets.ValueTextField
import compose.widgets.rememberAppendSuffixTransformation
import eve.data.asIsk
import eve.data.toDecimalWithPrecisionAtMost
import eve.data.toDecimalWithSignificantDigitsAtMost
import theorycrafter.ui.TextField
import theorycrafter.ui.TheorycrafterTheme
import theorycrafter.ui.tooltip
import theorycrafter.utils.thenIf
import java.text.DecimalFormat


/**
 * A text field that allows inputting an integer value.
 */
@Composable
fun IntTextField(
    value: Int?,
    onValueChange: (Int?) -> Unit,
    range: IntRange? = null,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    readOnly: Boolean = false,
    textStyle: TextStyle = LocalTextStyle.current,
    label: @Composable (() -> Unit)? = null,
    placeholder: @Composable (() -> Unit)? = null,
    leadingIcon: @Composable (() -> Unit)? = null,
    trailingIcon: @Composable (() -> Unit)? = null,
    visualTransformation: VisualTransformation = VisualTransformation.None,
    keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
    keyboardActions: KeyboardActions = KeyboardActions(),
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    shape: Shape =
        MaterialTheme.shapes.small.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize),
    colors: TextFieldColors = TheorycrafterTheme.colors.textFieldColors(),
) {
    ValueTextField(
        value = value,
        onValueChange = onValueChange,
        stringConverter = remember(range) {
            ConstrainedDecimalIntConverter(
                constraint = if (range == null) null else range::contains
            )
        },
        textFieldProvider = { text, onTextChange, _, isError, innerModifier ->
            TheorycrafterTheme.TextField(
                value = text,
                onValueChange = onTextChange,
                modifier = modifier.then(innerModifier),
                isError = isError,
                singleLine = true,
                enabled = enabled,
                readOnly = readOnly,
                textStyle = textStyle,
                label = label,
                placeholder = placeholder,
                leadingIcon = leadingIcon,
                trailingIcon = trailingIcon,
                visualTransformation = visualTransformation,
                keyboardOptions = keyboardOptions,
                keyboardActions = keyboardActions,
                interactionSource = interactionSource,
                shape = shape,
                colors = colors,
            )
        },
    )
}


/**
 * A text field that allows inputting a double value as a decimal.
 */
@Composable
fun DoubleTextField(
    value: Double?,
    onValueChange: (Double?) -> Unit,
    formatter: (Double) -> String = Double::toString,
    constraint: ((Double) -> Boolean)? = null,
    constraintFailureMessage: (String) -> String = { "Value $it is not legal here" },
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    readOnly: Boolean = false,
    textStyle: TextStyle = LocalTextStyle.current,
    label: @Composable (() -> Unit)? = null,
    placeholder: @Composable (() -> Unit)? = null,
    leadingIcon: @Composable (() -> Unit)? = null,
    trailingIcon: @Composable (() -> Unit)? = null,
    visualTransformation: VisualTransformation = VisualTransformation.None,
    keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
    keyboardActions: KeyboardActions = KeyboardActions(),
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    shape: Shape =
        MaterialTheme.shapes.small.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize),
    colors: TextFieldColors = TheorycrafterTheme.colors.textFieldColors(),
) {
    val converter = remember(constraint, formatter) {
        object : StringConverter<Double> {
            override fun valueToString(value: Double): String = formatter(value)
            override fun stringToValue(string: String): Double {
                val number = string.toDoubleOrNull()
                if (number == null)
                    throw IllegalArgumentException("$string is not a valid number")
                if ((constraint != null) && !constraint(number))
                    throw IllegalArgumentException(constraintFailureMessage(string))
                return number
            }
        }
    }

    ValueTextField(
        value = value,
        onValueChange = onValueChange,
        stringConverter = converter,
        textFieldProvider = { text, onTextChange, _, isError, innerModifier ->
            TheorycrafterTheme.TextField(
                value = text,
                onValueChange = onTextChange,
                modifier = modifier.then(innerModifier),
                isError = isError,
                singleLine = true,
                enabled = enabled,
                readOnly = readOnly,
                textStyle = textStyle,
                label = label,
                placeholder = placeholder,
                leadingIcon = leadingIcon,
                trailingIcon = trailingIcon,
                visualTransformation = visualTransformation,
                keyboardOptions = keyboardOptions,
                keyboardActions = keyboardActions,
                interactionSource = interactionSource,
                shape = shape,
                colors = colors,
            )
        },
    )
}


/**
 * A text field for inputting a ISK amount.
 */
@Composable
fun IskTextField(
    value: Double?,
    onValueChange: (Double?) -> Unit,
    constraint: ((Double) -> Boolean)? = null,
    constraintFailureMessage: (String) -> String = { "Value $it is not legal here" },
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    readOnly: Boolean = false,
    textStyle: TextStyle = LocalTextStyle.current,
    label: @Composable (() -> Unit)? = null,
    placeholder: @Composable (() -> Unit)? = null,
    leadingIcon: @Composable (() -> Unit)? = null,
    trailingIcon: @Composable (() -> Unit)? = null,
    keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
    keyboardActions: KeyboardActions = KeyboardActions(),
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    shape: Shape =
        MaterialTheme.shapes.small.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize),
    colors: TextFieldColors = TheorycrafterTheme.colors.textFieldColors(),
    showIskSuffix: Boolean = true
) {
    val visualTransformation = if (!showIskSuffix || (value == null))
        VisualTransformation.None
    else
        rememberAppendSuffixTransformation { " ISK" }
    val decimalFormat = remember { DecimalFormat("#,###.##") }
    val converter = remember(constraint) {
        object : StringConverter<Double> {
            override fun valueToString(value: Double): String = decimalFormat.format(value)
            override fun stringToValue(string: String): Double {
                return string.filter { it != ',' }.toDoubleOrNull()
                    ?: throw IllegalArgumentException("$string is not a valid number")
            }
        }
    }

    ValueTextField(
        value = value,
        onValueChange = onValueChange,
        stringConverter = converter,
        textFieldProvider = { text, onTextChange, innerValue, isError, innerModifier ->
            val constraintErrorMessage = if ((constraint != null) && (innerValue != null) && !constraint(innerValue))
                constraintFailureMessage(text)
            else
                null
            TheorycrafterTheme.TextField(
                value = text,
                onValueChange = onTextChange,
                modifier = modifier
                    .then(innerModifier)
                    .thenIf(!isError) {
                        if (constraintErrorMessage != null)
                            tooltip(constraintErrorMessage)
                        else if (innerValue != null)
                            tooltip(innerValue.asIsk())
                        else
                            Modifier
                    },
                isError = isError || constraintErrorMessage != null,
                singleLine = true,
                enabled = enabled,
                readOnly = readOnly,
                textStyle = textStyle,
                label = label,
                placeholder = placeholder,
                leadingIcon = leadingIcon,
                trailingIcon = trailingIcon,
                visualTransformation = visualTransformation,
                keyboardOptions = keyboardOptions,
                keyboardActions = keyboardActions,
                interactionSource = interactionSource,
                shape = shape,
                colors = colors,
            )
        },
    )
}


/**
 * Returns a function that formats a [Double] to a [String] with the given maximum precision.
 */
@Stable
fun maxPrecisionFormatter(precision: Int): (Double) -> String = { it.toDecimalWithPrecisionAtMost(precision) }


/**
 * Returns a function that formats a [Double] to a [String] with the given number of max. significant digits.
 */
@Stable
fun maxSignificantDigitsFormatter(digits: Int): (Double) -> String = { it.toDecimalWithSignificantDigitsAtMost(digits) }
