<template>
  <div class="filter-modal" v-show="show" @click="closeFilter">
    <div class="filter-panel" ref="panel">
      <div class="filter-title" v-if="title">{{ title }}</div>
      <div class="filter" v-for="(f, idx) in filters" :key="idx">
        <date-picker
          v-if="f.type == 'date'"
          :label="f.name"
          lang="fr"
          target="day"
          :modelValue="defaultValues[idx]"
          @update:modelValue="updateDateFilter($event, idx)"
        />
        <date-picker
          v-if="f.type == 'hour'"
          :label="f.name"
          lang="fr"
          target="hour"
          :modelValue="defaultValues[idx]"
          @update:modelValue="updateDateFilter($event, idx)"
        />
        <select-chip-input
          v-if="f.type == 'category'"
          :label="f.name"
          :modelValue="defaultValues[idx]"
          @update:modelValue="updateCategoryFilter($event, idx)"
          :autocompleteList="autocompleteList[idx]"
          :placeholder="f.placeholder"
          secondaryPlaceholder="+1"
        />
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, PropType } from "vue";
import SelectChipInput from "@/components/SelectChipInput.vue";
import DatePicker from "@/components/date-picker.vue";
import dayjs, { ConfigType } from "dayjs";

export type FilterParam = {
  name: string;
  key: string;
  type: "hour" | "date" | "category";
  direction?: "bigger" | "smaller";
  default?: string;
  placeholder?: string;
};
export default defineComponent({
  components: { DatePicker, SelectChipInput },
  emits: ["filterChange"],
  props: {
    title: { type: String, default: "Apply filter" },
    data: { type: Array as PropType<Array<Record<string, unknown>>>, default: () => [] },
    filters: { type: Array as PropType<Array<FilterParam>>, default: () => [] },
  },
  data() {
    return {
      defaultValues: [] as Array<unknown>,
      filterFunctions: [] as Array<(o: Record<string, unknown>) => boolean>,
      show: false,
    };
  },
  computed: {
    autocompleteList(): Array<Array<unknown>> {
      return this.filters.map((f) => {
        return f.type == "category" ? Array.from(new Set(this.data.flatMap((o) => o[f.key]))) : [];
      });
    },
  },
  watch: {
    data() {
      this.emitFilteredValue();
    },
    filters() {
      this.reset();
    },
  },
  methods: {
    open() {
      this.show = true;
    },
    closeFilter(e: MouseEvent) {
      if (!(this.$refs.panel as HTMLElement).contains(e.target as Node)) {
        this.show = false;
      }
    },
    reset() {
      this.filterFunctions = this.filters.map(() => () => true);
      this.defaultValues = this.filters.map(this.getDefault);
    },
    getDefault(f: FilterParam) {
      if (["date", "hour"].includes(f.type)) {
        if (f.default == undefined) return "";
        const d = dayjs(f.default);
        return d.isValid() ? d.toISOString() : "";
      }
      if (f.type == "category") {
        if (f.default == undefined) return [];
        return JSON.parse(f.default) ?? [];
      }
    },
    emitFilteredValue() {
      this.$emit(
        "filterChange",
        this.data.filter((o) => !this.filterFunctions.some((f) => !f(o)))
      );
    },
    updateDateFilter(value: Date, idx: number) {
      const f = this.filters[idx];
      const d = dayjs(value);
      const compareFunction = f.direction == "smaller" ? d.isBefore : d.isAfter;
      this.filterFunctions[idx] = d.isValid()
        ? (o) => compareFunction(dayjs(o[f.key] as ConfigType))
        : () => true;
      this.defaultValues[idx] = value;
      this.emitFilteredValue();
    },
    updateCategoryFilter(value: Array<unknown>, idx: number) {
      const f = this.filters[idx];

      this.filterFunctions[idx] =
        value.length > 0
          ? (o) => !value.some((k) => !(o[f.key] as Array<unknown>).includes(k))
          : () => true;

      this.defaultValues[idx] = value;
      this.emitFilteredValue();
    },
  },
  mounted() {
    this.reset();
  },
});
</script>

<style scoped>
.filter-modal {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  background-color: rgba(0, 0, 0, 0.2);
  z-index: 1300;
}
.filter-panel {
  position: relative;
  top: 0;
  left: 0;
  bottom: 0;
  overflow-x: auto;
  background-color: #fff;
  width: 350px;
  height: 100vh;
}
.filter-title {
  padding: 16px;
  font-size: 2rem;
}
.filter {
  padding: 8px 16px;
}
</style>
