<script>
import {
  Vue, Component, Prop, Model, Emit,
} from 'vue-property-decorator';
import itfWizardStep from './Step';
import itfWizardSidebar from './Sidebar';

export default @Component({
  name: 'itfWizard',
  components: {
    itfWizardStep,
    itfWizardSidebar,
  },
  provide() {
    return { stepsManager: this }; // do not use Provide from vue-property-decorator
  }
})
class itfWizard extends Vue {
  @Model('input') value;

  @Prop(Boolean) keepAlive;

  stepsNodes;

  disabledNext = true;

  isPending = false;

  get currentStepId() {
    const stepId = this.value && this.value.current;

    return stepId || (this.stepsNodes && this.stepsNodes[0] && itfWizard.getStepsId(this.stepsNodes[0]));
  }

  get currentSidebarId() {
    const node = this.getCurrentStepNode();
    if (!node) {
      return null;
    }
    const attrs = node.data.attrs || {};
    return attrs['substep-of'] || itfWizard.getStepsId(node);
  }

  get currentStepIndex() {
    const step = this.stepsNodes.findIndex((step) => this.currentStepId.toString() === itfWizard.getStepsId(step).toString());
    return step === -1 ? 0 : step;
  }

  get currentVisibleStepIndex() {
    const stepId = this.currentSidebarId;
    const stepIndex = this.totalStepsNodes.findIndex((step) => stepId.toString() === itfWizard.getStepsId(step).toString());
    return stepIndex === -1 ? 0 : stepIndex;
  }

  get nextStep() {
    for (let i = this.currentStepIndex + 1; i < this.stepsNodes.length; i++) {
      const node = this.stepsNodes[i];
      const id = itfWizard.getStepsId(node);
      if (this.value && this.value.hidden && this.value.hidden.indexOf(id) !== -1) {
        continue;
      }
      return node;
    }
    return null;
  }

  get prevStep() {
    for (let i = this.currentStepIndex - 1; i >= 0; i--) {
      const node = this.stepsNodes[i];
      const id = itfWizard.getStepsId(node);
      if (this.value && this.value.hidden && this.value.hidden.indexOf(id) !== -1) {
        continue;
      }
      return node;
    }
    return null;
  }

  get totalSteps() {
    return this.totalStepsNodes.length - 1;
  }

  get totalStepsNodes() {
    return this.stepsNodes.filter((node) => !(node.data.attrs || {})['substep-of']);
  }

  getCurrentStepNode() {
    const step = this.stepsNodes.find((step) => this.currentStepId.toString() === itfWizard.getStepsId(step).toString());
    return step || this.stepsNodes[0];
  }

  render(createElement) {
    this.stepsNodes = parseNodes(this.$slots);

    let node = this.getCurrentStepNode();

    if (node) {
      node.componentOptions.propsData.hasPrev = this.prevStep !== null;
      node.componentOptions.propsData.hasNext = this.nextStep !== null;
      node.componentOptions.propsData.totalSteps = this.totalSteps;
      node.componentOptions.propsData.currentStepIndex = this.currentVisibleStepIndex;
    }

    const isSidebarNeeded = typeof node.componentOptions.propsData.sidebar !== 'undefined';

    if (this.keepAlive) {
      node = createElement('keep-alive', {}, [node]);
    }

    return createElement('div', { staticClass: 'itf-wizard row gx-0' }, [
      isSidebarNeeded ? createElement('div', { staticClass: 'col-md-3 d-none d-md-flex' }, [
        createSidebar({
          value: (this.value || {}).completed || [],
          steps: this.stepsNodes,
          selectedId: this.currentSidebarId,
        }),
      ]) : null,
      createElement('div', { staticClass: isSidebarNeeded ? 'col-md-9 itf-wizard-step' : 'col-xs-12 itf-wizard-step' }, [node])
    ]);

    function createSidebar(props) {
      return createElement('itf-wizard-sidebar', {
        staticClass: 'itf-wizard-sidebar w-100',
        props,
      });
    }

    function parseNodes(slots) {
      const items = [];
      const nodes = (slots.default || []);

      for (const vnode of nodes) {
        if (!vnode.componentOptions) {
          continue;
        }
        if (vnode.componentOptions.tag.includes('-step')) {
          items.push(vnode);
        }
      }
      return items;
    }
  }

  static getStepsId(step) {
    return step && step.componentOptions && step.componentOptions.propsData.id;
  }

  @Emit('input')
  setValue(value) {
    this.stepsNodes = null;
    this.disabledNext = true;
  }

  scrollToStep() {
    const node = this.getCurrentStepNode();
    if (node && node.elm) {
      node.elm.scrollIntoView({
        behavior: 'smooth',
      });
    }
  }

  @Emit('step-changed')
  goto(stepId, forward = false) {
    this.$nextTick(() => this.scrollToStep());
  }

  goNextStep(completed = false) {
    const newValue = { ...(this.value || {}), current: itfWizard.getStepsId(this.nextStep) };
    if (completed) {
      const id = itfWizard.getStepsId(this.getCurrentStepNode());

      newValue.completed = newValue.completed ? [...newValue.completed] : [];
      if (newValue.completed.indexOf(id) === -1) {
        newValue.completed.push(id);
      }
    }
    this.setValue(newValue);
    this.goto(newValue.current, true);
  }

  goPrevStep() {
    const stepId = itfWizard.getStepsId(this.prevStep);
    this.setValue({ ...this.value, current: stepId });
    this.goto(stepId, false);
  }

  enableNext(isEnabled = true) {
    this.disabledNext = !isEnabled;
  }

  setPending(isPending = true) {
    this.isPending = isPending;
  }

  getPending() {
    return this.isPending;
  }

  skipStep() {
    const skip = () => {
      const newValue = { ...(this.value || {}), current: itfWizard.getStepsId(this.nextStep) };
      newValue.skipped = newValue.skipped ? [...newValue.skipped] : [];
      if (newValue.skipped.indexOf(this.currentStepId) === -1) {
        newValue.skipped.push(this.currentStepId);
      }
      newValue.completed = (newValue.completed || []).filter((id) => id !== this.currentStepId);
      this.setValue(newValue);

      this.goto(newValue.current, true);
    };
    skip();
  }

  hideStep(id, isHidden = true) {
    const newValue = { ...(this.value || {}) };
    newValue.hidden = newValue.hidden ? [...newValue.hidden] : [];
    if (newValue.hidden.indexOf(id) === -1 && isHidden) {
      newValue.hidden.push(id);
    }
    if (newValue.hidden.indexOf(id) !== -1 && !isHidden) {
      newValue.hidden.splice(newValue.hidden.indexOf(id), 1);
    }
    newValue.completed = (newValue.completed || []).filter((id) => id !== this.currentStepId);
    this.setValue(newValue);
  }
}
</script>
