<template>
  <div>
    <div class="flex">
      <q-input
        v-model="localValue"
        filled
        dense
        style="min-width: 80%"
      />
      <q-btn
        v-if="isOuterStatement"
        outline
        type="button"
        color="secondary"
        @click="showTree = !showTree"
      >
        <q-icon color="secondary" name="add" />
      </q-btn>
    </div>
    <q-tree
      v-if="isOuterStatement && showTree"
      class="treeSelect"
      :nodes="reducedService"
      dense
      accordion
      node-key="id"
      label-key="label"
      tick-strategy="strict"
      :ticked.sync="ticked"
      :expanded.sync="expanded"
    />
  </div>
</template>

<script lang="ts">
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
import { v4 } from 'uuid';
import isEmpty from 'lodash/isEmpty';

@Component
export default class NodeTreeStatementSelect extends Vue {
  @Prop({ required: true }) statement: {};
  @Prop({ default: true }) wrappedInCurlyBraces: boolean;
  @Prop({ default: '' }) value: string;

  showTree = false;
  selected = '';
  ticked = [];
  expanded = [];
  localValue = '';
  reducedService: Node[] = [];

  created() {
    this.localValue = this.value;
  }

  get isOuterStatement() {
    return !isEmpty(this.statement);
  }

  @Watch('statement', { immediate: true })
  setTree() {
    this.reducedService = [this.mappedService(this.statement)];
  }

  @Watch('ticked', { immediate: true })
  setTicked(newTick: string[]) {
    if (!newTick.length) {
      this.expanded = [];
      return;
    }
    if (this.ticked.length > 1) {
      this.ticked.shift();
    }

    let mappedValue: string[] = [];
    function searchItem(item: { [key: string]: any }, nodeId: string) {
      Object.keys(item).forEach((key: string) => {
        if (typeof item[key] === 'object') {
          searchItem(item[key], nodeId);
        }
        if (typeof item[key] === 'string') {
          let searchAsRegEx = new RegExp(nodeId, 'gi');
          if (item[key].match(searchAsRegEx)) {
            mappedValue.push(item.label);
          }
        }
      });
      return mappedValue;
    }

    [...this.expanded, this.ticked[0]].forEach((el) => {
      return searchItem(this.reducedService[0], el);
    });

    mappedValue = mappedValue.filter((el) => el && el !== 'root');

    const joinedValue =
      mappedValue.length === 1
        ? mappedValue[0]
        : mappedValue
            ?.map((el) => (!isNaN(+el) ? `[${el}]` : `.${el}`))
            .join('')
            .slice(1);

    this.localValue = joinedValue
      ? this.wrappedInCurlyBraces
        ? '${' + joinedValue + '}'
        : joinedValue
      : '';
    this.showTree = false;
  }

  @Watch('expanded', { immediate: true })
  resetSelected(newVal: [], oldVal: []) {
    if (newVal?.length > oldVal?.length) return;
    if (newVal?.length < oldVal?.length) {
      this.expanded = [];
    }
  }

  @Watch('localValue')
  onInput(v: string) {
    if (this.value !== v) {
      this.$emit('input', v);
    }
  }

  mappedService(statement: {}) {
    let reducedService: Node = {
      label: 'root',
      id: v4(),
      noTick: true,
      children: [],
    };
    if (Object.keys(statement).length) {
      const topNode = setChildren(statement);
      reducedService.children.push(...topNode.children);
    }
    function setChildren(parent: { [key: string]: any }) {
      let currentLevel: Node = { label: '', id: '', children: [] };
      for (const [key, value] of Object.entries(parent)) {
        if (typeof value !== 'object') {
          currentLevel.children.push({ label: key, id: v4() });
        } else {
          const nextLevel = setChildren(value);
          currentLevel.children.push({
            label: key,
            id: v4(),
            children: nextLevel.children,
          });
        }
      }
      return currentLevel;
    }
    return reducedService;
  }
}

type Node = {
  label: string;
  id: string;
  noTick?: boolean;
  children: Children;
};

type Children = {
  label: string;
  id: string;
  children?: any[];
}[];
</script>
<style lang="sass" module>
.treeSelect
  position: absolut
</style>
