// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.jetbrains.services.caws

import com.intellij.openapi.extensions.ExtensionNotApplicableException
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.rd.util.launchChildOnUi
import com.intellij.openapi.rd.util.launchIOBackground
import com.intellij.openapi.rd.util.launchUnderBackgroundProgress
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.util.text.nullize
import com.intellij.util.ui.UIUtil
import com.jetbrains.ide.model.uiautomation.BeAlignment
import com.jetbrains.ide.model.uiautomation.BeControl
import com.jetbrains.ide.model.uiautomation.BeSizingType
import com.jetbrains.ide.model.uiautomation.UpdateSourceTrigger
import com.jetbrains.rd.ui.bedsl.button
import com.jetbrains.rd.ui.bedsl.dsl.combobox
import com.jetbrains.rd.ui.bedsl.dsl.getText
import com.jetbrains.rd.ui.bedsl.dsl.horizontalGrid
import com.jetbrains.rd.ui.bedsl.dsl.label
import com.jetbrains.rd.ui.bedsl.dsl.replaceWith
import com.jetbrains.rd.ui.bedsl.dsl.textBox
import com.jetbrains.rd.ui.bedsl.dsl.verticalGrid
import com.jetbrains.rd.ui.bedsl.dsl.withColor
import com.jetbrains.rd.ui.bedsl.dsl.withMargin
import com.jetbrains.rd.ui.bedsl.link
import com.jetbrains.rd.util.lifetime.Lifetime
import com.jetbrains.rdserver.unattendedHost.customization.controlCenter.GatewayControlCenterTabProvider
import software.amazon.awssdk.services.codecatalyst.CodeCatalystClient
import software.aws.toolkits.core.utils.error
import software.aws.toolkits.core.utils.getLogger
import software.aws.toolkits.jetbrains.core.awsClient
import software.aws.toolkits.jetbrains.core.credentials.sono.SonoCredentialManager
import software.aws.toolkits.jetbrains.core.credentials.sono.lazilyGetUserId
import software.aws.toolkits.jetbrains.services.caws.envclient.CawsEnvironmentClient
import software.aws.toolkits.jetbrains.utils.notifyError
import software.aws.toolkits.resources.message
import software.aws.toolkits.telemetry.CodecatalystTelemetry
import software.aws.toolkits.telemetry.CodecatalystUpdateDevEnvironmentLocationType
import java.nio.file.Path
import java.time.Duration
import software.aws.toolkits.telemetry.Result as TelemetryResult

class UpdateWorkspaceSettingsTab : GatewayControlCenterTabProvider {
    init {
        if (System.getenv(CawsConstants.CAWS_ENV_ID_VAR) == null) {
            throw ExtensionNotApplicableException.create()
        }
    }

    private val project: Project by lazy {
        ProjectManager.getInstance().defaultProject
    }

    override val id: String
        get() = "caws.configureWorkspaceTab"
    override val title: String
        get() = message("caws.configure_workspace_tab_title")

    override fun getControl(lifetime: Lifetime): BeControl = verticalGrid {
        row {
            label(message("loading_resource.loading"))
        }
    }.also {
        lifetime.launchIOBackground {
            try {
                val connection = SonoCredentialManager.getInstance(project).getConnectionSettings()
                    ?: error("Failed to fetch connection settings from Dev Environment")
                val envId = System.getenv(CawsConstants.CAWS_ENV_ID_VAR) ?: error("envId env var null")
                val org = System.getenv(CawsConstants.CAWS_ENV_ORG_NAME_VAR) ?: error("space env var null")
                val projectName = System.getenv(CawsConstants.CAWS_ENV_PROJECT_NAME_VAR) ?: error("project env var null")

                val client = connection.awsClient<CodeCatalystClient>()
                val initialEnv = client.getDevEnvironment {
                    it.id(envId)
                    it.spaceName(org)
                    it.projectName(projectName)
                }

                val isFree = isSubscriptionFreeTier(client, org)

                val alias = textBox(lifetime, false, "caws.development.workspace.alias", UpdateSourceTrigger.TextChanged)
                initialEnv.alias()?.let { alias.text.set(it) }

                val timeout = InactivityTimeout.DEFAULT_VALUES.toList()
                val instanceSize = loadParameterDescriptions().environmentParameters.instanceTypes.keys.let { types ->
                    if (isFree) {
                        types.filter { it.isSupportedInFreeTier() }
                    } else {
                        types
                    }
                }.toList()

                var timeoutSelected = initialEnv.inactivityTimeoutMinutes()
                var instanceSizeSelected = initialEnv.instanceType()

                launchChildOnUi {
                    it.replaceWith(
                        verticalGrid {
                            row {
                                horizontalGrid {
                                    column {
                                        link(message("caws.open.devfile"), lifetime) {
                                            val project = inferActiveProject()
                                            try {
                                                val devfileLocation = CawsEnvironmentClient.getInstance().getStatus().location ?: "devfile.yaml"
                                                // The path returned by getStatus() is relative to /projects
                                                val devfilePath = VirtualFileManager.getInstance().findFileByNioPath(
                                                    Path.of("${CawsConstants.CAWS_ENV_PROJECT_DIR}/$devfileLocation")
                                                )
                                                if (devfilePath != null) {
                                                    FileEditorManager.getInstance(project).openFile(devfilePath, true)
                                                }
                                            } catch (e: Exception) {
                                                val failedToOpenDevfile = message("caws.open.devfile.failed")
                                                LOG.error(e) { failedToOpenDevfile }
                                                notifyError(failedToOpenDevfile, "$failedToOpenDevfile: ${e.message}", project)
                                            }
                                        }
                                    }
                                }
                            }

                            row {
                                horizontalGrid {
                                    column {
                                        label(message("caws.workspace.details.alias.label"))
                                    }
                                    column {
                                        alias
                                    }
                                }
                            }

                            row {
                                horizontalGrid {
                                    column {
                                        label(message("caws.workspace.details.inactivity_timeout"))
                                    }
                                    column {
                                        combobox(
                                            lifetime,
                                            timeout,
                                            selectedValue = InactivityTimeout(Duration.ofMinutes(initialEnv.inactivityTimeoutMinutes().toLong())),
                                            handleSelected = {
                                                timeoutSelected = it.asMinutes()
                                            },
                                            presentation = {
                                                label(it.displayText())
                                            }
                                        )
                                    }
                                }
                            }

                            row {
                                horizontalGrid {
                                    column {
                                        label(message("caws.workspace.instance_size"))
                                    }

                                    column {
                                        combobox(lifetime, instanceSize, selectedValue = initialEnv.instanceType(), handleSelected = {
                                            instanceSizeSelected = it
                                        }, presentation = {
                                            // TODO: Velox to provide API for this info
                                            label(it.toString().substringAfter("dev.standard1.").capitalize())
                                        })
                                    }
                                }
                            }

                            row(BeSizingType.Fit, BeAlignment.Right) {
                                button(message("caws.configure_workspace_tab_save_button"), lifetime) {
                                    lifetime.launchIOBackground buttonAction@{
                                        if (initialEnv.instanceType() == instanceSizeSelected &&
                                            initialEnv.alias().nullize() == alias.getText().nullize() &&
                                            initialEnv.inactivityTimeoutMinutes() == timeoutSelected
                                        ) {
                                            // noop
                                            return@buttonAction
                                        }

                                        var result = TelemetryResult.Succeeded
                                        try {
                                            lifetime.launchUnderBackgroundProgress(message("caws.update_dev_environment")) {
                                                client.updateDevEnvironment {
                                                    it.id(envId)
                                                    it.spaceName(org)
                                                    it.projectName(projectName)

                                                    if (initialEnv.instanceType() != instanceSizeSelected) {
                                                        it.instanceType(instanceSizeSelected)
                                                    }

                                                    if (initialEnv.alias().nullize() != alias.getText().nullize()) {
                                                        it.alias(alias.getText().orEmpty())
                                                    }

                                                    if (initialEnv.inactivityTimeoutMinutes() != timeoutSelected) {
                                                        it.inactivityTimeoutMinutes(timeoutSelected)
                                                    }
                                                }
                                            }.join()
                                        } catch (e: Exception) {
                                            result = TelemetryResult.Failed
                                            val message = message("caws.update_dev_environment.failed")
                                            LOG.error(e) { message }
                                            notifyError(message, e.message ?: message("general.unknown_error"), project = inferActiveProject())
                                        }

                                        CodecatalystTelemetry.updateDevEnvironmentSettings(
                                            project = null,
                                            userId = lazilyGetUserId(),
                                            codecatalystUpdateDevEnvironmentLocationType = CodecatalystUpdateDevEnvironmentLocationType.Remote,
                                            result = result
                                        )
                                    }
                                }
                            }
                        }
                    )
                }
            } catch (e: Exception) {
                LOG.error(e) { "Failed to load control center tab" }
                launchChildOnUi {
                    it.replaceWith(
                        verticalGrid {
                            row {
                                val message = e.message ?: message("general.unknown_error")
                                label(message)
                                    .withColor(UIUtil.getErrorForeground())
                            }
                        }
                    )
                }
            }
        }
    }.withMargin {
        margin(25, 25, 25, 25)
    }

    private fun inferActiveProject() = ProjectManager.getInstance().openProjects.first()

    companion object {
        private val LOG = getLogger<UpdateWorkspaceSettingsTab>()
    }
}