<template>
  <div class="column q-col-gutter-sm">
    <div v-if="filters.length">
      <div v-for="(filter, filterIndex) in filters" :key="filterIndex" class="row col-auto items-center">
        <w-btn
          :id="`removeFilter-${filterIndex}`"
          class="q-mr-sm"
          flat
          round
          color="negative"
          icon="fal fa-minus"
          @click="filters.splice(filterIndex, 1);"
        />
        <div class="row col q-col-gutter-sm">
          <w-select-filter
            :id="`fieldList-${filterIndex}`"
            v-model="filters[filterIndex].field"
            class="col-sm-4 col-8"
            :options="columnOptions"
            map-options
            emit-value
            label="Field"
            @update:model-value="onFieldChanged(filterIndex)"
          />
          <w-select
            :id="`filterList-${filterIndex}`"
            v-model="filters[filterIndex].operator"
            class="col-sm-2 col-4"
            :options="getFilters(filters[filterIndex].field)"
            :disable="isDisabled(filters[filterIndex])"
            emit-value
            map-options
            label="Operator"
          />
          <template v-if="filters[filterIndex].field">
            <w-autocomplete v-if="filters[filterIndex].field.type === 'LOCATION'" v-model="filters[filterIndex].value" class="col-sm-6 col-12" :search="locationSearch" label="Value" />
            <w-select-filter
              v-else-if="filters[filterIndex].field.subentity"
              v-model="filters[filterIndex].value"
              class="col-sm-6 col-12"
              multiple
              toggle
              filter
              use-chips
              :options="filters[filterIndex].field.subentity.options"
              clearable
              emit-value
              map-options
              label="Value"
              @clear="filters[filterIndex].value = []"
            />
            <w-select-filter
              v-else-if="filters[filterIndex].field.type === 'LIST'"
              v-model="filters[filterIndex].value"
              class="col-sm-6 col-12"
              multiple
              toggle
              filter
              use-chips
              :options="filters[filterIndex].field.options"
              clearable
              emit-value
              map-options
              label="Value"
              @clear="filters[filterIndex].value = []"
            />
            <w-select
              v-else-if="filters[filterIndex].field.type === 'BOOLEAN'"
              v-model="filters[filterIndex].value"
              class="col-sm-6 col-12"
              :options="filters[filterIndex].field.options || [{ label: 'Yes', value: true}, { label: 'No', value: false }]"
              emit-value
              map-options
              label="Value"
            />
            <template v-else-if="filters[filterIndex].field.type === 'INTEGER'">
              <div v-if="filters[filterIndex].operator === 'between'" class="row col-sm-6 col-12 gutter-x-sm">
                <w-input :id="`filterValueStart-${filterIndex}`" v-model="filters[filterIndex].start" class="col" type="number" clearable label="Start" />
                <w-input :id="`filterValueEnd-${filterIndex}`" v-model="filters[filterIndex].end" class="col" type="number" clearable label="End" />
              </div>
              <w-input
                v-else-if="filters[filterIndex].operator === 'in'"
                :id="`filterValue-${filterIndex}`"
                v-model="filters[filterIndex].value"
                class="col-sm-6 col-12"
                clearable
                label="Value"
                @keyup.enter="$emit('search')"
              />
              <w-input
                v-else
                :id="`filterValue-${filterIndex}`"
                v-model="filters[filterIndex].value"
                class="col-sm-6 col-12"
                type="number"
                clearable
                label="Value"
                @keyup.enter="$emit('search')"
              />
            </template>
            <w-input
              v-else
              :id="`filterValue-${filterIndex}`"
              v-model="filters[filterIndex].value"
              class="col-sm-6 col-12"
              clearable
              label="Value"
              @keyup.enter="$emit('search')"
            />
          </template>
          <w-input
            v-else
            id="`filterValue-${filterIndex}`"
            v-model="filters[filterIndex].value"
            class="col-sm-6 col-12"
            clearable
            disable
            label="Value"
          />
        </div>
      </div>
    </div>
    <div class="col-auto">
      <w-btn id="addFilter" flat color="primary" label="Add filter clause" icon="fal fa-plus" @click="filters.push({ });" />
    </div>
    <div class="col-auto">
      <w-btn id="search" color="primary" label="Search" icon="fal fa-search" :disable="searchDisabled" @click="$emit('search')" />
    </div>
  </div>
</template>

<script>
export default {
  props: {
    columns: {
      type: Array,
      required: true,
    },
    initialFilters: {
      type: Array,
      default: () => [],
    },
    excludeColumnKeys: {
      type: Array,
      default: () => null,
    },
    locationSearch: {
      type: Function,
      default: () => [],
    },
  },
  emits: [
    "filtersUpdated",
    "search"
  ],
  data() {
    return {
      filters: [],
      value: null,
    };
  },
  computed: {
    columnOptions() {
      if (!this.columns) {
        return [];
      }

      const result = this.columns
        .filter(c => !this.excludeColumnKeys || !this.excludeColumnKeys.includes(c.key))
        .map(c => ({
          label: c.subentity ? c.subentity.name : c.label,
          value: c,
        }));

      return result.sort((a, b) => a.label > b.label ? 1 : (b.label > a.label ? -1 : 0));
    },
    searchDisabled() {
      return !this.filters.every(f => {
        if (!f.field) {
          return false;
        }
        switch (f.field.type) {
          case "BOOLEAN":
            return f.value !== undefined;
          case "INTEGER":
            return f.value !== null && f.value !== undefined;
          case "STRING":
          case "DATE":
            return !!f.value;
          case "LOCATION":
            return f.value && f.value.id;
          case "LIST":
            return f.value && f.value.length;
          default:
            if (f.field.subentity) {
              return f.value && f.value.length;
            } else {
              return false;
            }
        }
      });
    }
  },
  watch: {
    columns: {
      handler: function () {
        this.filters = this.initialFilters?.map(f => ({
          field: this.columns?.find(c => c.name === f.field && !c.subentity),
          operator: f.operator,
          value: Array.isArray(f.value) ? f.value.join(",") : f.value,
        })) ?? [];
      },
      immediate: true,
    },
    filters: {
      handler: function () { this.updateFilters(); },
      deep: true,
    },
  },
  methods: {
    isDisabled(filter) {
      return !filter.field || (filter.field.type === "BOOLEAN");
    },
    onFieldChanged(filterIndex) {
      const filter = this.filters[filterIndex];
      if (filter.field.valueType === "FILE") {
        filter.operator = "contains";
      } else if (filter.field.type !== "LOCATION" && (filter.field.subentity || filter.field.type === "LIST" || filter.field.valueType === "JSONArray")) {
        filter.value = [];
        filter.operator = "in";
      } else if (filter.field.type === "BOOLEAN") {
        filter.value = undefined;
        filter.operator = "eq";
      } else {
        filter.value = null;

        switch (filter.field.type) {
          case "BOOLEAN":
          case "INTEGER":
          case "STRING":
            filter.operator = "eq";
            break;
          case "LOCATION":
            filter.operator = "in";
            break;
          default:
            filter.operator = "";
        }
      }
    },
    getFilters(field) {
      if (field) {
        if (field.valueType === "FILE") {
          return [
            {
              label: "contains",
              value: "contains",
            },
          ];
        } else if (field.type === "LOCATION") {
          return [
            {
              label: "in",
              value: "in",
            },
          ];
        } else if (field.subentity || field.type === "LIST") {
          return [
            {
              label: "in",
              value: "in",
            },
            {
              label: "not in",
              value: "notIn",
            },
          ];
        } else {
          switch (field.type) {
            case "INTEGER":
              return [
                {
                  label: "=",
                  value: "eq",
                },
                {
                  label: "<",
                  value: "lt",
                },
                {
                  label: ">",
                  value: "gt",
                },
                {
                  label: "between",
                  value: "between",
                },
                {
                  label: "in",
                  value: "in",
                },
              ];
            case "BOOLEAN":
              return [
                {
                  label: "=",
                  value: "eq",
                },
              ];
            case "STRING":
              return [
                {
                  label: "=",
                  value: "eq",
                },
                {
                  label: "contains",
                  value: "like",
                },
                {
                  label: "starts with",
                  value: "starts",
                },
                {
                  label: "ends with",
                  value: "ends",
                },
              ];
            case "DATE":
              return [
                {
                  label: "=",
                  value: "eq",
                },
              ];
          }
        }
      } else {
        return [];
      }
    },
    updateFilters() {
      const filters = this.filters
        .map(x => {
          if (!x.field) {
            return { };
          }

          if (x.operator === "between") {
            delete x.value;
          } else {
            delete x.start;
            delete x.end;
          }

          const result = {
            field: x.field.name,
            operator: x.operator,
          };

          if (x.field.subentity) {
            result.subentity = x.field.subentity.id;

            if (x.field.subentity.association) {
              result.association = x.field.subentity.association;
            }
          }

          if (x.field.jsoncolumn) {
            result.jsoncolumn = x.field.jsoncolumn;
          }

          if (x.field.valueType) {
            result.valueType = x.field.valueType;
          }

          if (x.field.type) {
            result.type = x.field.type;
          }

          if (x.field.type === "INTEGER" && x.operator === "in" && (typeof x.value === "string" || x.value instanceof String)) {
            result.value = x.value.split(",");
          } else {
            result.value = x.value;
          }

          if (x.start) {
            result.start = x.start;
          }

          if (x.end) {
            result.end = x.end;
          }

          return result;
        });

      this.$emit("filtersUpdated", filters.length && filters);
    },
  },
};
</script>
