import {
  CreateTemplateVersionRequest,
  TemplateVersion,
  TemplateVersionVariable,
  VariableValue,
} from "api/typesGenerated"
import { FormikContextType, FormikTouched, useFormik } from "formik"
import { FC } from "react"
import { getFormHelpers } from "utils/formUtils"
import * as Yup from "yup"
import { useTranslation } from "react-i18next"
import {
  FormFields,
  FormSection,
  HorizontalForm,
  FormFooter,
} from "components/Form/Form"
import {
  SensitiveVariableHelperText,
  TemplateVariableField,
} from "components/TemplateVariableField/TemplateVariableField"

export interface TemplateVariablesForm {
  templateVersion: TemplateVersion
  templateVariables: TemplateVersionVariable[]
  onSubmit: (data: CreateTemplateVersionRequest) => void
  onCancel: () => void
  isSubmitting: boolean
  error?: unknown
  // Helpful to show field errors on Storybook
  initialTouched?: FormikTouched<CreateTemplateVersionRequest>
}
export const TemplateVariablesForm: FC<TemplateVariablesForm> = ({
  templateVersion,
  templateVariables,
  onSubmit,
  onCancel,
  error,
  isSubmitting,
  initialTouched,
}) => {
  const initialUserVariableValues =
    selectInitialUserVariableValues(templateVariables)
  const form: FormikContextType<CreateTemplateVersionRequest> =
    useFormik<CreateTemplateVersionRequest>({
      initialValues: {
        template_id: templateVersion.template_id,
        provisioner: "terraform",
        storage_method: "file",
        tags: templateVersion.job.tags,
        file_id: templateVersion.job.file_id,
        user_variable_values: initialUserVariableValues,
      },
      validationSchema: Yup.object({
        user_variable_values: ValidationSchemaForTemplateVariables(
          "templateVariablesPage",
          templateVariables,
        ),
      }),
      onSubmit,
      initialTouched,
    })
  const getFieldHelpers = getFormHelpers<CreateTemplateVersionRequest>(
    form,
    error,
  )
  const { t } = useTranslation("templateVariablesPage")

  return (
    <HorizontalForm
      onSubmit={form.handleSubmit}
      aria-label={t("formAriaLabel").toString()}
    >
      {templateVariables.map((templateVariable, index) => {
        let fieldHelpers
        if (templateVariable.sensitive) {
          fieldHelpers = getFieldHelpers(
            "user_variable_values[" + index + "].value",
            <SensitiveVariableHelperText />,
          )
        } else {
          fieldHelpers = getFieldHelpers(
            "user_variable_values[" + index + "].value",
          )
        }

        return (
          <FormSection
            key={templateVariable.name}
            title={templateVariable.name}
            description={templateVariable.description}
          >
            <FormFields>
              <TemplateVariableField
                {...fieldHelpers}
                templateVersionVariable={templateVariable}
                initialValue={initialUserVariableValues[index].value}
                disabled={isSubmitting}
                onChange={async (value) => {
                  await form.setFieldValue("user_variable_values." + index, {
                    name: templateVariable.name,
                    value: value,
                  })
                }}
              />
            </FormFields>
          </FormSection>
        )
      })}

      <FormFooter onCancel={onCancel} isLoading={isSubmitting} />
    </HorizontalForm>
  )
}

export const selectInitialUserVariableValues = (
  templateVariables: TemplateVersionVariable[],
): VariableValue[] => {
  const defaults: VariableValue[] = []
  templateVariables.forEach((templateVariable) => {
    // Boolean variables must be always either "true" or "false"
    if (templateVariable.type === "bool" && templateVariable.value === "") {
      defaults.push({
        name: templateVariable.name,
        value: templateVariable.default_value,
      })
      return
    }

    if (templateVariable.sensitive) {
      defaults.push({
        name: templateVariable.name,
        value: "",
      })
      return
    }

    if (templateVariable.required && templateVariable.value === "") {
      defaults.push({
        name: templateVariable.name,
        value: templateVariable.default_value,
      })
      return
    }

    defaults.push({
      name: templateVariable.name,
      value: templateVariable.value,
    })
  })
  return defaults
}

const ValidationSchemaForTemplateVariables = (
  ns: string,
  templateVariables: TemplateVersionVariable[],
): Yup.AnySchema => {
  const { t } = useTranslation(ns)

  return Yup.array()
    .of(
      Yup.object().shape({
        name: Yup.string().required(),
        value: Yup.string().test("verify with template", (val, ctx) => {
          const name = ctx.parent.name
          const templateVariable = templateVariables.find(
            (variable) => variable.name === name,
          )
          if (templateVariable && templateVariable.sensitive) {
            // It's possible that the secret is already stored in database,
            // so we can't properly verify the "required" condition.
            return true
          }
          if (templateVariable && templateVariable.required) {
            if (!val || val.length === 0) {
              return ctx.createError({
                path: ctx.path,
                message: t("validationRequiredVariable").toString(),
              })
            }
          }
          return true
        }),
      }),
    )
    .required()
}
