import { mapActions } from 'vuex'
import element from '@baserow/modules/builder/mixins/element'
import { FormContainerElementType } from '@baserow/modules/builder/elementTypes'

export default {
  mixins: [element],
  data() {
    return {
      inputValue: null,
    }
  },
  mounted() {
    // When a form element is mounted, we want to set the initial value of the form
    // data in the store, but *only* after we have a fully complete `recordIndexPath`,
    // otherwise we will create two entries in the form data store: one for just the
    // elementId, and once the `recordIndexPath` is set, another one with the full path.
    const initialValue = this.elementType.getInitialFormDataValue(
      this.element,
      this.applicationContext
    )
    this.setFormData(initialValue)
  },
  computed: {
    uniqueElementId() {
      return this.elementType.uniqueElementId(
        this.element,
        this.applicationContext.recordIndexPath
      )
    },
    formElementData() {
      return this.$store.getters['formData/getElementFormEntry'](
        this.page,
        this.uniqueElementId
      )
    },
    elementFormDataValue() {
      return this.formElementData?.value
    },
    formElementInvalid() {
      return this.$store.getters['formData/getElementInvalid'](
        this.page,
        this.uniqueElementId
      )
    },
    displayFormDataError() {
      return (
        this.formElementTouched && this.formElementInvalid && !this.isEditMode
      )
    },
    errorMessage() {
      return this.displayFormDataError ? this.getErrorMessage() : ''
    },
    formElementTouched() {
      return this.$store.getters['formData/getElementTouched'](
        this.page,
        this.uniqueElementId
      )
    },
    /**
     * Returns whether the form element is a descendant of a form container.
     * @returns {Boolean} If one or more ancestors is a form container.
     */
    isDescendantOfFormContainer() {
      return this.$store.getters['element/getAncestors'](
        this.page,
        this.element
      ).some(({ type }) => type === FormContainerElementType.getType())
    },
  },
  methods: {
    ...mapActions({
      actionSetFormData: 'formData/setFormData',
    }),
    /*
     * When a form element has been modified (e.g. a user has inputted a value),
     * this method is responsible for updating the form data in the store, and
     * if it's not inside a form container, marking the form element as having
     * been 'touched' by the user.
     */
    handleFormElementChange(value) {
      this.setFormData(value)
      if (!this.isDescendantOfFormContainer) {
        this.onFormElementTouch()
      }
    },
    setFormData(value) {
      return this.actionSetFormData({
        page: this.page,
        uniqueElementId: this.uniqueElementId,
        payload: {
          value,
          elementId: this.element.id,
          touched: this.formElementTouched,
          type: this.elementType.formDataType(this.element),
          isValid: this.elementType.isValid(
            this.element,
            value,
            this.applicationContext
          ),
        },
      })
    },
    /**
     * Responsible for marking this form element as being 'touched' by a
     * user. This will help influence whether to display validation errors.
     */
    onFormElementTouch() {
      this.$store.dispatch('formData/setElementTouched', {
        page: this.page,
        wasTouched: true,
        uniqueElementId: this.uniqueElementId,
      })
    },
    /** Override this method to display the right error message */
    getErrorMessage() {
      return ''
    },
  },
  watch: {
    /**
     * When a form element's formData value changes.
     */
    elementFormDataValue: {
      handler(newValue) {
        this.inputValue = newValue
      },
      immediate: true,
    },
    inputValue(newValue) {
      this.handleFormElementChange(newValue)
    },
  },
}