<template>
  <div class="vue-query-builder">
    <slot v-bind="vqbProps">
      <query-builder-group v-bind="vqbProps" v-model:query="query" />
    </slot>
  </div>
</template>

<script>
import QueryBuilderGroup from "./layouts/Bootstrap5/Bootstrap5Group";
import QueryBuilderRule from "./layouts/Bootstrap5/Bootstrap5Rule";
import deepClone from "./utilities.js";

var defaultLabels = {
  matchType: "Match Type",
  matchTypes: [
    { id: "all", label: "All" },
    { id: "any", label: "Any" },
  ],
  addRule: "Add Rule",
  removeRule: "&times;",
  addGroup: "Add Group",
  removeGroup: "&times;",
  textInputPlaceholder: "value",
};

export default {
  name: "VueQueryBuilder",

  components: {
    QueryBuilderGroup,
  },

  props: {
    rules: Array,
    labels: {
      type: Object,
      default() {
        return defaultLabels;
      },
    },
    maxDepth: {
      type: Number,
      default: 3,
      validator: function (value) {
        return value >= 1;
      },
    },
    groupComponent: {
      type: Object,
      default: QueryBuilderGroup,
    },
    ruleComponent: {
      type: Object,
      default: QueryBuilderRule,
    },
    value: Object,
  },

  data() {
    return {
      query: {
        logicalOperator: this.labels.matchTypes[0].id,
        children: [],
      },
      ruleTypes: {
        text: {
          operators: [
            "equals",
            "does not equal",
            "contains",
            "does not contain",
            "is empty",
            "is not empty",
            "begins with",
            "ends with",
          ],
          inputType: "text",
          id: "text-field",
        },
        numeric: {
          operators: ["=", "<>", "<", "<=", ">", ">="],
          inputType: "number",
          id: "number-field",
        },
        custom: {
          operators: [],
          inputType: "text",
          id: "custom-field",
        },
        radio: {
          operators: [],
          choices: [],
          inputType: "radio",
          id: "radio-field",
        },
        checkbox: {
          operators: [],
          choices: [],
          inputType: "checkbox",
          id: "checkbox-field",
        },
        select: {
          operators: [],
          choices: [],
          inputType: "select",
          id: "select-field",
        },
        "multi-select": {
          operators: ["="],
          choices: [],
          inputType: "select",
          id: "multi-select-field",
        },
      },
    };
  },

  computed: {
    mergedLabels() {
      return Object.assign({}, defaultLabels, this.labels);
    },

    mergedRules() {
      var mergedRules = [];
      // eslint-disable-next-line @typescript-eslint/no-this-alias
      var vm = this;

      vm.rules.forEach(function (rule) {
        if (typeof vm.ruleTypes[rule.type] !== "undefined") {
          mergedRules.push(Object.assign({}, vm.ruleTypes[rule.type], rule));
        } else {
          mergedRules.push(rule);
        }
      });

      return mergedRules;
    },

    vqbProps() {
      return {
        index: 0,
        depth: 1,
        maxDepth: this.maxDepth,
        ruleTypes: this.ruleTypes,
        rules: this.mergedRules,
        labels: this.mergedLabels,
        groupComponent: this.groupComponent,
        ruleComponent: this.ruleComponent,
      };
    },
  },

  mounted() {
    this.$watch(
      "query",
      (newQuery) => {
        if (JSON.stringify(newQuery) !== JSON.stringify(this.value)) {
          this.$emit("input", deepClone(newQuery));
        }
      },
      {
        deep: true,
      }
    );

    this.$watch(
      "value",
      (newValue) => {
        if (JSON.stringify(newValue) !== JSON.stringify(this.query)) {
          this.query = deepClone(newValue);
        }
      },
      {
        deep: true,
      }
    );

    if (typeof this.value !== "undefined") {
      this.query = Object.assign(this.query, this.value);
    }
  },
};
</script>
