package theorycrafter.ui

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.width
import androidx.compose.material.SnackbarHostState
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.Clipboard
import androidx.compose.ui.platform.LocalClipboard
import compose.utils.getText
import compose.utils.setText
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import theorycrafter.FitExportOptionSettings
import theorycrafter.fits.FitHandle
import theorycrafter.TheorycrafterContext
import theorycrafter.formats.EftExportOptions
import theorycrafter.formats.fitFromEft
import theorycrafter.formats.toEft
import theorycrafter.storage.StoredFit
import theorycrafter.ui.widgets.CheckboxedText
import theorycrafter.ui.widgets.DialogInitiallyFocusedButton
import theorycrafter.ui.widgets.InnerDialog
import theorycrafter.utils.ValueOrError


/**
 * An object that can copy fits to the clipboard.
 */
@Stable
class FitCopier(
    private val clipboard: Clipboard,
    private val snackbarHost: SnackbarHostState,
    private val coroutineScope: CoroutineScope,
) {


    /**
     * The fit handle for which the export options dialog is being shown.
     */
    private var fitHandleToExportWithOptions: FitHandle? by mutableStateOf(null)


    /**
     * Copies the given fit to the clipboard.
     */
    fun copy(fitHandle: FitHandle, options: FitExportOptions? = null) {
        val actualOptions = options ?: TheorycrafterContext.settings.regularFitExportOptions.toFitExportOptions()
        with(TheorycrafterContext.eveData) {
            val fit = TheorycrafterContext.fits.storedFitOf(fitHandle)
            val shipType = shipType(fit.shipTypeId)
            val text = when (actualOptions) {
                is FitExportOptions.Eft -> fit.toEft(actualOptions.eftOptions)
            }
            clipboard.setText(text)
            coroutineScope.launch {
                snackbarHost.showSnackbar("Copied \"${fitHandle.name}\" (${shipType.name}) to clipboard")
            }
        }
    }


    /**
     * Copies the given fit to the clipboard, after letting the user specify export options.
     */
    fun copyWithOptions(fitHandle: FitHandle) {
        fitHandleToExportWithOptions = fitHandle
    }


    /**
     * A composable to put somewhere in order to let the [FitCopier] show the options dialog.
     */
    @Composable
    fun content() {
        fitHandleToExportWithOptions?.let { fitHandle ->
            val settings = TheorycrafterContext.settings
            FitExportOptionsDialog(
                exportOptionSettings = settings.fitExportOptionsDialogValues,
                onExportOptionsChanged = {
                    settings.fitExportOptionsDialogValues = it
                },
                onDismiss = { fitHandleToExportWithOptions = null },
                confirmText = "Copy to Clipboard",
                onConfirm = { copy(fitHandle, it) }
            )
        }
    }


}


/**
 * Creates a remembered [FitCopier] for the calling compose context.
 */
@Composable
fun rememberFitCopier(): FitCopier {
    val clipboard = LocalClipboard.current
    val snackbarHost = LocalSnackbarHost.current
    val coroutineScope = rememberCoroutineScope()

    val fitCopier = remember(clipboard, snackbarHost, coroutineScope) {
        FitCopier(clipboard, snackbarHost, coroutineScope)
    }
    fitCopier.content()

    return fitCopier
}


/**
 * UI for editing [FitExportOptionSettings].
 */
@Composable
fun FitExportOptionsEditor(
    exportOptionSettings: FitExportOptionSettings,
    onExportOptionsChanged: (FitExportOptionSettings) -> Unit,
) {
    val eftExportOptions = exportOptionSettings.eftExportOptions

    fun setEftExportOptions(options: EftExportOptions) {
        onExportOptionsChanged(FitExportOptionSettings(options))
    }

    Column(Modifier.width(IntrinsicSize.Max)) {
        CheckboxedText(
            text = "Loaded Charges",
            checked = eftExportOptions.loadedCharges,
            onCheckedChange = {
                setEftExportOptions(eftExportOptions.copy(loadedCharges = it))
            },
            modifier = Modifier.fillMaxWidth()
        )
        CheckboxedText(
            text = "Implants",
            checked = eftExportOptions.implants,
            onCheckedChange = {
                setEftExportOptions(eftExportOptions.copy(implants = it))
            },
            modifier = Modifier.fillMaxWidth()
        )
        CheckboxedText(
            text = "Boosters",
            checked = eftExportOptions.boosters,
            onCheckedChange = {
                setEftExportOptions(eftExportOptions.copy(boosters = it))
            },
            modifier = Modifier.fillMaxWidth()
        )
        CheckboxedText(
            text = "Cargo",
            checked = eftExportOptions.cargo,
            onCheckedChange = {
                setEftExportOptions(eftExportOptions.copy(cargo = it))
            },
            modifier = Modifier.fillMaxWidth()
        )
        CheckboxedText(
            text = "Mutated Attributes",
            checked = eftExportOptions.mutatedAttributes,
            onCheckedChange = {
                setEftExportOptions(eftExportOptions.copy(mutatedAttributes = it))
            },
            modifier = Modifier.fillMaxWidth()
        )
    }
}


/**
 * The dialog to let the user select fit export options.
 */
@Composable
private fun FitExportOptionsDialog(
    exportOptionSettings: FitExportOptionSettings,
    onExportOptionsChanged: (FitExportOptionSettings) -> Unit,
    onDismiss: () -> Unit,
    confirmText: String,
    onConfirm: (FitExportOptions) -> Unit,
) {

    InnerDialog(
        title = "Include:",
        onDismiss = onDismiss,
        confirmText = confirmText,
        initiallyFocusedButton = DialogInitiallyFocusedButton.Confirm,
        onConfirm = {
            onConfirm(exportOptionSettings.toFitExportOptions())
        }
    ) {
        FitExportOptionsEditor(
            exportOptionSettings = exportOptionSettings,
            onExportOptionsChanged = onExportOptionsChanged
        )
    }
}


/**
 * Fit export options.
 */
sealed class FitExportOptions {

    /**
     * The options when exporting in EFT format.
     */
    class Eft(val eftOptions: EftExportOptions): FitExportOptions()

}


/**
 * Returns the [FitExportOptions] for the given [FitExportOptionSettings].
 */
fun FitExportOptionSettings.toFitExportOptions() = FitExportOptions.Eft(eftExportOptions)


/**
 * Imports a fit from the clipboard and returns its [FitHandle], or an error message.
 */
suspend fun importFitFromClipboard(
    clipboard: Clipboard,
    tags: List<String> = emptyList()
): ValueOrError<FitHandle, String> {
    val text = clipboard.getText()
    if (text == null)
        return ValueOrError.failure("No text in the clipboard")

    var fit = with(TheorycrafterContext.eveData) {
        fitFromEft(text)
    }
    if (fit == null)
        return ValueOrError.failure("Clipboard doesn't contain a valid fit.\nSupported formats: EFT")

    if (tags.isNotEmpty()) {
        fit = fit.copy(fitTimes = StoredFit.FitTimes.Unchanged, tags = fit.tags + tags)
    }

    val fitHandle = TheorycrafterContext.fits.add(fit)
    return ValueOrError.success(fitHandle)
}
