'use client'

import type {
  Environment,
  EnvironmentDeployment,
  Release,
} from '@dify/contracts/enterprise/types.gen'
import { Button } from '@langgenius/dify-ui/button'
import { useAtomValue, useSetAtom } from 'jotai'
import { ScopeProvider } from 'jotai-scope'
import { useTranslation } from 'react-i18next'
import { EnvVarBindingsPanel } from '../../shared/components/env-var-bindings'
import { isAvailableDeploymentTarget } from '../../shared/domain/runtime-status'
import { canAttemptDeployAtom, canSubmitDeployAtom, closeDeployDrawerAtom, deployBindingSlotsAtom, deployEnvVarSlotsAtom, deployEnvVarValuesAtom, deployFormAppInstanceIdAtom, deployHasBindingOptionsErrorAtom, deployHasSelectedEnvironmentAtom, deployIsBindingOptionsLoadingAtom, deployReadyFormConfigAtom, deployReadyFormLocalAtoms, deployReleaseSubmissionAtom, deploySelectedBindingsAtom, deployShowValidationErrorsAtom, deployTargetReleaseIdAtom, isDeployReleaseSubmittingAtom, releaseDeploymentViewAtom, releaseDeploymentViewIsErrorAtom, releaseDeploymentViewIsLoadingAtom, selectDeployBindingAtom, setDeployEnvVarAtom, showDeployValidationErrorsAtom } from '../state'
import {
  currentReleaseIdForEnvironment,
  selectableDeployReleases,
} from '../state/release-options'
import {
  BindingOptionsPanel,
  DEPLOY_DRAWER_BINDING_LIST_CLASS_NAME,
  DeployFormHeader,
  EnvironmentField,
  ReleaseField,
} from './form-sections'
import { DeployFormSkeleton } from './form-skeleton'

type DeployFormProps = {
  appInstanceId: string
  lockedEnvId?: string
  presetReleaseId?: string
}

type DeployReadyFormProps = DeployFormProps & {
  environments: Environment[]
  releases: Release[]
  runtimeRows: EnvironmentDeployment[]
  defaultReleaseId?: string
  releaseEmptyLabel?: string
}

function DeployRuntimeCredentialBindingsSection() {
  const { t } = useTranslation('deployments')
  const bindingSlots = useAtomValue(deployBindingSlotsAtom)
  const selectedBindings = useAtomValue(deploySelectedBindingsAtom)
  const isBindingOptionsLoading = useAtomValue(deployIsBindingOptionsLoadingAtom)
  const hasBindingOptionsError = useAtomValue(deployHasBindingOptionsErrorAtom)
  const showValidationErrors = useAtomValue(deployShowValidationErrorsAtom)
  const selectBinding = useSetAtom(selectDeployBindingAtom)

  return (
    <BindingOptionsPanel
      slots={bindingSlots}
      selections={selectedBindings}
      isLoading={isBindingOptionsLoading}
      hasError={hasBindingOptionsError}
      bindingCountLabel={t('deployDrawer.bindingCount', { count: bindingSlots.length })}
      showMissingRequired={showValidationErrors}
      onChange={selectBinding}
    />
  )
}

function DeployEnvVarBindingsSection() {
  const { t } = useTranslation('deployments')
  const envVarSlots = useAtomValue(deployEnvVarSlotsAtom)
  const envVarValues = useAtomValue(deployEnvVarValuesAtom)
  const isBindingOptionsLoading = useAtomValue(deployIsBindingOptionsLoadingAtom)
  const hasBindingOptionsError = useAtomValue(deployHasBindingOptionsErrorAtom)
  const showValidationErrors = useAtomValue(deployShowValidationErrorsAtom)
  const setDeployEnvVar = useSetAtom(setDeployEnvVarAtom)

  if (isBindingOptionsLoading || hasBindingOptionsError)
    return null

  return (
    <EnvVarBindingsPanel
      slots={envVarSlots}
      values={envVarValues}
      title={t('deployDrawer.envVars')}
      hint={t('deployDrawer.envVarHint')}
      envVarPlaceholder={t('deployDrawer.envVarPlaceholder')}
      literalSourceLabel={t('deployDrawer.envVarSource.literal')}
      defaultSourceLabel={t('deployDrawer.envVarSource.default')}
      lastDeploymentSourceLabel={t('deployDrawer.envVarSource.lastDeployment')}
      valueTypeLabels={{
        string: t('deployDrawer.envVarType.string'),
        number: t('deployDrawer.envVarType.number'),
        secret: t('deployDrawer.envVarType.secret'),
      }}
      sourceAriaLabel={key => t('deployDrawer.envVarSource.ariaLabel', { key })}
      defaultSourcePriority="lastDeployment"
      envVarCountLabel={t('deployDrawer.envVarCount', { count: envVarSlots.length })}
      missingRequiredLabel={t('deployDrawer.missingRequiredEnvVar')}
      listClassName={DEPLOY_DRAWER_BINDING_LIST_CLASS_NAME}
      showMissingRequired={showValidationErrors}
      onChange={setDeployEnvVar}
    />
  )
}

function DeployBindingsSection() {
  const targetReleaseId = useAtomValue(deployTargetReleaseIdAtom)
  const hasSelectedEnvironment = useAtomValue(deployHasSelectedEnvironmentAtom)

  if (!targetReleaseId || !hasSelectedEnvironment)
    return null

  return (
    <>
      <DeployRuntimeCredentialBindingsSection />
      <DeployEnvVarBindingsSection />
    </>
  )
}

function DeployFormBody() {
  return (
    <div className="min-h-0 flex-1 overflow-y-auto px-6 py-5">
      <div className="flex flex-col gap-5">
        <ReleaseField />
        <EnvironmentField />
        <DeployBindingsSection />
      </div>
    </div>
  )
}

function DeployFooter() {
  const { t } = useTranslation('deployments')
  const closeDeployDrawer = useSetAtom(closeDeployDrawerAtom)
  const showValidationErrors = useSetAtom(showDeployValidationErrorsAtom)
  const submitDeployRelease = useSetAtom(deployReleaseSubmissionAtom)
  const canAttemptDeploy = useAtomValue(canAttemptDeployAtom)
  const canDeploy = useAtomValue(canSubmitDeployAtom)
  const isSubmitting = useAtomValue(isDeployReleaseSubmittingAtom)
  const submitLabel = isSubmitting ? t('deployDrawer.deploying') : t('deployDrawer.deploy')

  function handleDeploy() {
    showValidationErrors()

    if (!canDeploy)
      return

    submitDeployRelease({
      deployFailedMessage: t('deployDrawer.deployFailed'),
    })
  }

  return (
    <div className="flex shrink-0 justify-end gap-2 border-t border-divider-subtle bg-background-default-subtle px-6 py-4">
      <Button type="button" variant="secondary" onClick={closeDeployDrawer}>
        {t('deployDrawer.cancel')}
      </Button>
      <Button variant="primary" disabled={!canAttemptDeploy} onClick={handleDeploy}>
        {submitLabel}
      </Button>
    </div>
  )
}

function deployReadyFormStoreKey({
  appInstanceId,
  environments,
  releases,
  runtimeRows,
  defaultReleaseId,
  lockedEnvId,
  presetReleaseId,
}: DeployReadyFormProps) {
  return [
    appInstanceId,
    lockedEnvId ?? 'any',
    presetReleaseId ?? 'new',
    defaultReleaseId ?? 'none',
    environments.map(env => env.id).join(','),
    releases.map(release => release.id).join(','),
    runtimeRows.map(row => `${row.environment.id}:${row.currentRelease?.id ?? 'none'}`).join(','),
  ].join('|')
}

function DeployReadyForm(config: DeployReadyFormProps) {
  return (
    <ScopeProvider
      atoms={[
        [deployReadyFormConfigAtom, config],
        ...deployReadyFormLocalAtoms,
      ]}
      name="DeployReadyForm"
    >
      <div className="flex min-h-0 flex-1 flex-col">
        <DeployFormHeader />
        <DeployFormBody />
        <DeployFooter />
      </div>
    </ScopeProvider>
  )
}

function DeployFormContent({
  appInstanceId,
  lockedEnvId,
  presetReleaseId,
}: DeployFormProps) {
  const { t } = useTranslation('deployments')
  const deploymentView = useAtomValue(releaseDeploymentViewAtom)
  const isLoading = useAtomValue(releaseDeploymentViewIsLoadingAtom)
  const isError = useAtomValue(releaseDeploymentViewIsErrorAtom)

  if (isLoading) {
    return <DeployFormSkeleton />
  }

  if (isError) {
    return (
      <div className="p-4 system-sm-regular text-text-destructive">
        {t('common.loadFailed')}
      </div>
    )
  }

  if (!deploymentView) {
    return (
      <div className="p-4 system-sm-regular text-text-destructive">
        {t('common.loadFailed')}
      </div>
    )
  }

  const runtimeRows = deploymentView.environmentDeployments
  const environments = runtimeRows
    .filter(row => lockedEnvId || isAvailableDeploymentTarget(row))
    .map(row => row.environment)
  const releaseRows = deploymentView.releases
  const currentReleaseId = currentReleaseIdForEnvironment(runtimeRows, lockedEnvId)
  const releases = selectableDeployReleases({
    releases: releaseRows,
    lockedEnvId,
    currentReleaseId,
    presetReleaseId,
  })
  const defaultReleaseId = releases[0]?.id
  const releaseEmptyLabel = lockedEnvId && !presetReleaseId && currentReleaseId
    ? t('deployDrawer.noOtherReleaseAvailable')
    : undefined
  const readyFormConfig = {
    appInstanceId,
    environments,
    releases,
    runtimeRows,
    defaultReleaseId,
    releaseEmptyLabel,
    lockedEnvId,
    presetReleaseId,
  }
  const formKey = deployReadyFormStoreKey(readyFormConfig)

  return (
    <DeployReadyForm
      key={formKey}
      {...readyFormConfig}
    />
  )
}

export function DeployForm(props: DeployFormProps) {
  return (
    <ScopeProvider
      key={props.appInstanceId}
      atoms={[
        [deployFormAppInstanceIdAtom, props.appInstanceId],
      ]}
      name="DeployForm"
    >
      <DeployFormContent {...props} />
    </ScopeProvider>
  )
}
