<template>
  <div class="tf-inline-form-field wt-inline-form-field">
    <div
      v-if="isEditing"
      class="container-editing"
    >
      <ElForm
        :model="{ [prop]: localValue }"
        @submit.native.prevent
      >
        <ElFormItem
          :prop="prop"
          size="medium"
        >
          <slot>
            <ElInput
              ref="input"
              v-bind="{
                disabled: btnsDisabled,
                placeholder
              }"
              v-model="localValue"
              @keyup.native.enter="onSave"
              @keyup.native.escape="onCancel"
            >
              <template
                v-if="showExtension"
                slot="append"
                :data-test="`inline form field ${prop} extension`"
              >
                {{ extension }}
              </template>
            </ElInput>
          </slot>
          <div
            v-if="showBtnActions"
            class="btn-group align-end"
          >
            <!-- btn: cancel edit -->
            <AppButton
              :is-disabled="btnsDisabled"
              data-test="cancel inline form field edit"
              icon="fa-solid fa-xmark"
              size="icon"
              title="Cancel edit"
              type="decline"
              @click="onCancel"
            />
            <!-- btn: save product -->
            <AppButton
              :is-disabled="btnsDisabled || btnSaveDisabled"
              :is-loading="isSaving"
              data-test="confirm inline form field edit"
              icon="fa-solid fa-check"
              size="icon"
              title="Confirm edit"
              type="affirm"
              @click="onSave"
            />
          </div>
        </ElFormItem>
      </ElForm>
      <div class="btn-group align-start">
        <AppButton
          v-if="showBtnDelete"
          :is-disabled="btnsDisabled"
          :text="`Delete${deleteType ? ` ${deleteType}` : ''}`"
          class="btn-delete"
          data-test="delete inline form field"
          size="text-small"
          type="decline"
          @click="onDelete"
        />
      </div>
    </div>
    <div
      v-else
      :class="[
        { 'is-hovering': isHovering },
        'container-readonly'
      ]"
    >
      <span
        class="faux-input"
        @click="onEdit"
      >
        <span>{{ value }}</span>
      </span>
      <AppButton
        v-if="showBtnEdit"
        data-test="edit inline form field"
        class="btn-edit"
        icon="fa-solid fa-pencil"
        title="Edit field"
        type="primary"
        size="icon"
        @click="onEdit"
      />
    </div>
  </div>
</template>

<script>
  /**
   * This is an input used when there are "readonly" and "edit" modes. The "readonly" will render out text that on hover (or parental hover) will look like an input. The "edit" mode by default will show an  input > cancel button > save button" design pattern is required. The parent should handle the readonly state along with the API's success/failure returns.
   *
   * @vuedoc
   * @exports TfForm/TfInlineFormField
   * @category Components
   */
  export default {
    name: 'TfInlineFormField',
    props: {
      deleteType: {
        type: String,
        default: '',
      },
      isDisabled: {
        type: Boolean,
        default: false,
      },
      isEditing: {
        type: Boolean,
        required: true,
      },
      isHovering: {
        type: Boolean,
        default: false,
      },
      isDeleting: {
        type: Boolean,
        default: false,
      },
      isSaving: {
        type: Boolean,
        required: true,
      },
      isRequired: {
        type: Boolean,
        default: true,
      },
      placeholder: {
        type: String,
        default: 'Enter value',
      },
      prop: {
        type: String,
        required: true,
      },
      showBtnDelete: {
        type: Boolean,
        default: true,
      },
      showBtnEdit: {
        type: Boolean,
        default: true,
      },
      showBtnActions: {
        type: Boolean,
        default: true,
      },
      showExtension: {
        type: Boolean,
        default: true,
      },
      value: {
        type: String,
        required: true,
      },
    },
    data: (vm) => ({
      originalValue: vm.value,
    }),
    computed: {
      /**
       * Evaluate different states to disable all the buttons in the template.
       *
       * @returns {boolean}
       */
      btnsDisabled() {
        return this.isDeleting || this.isSaving || this.isDisabled;
      },
      /**
       * Evaluate different states to disable all the save in the template and prevent the form from submitting on enter.
       *
       * @returns {boolean}
       */
      btnSaveDisabled() {
        return this.isRequired && !this.localValue;
      },
      /**
       * Evaluate the props to get the extension if necessessary.
       *
       * @returns {string}
       */
      extension() {
        return this.showExtension
          ? this.value.substr(this.value.lastIndexOf('.') + 1)
          : '';
      },
      /**
       * Get the local value for the input with/without the extension.
       * Emit the new value with/without the extension.
       *
       * @returns {string}
       */
      localValue: {
        get() {
          return this.showExtension && this.isEditing
            ? this.value.replace(`.${this.extension}`, '')
            : this.value;
        },
        set(value) {
          const newValue = this.extension
            ? `${value}.${this.extension}`
            : value;

          this.$emit('input', newValue);
        },
      },
    },
    methods: {
      /**
       * Emit "cancel" and "edit" correctly to reset the state in the parent.
       */
      onCancel() {
        this.$emit('cancel', this.originalValue);
        this.$emit('edit', false);
      },
      /**
       * Set btnsDisable to `true` and emit "delete" to the parent.
       */
      onDelete() {
        this.$emit('delete');
      },
      /**
       * Emit up to the parent and wait for the prop to change. If the default slot for the edit mode is being used, we focus on the input.
       */
      onEdit() {
        this.$emit('edit', true);

        this.$nextTick(() => {
          // need to test b/c of jest timing issues I can't pinpoint as well.
          if (this.$slots.default || !this.$refs.input) {
            return;
          }

          this.$refs.input.$el.querySelector('input').focus();
        });
      },
      /**
       * Emit "save" with the correct value/extension only if any of the disabled states aren't true. We also want to update "originalValue" since this component is not destroyed.
       */
      onSave() {
        if (this.btnsDisabled || this.btnSaveDisabled) {
          return;
        }

        const newValue = this.showExtension && this.isEditing
          ? `${this.localValue}.${this.extension}`
          : this.localValue;
        this.originalValue = newValue;

        this.$emit('save', newValue);
      },
    },
  };
</script>

<style lang="scss" scoped>
  @import 'src/assets/styles/variables';

  .el-form-item {
    margin: 0;
  }

  ::v-deep {
    .el-form-item__content {
      display: flex;
      align-items: center;
    }

    .el-input-group__append {
      background-color: $tf-extra-light-gray-2;
      border: {
        top-right-radius: 2px;
        bottom-right-radius: 2px;
      }
    }
  }

  .btn-group.align-end {
    margin-left: 15px;

    // Long term, this should be replaced in the button component scoped styles.
    .tf-button.tf-button--icon-no-fill + .tf-button.tf-button--icon-no-fill,
    .wt-button.wt-button--icon-no-fill + .wt-button.wt-button--icon-no-fill {
      margin-left: 4px;
    }
  }

  span {
    box-sizing: border-box;
    display: flex;
    flex: 1;
    align-items: center;
    transition: all .5s;
    min-height: 30px;
    border: 1px solid transparent;
    border-radius: 2px;
    line-height: 26px;
  }

  .tf-button.btn-delete,
  .wt-button.btn-delete {
    margin-top: 3px;
  }

  .btn-edit {
    opacity: 0;
    transition: all .5s;
  }

  .container {
    &-editing {
      display: flex;
      flex-direction: column;
    }

    &-readonly {
      display: flex;

      &:hover,
      &.is-hovering {
        .btn-edit {
          opacity: 1;
        }

        .faux-input {
          padding: 0 15px;
          border-color: $tf-light-gray;
          background: $tf-white;
        }
      }
    }
  }

  .faux-input {
    border: 1px solid transparent;
    cursor: pointer;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;

    > span {
      display: block;
      text-overflow: ellipsis;
      white-space: nowrap;
      overflow: hidden;
    }
  }

  .tf-inline-form-field,
  .wt-inline-form-field {
    width: 100%;
  }
</style>
