import { Form, Formik } from 'formik'
import * as R from 'ramda'
import React, { useState } from 'react'
import ErrorMessage from '../../../components/ErrorMessage'
import Modal from '../../../components/Modal'
import Stepper from '../../../components/Stepper'

import { Button } from '../../../components/buttons'

import ChooseType, {
  validationSchema as chooseTypeSchema,
  defaultValues as chooseTypeDefaults,
} from './Forms/ChooseType'
import NameOfRequirement, {
  validationSchema as nameOfReqSchema,
  defaultValues as nameOfReqDefaults,
} from './Forms/NameOfRequirement'
import Screen1Information, {
  validationSchema as screen1InfoSchema,
  defaultValues as screen1InfoDefaults,
} from './Forms/Screen1Information'
import Screen2Information, {
  validationSchema as screen2InfoSchema,
  defaultValues as screen2InfoDefaults,
} from './Forms/Screen2Information'
import TypeFields, {
  defaultValues as typeFieldsDefaults,
  validationSchema as typeFieldsValidationSchema,
} from './Forms/TypeFields'
import WizardSplash from './WizardSplash'

const LAST_STEP = 5

const getStep = (step, existingRequirements) =>
  [
    {
      validationSchema: nameOfReqSchema(existingRequirements),
      Component: NameOfRequirement,
    },
    {
      validationSchema: screen1InfoSchema,
      Component: Screen1Information,
    },
    { validationSchema: chooseTypeSchema, Component: ChooseType },
    {
      validationSchema: screen2InfoSchema,
      Component: Screen2Information,
    },
    {
      validationSchema: typeFieldsValidationSchema,
      Component: TypeFields,
    },
  ][step - 1]

const nonEmptyStr = obj => obj.text && obj.text.length

const formatValues = (values, isEditing) => {
  const isChoiceList = values.inputType === 'choiceList'
  const choices = isChoiceList
    ? isEditing
      ? R.path(['listChoices'])(values)
      : R.map(o => o.text)(R.filter(nonEmptyStr)(values.listChoices) ?? [])
    : []

  const hasInputLength = values.constraintType === 'length'
  const inputLength = hasInputLength ? values.inputLength : ''

  let resObj = {
    name: values.requirementName,
    screen1: {
      text: values.screen1Text,
      title: values.screen1Title,
    },
    screen2: {
      title: values.screen2Title,
      text: values.screen2Text,
    },
    input: {
      type: values.inputType,
      constraintType: values.constraintType,
    },
  }

  if (isChoiceList) {
    resObj = R.assocPath(['input', 'choiceList'], choices, resObj)
  }

  if (hasInputLength) {
    resObj = R.assocPath(['input', 'numDigits'], inputLength, resObj)
  }

  if (values.inputLabel1) {
    resObj = R.assocPath(['input', 'label1'], values.inputLabel1, resObj)
  }

  if (values.inputLabel2) {
    resObj = R.assocPath(['input', 'label2'], values.inputLabel2, resObj)
  }

  if (isEditing) {
    resObj = R.assocPath(['id'], values.id, resObj)
  }

  return resObj
}

const makeEditingValues = ({ customRequest, id }) => {
  return {
    id,
    requirementName: customRequest.name,
    screen1Title: customRequest.screen1.title,
    screen1Text: customRequest.screen1.text,
    screen2Title: customRequest.screen2.title,
    screen2Text: customRequest.screen2.text,
    inputType: customRequest.input.type,
    inputLabel1: customRequest.input.label1,
    inputLabel2: customRequest.input.label2,
    listChoices: customRequest.input.choiceList,
    constraintType: customRequest.input.constraintType,
    inputLength: customRequest.input.numDigits,
  }
}

const chooseNotNull = (a, b) => (R.isNil(b) ? a : b)

const Wizard = ({
  onClose,
  toBeEdited,
  onSave,
  hasError,
  existingRequirements,
}) => {
  const isEditing = !R.isNil(toBeEdited)
  const [step, setStep] = useState(isEditing ? 1 : 0)

  const defaultValues = {
    ...nameOfReqDefaults,
    ...screen1InfoDefaults,
    ...screen2InfoDefaults,
    ...chooseTypeDefaults,
    ...typeFieldsDefaults,
  }

  // If we're editing, filter out the requirement being edited so that validation schemas don't enter in circular conflicts
  existingRequirements = isEditing
    ? R.filter(it => it.id !== toBeEdited.id, existingRequirements)
    : existingRequirements
  const stepOptions = getStep(step, existingRequirements)
  const isLastStep = step === LAST_STEP

  const onContinue = values => {
    const showScreen2 =
      values.inputType === 'numerical' || values.inputType === 'choiceList'
    if (isEditing && step === 2) {
      return showScreen2
        ? setStep(4)
        : onSave(formatValues(values, isEditing), isEditing)
    }
    if (isEditing && step === 4) {
      return onSave(formatValues(values, isEditing), isEditing)
    }
    if (step === 3) {
      return showScreen2 ? setStep(step + 1) : setStep(step + 2)
    }
    if (!isLastStep) {
      return setStep(step + 1)
    }
    return onSave(formatValues(values, isEditing), isEditing)
  }

  const editingValues = isEditing ? makeEditingValues(toBeEdited) : {}
  const initialValues = R.mergeWith(chooseNotNull, defaultValues, editingValues)
  const wizardTitle = isEditing
    ? 'Editing custom requirement'
    : 'New custom requirement'
  return (
    <Modal
      title={step > 0 ? wizardTitle : ''}
      handleClose={onClose}
      width={520}
      height={620}
      open={true}>
      {step > 0 && (
        <Stepper
          className="mt-4 mb-4 mx-0"
          steps={LAST_STEP}
          currentStep={step}
        />
      )}
      {step === 0 && !isEditing && <WizardSplash onContinue={onContinue} />}
      {step > 0 && (
        <Formik
          validateOnBlur={false}
          validateOnChange={false}
          enableReinitialize={true}
          onSubmit={onContinue}
          initialValues={initialValues}
          validationSchema={stepOptions.validationSchema}>
          {({ errors }) => (
            <Form
              className="h-full flex flex-col"
              id={'custom-requirement-form'}>
              <stepOptions.Component />
              <div className="flex flex-row mt-auto mx-0 mb-4">
                {(hasError || !R.isEmpty(errors)) && (
                  <ErrorMessage>
                    {R.head(R.values(errors)) ?? `Failed to save`}
                  </ErrorMessage>
                )}
                <Button className="ml-auto" type="submit">
                  {isLastStep ? 'Save' : 'Next'}
                </Button>
              </div>
            </Form>
          )}
        </Formik>
      )}
    </Modal>
  )
}

export default Wizard
