<template>
  <div class="editor-panel">
    <h3>Modifier un créneau</h3>
    <div class="actions">
      <button class="btn small primary" v-on:click="emitCreationOrder">
        <i class="material-icons right">create</i>Nouveau
      </button>
      <button
        class="btn small primary"
        :class="{ disabled: creneau === undefined }"
        v-on:click="emitDuplicateOrder"
      >
        <i class="material-icons right">content_copy</i>Dupliquer
      </button>
      <button
        class="btn small error"
        :class="{ disabled: creneau === null }"
        v-on:click="emitDeleteOrder"
      >
        <i class="material-icons right">delete_forever</i>Supprimer
      </button>
    </div>
    <div class="empty" v-if="creneau === undefined">Veuillez selectioner un creneau.</div>
    <div class="editor-body" v-else>
      <styled-input
        label="Titre"
        id="last_name"
        type="text"
        :modelValue="creneau.title"
        @input="inputListener($event, 'title')"
      />
      <styled-input
        class="s6"
        label="Date"
        id="creneauDate"
        type="text"
        placeholder="yyyy/mm/dd"
        v-model.lazy="jour"
      />
      <styled-input
        class="s6"
        label="Heure"
        id="creneauHeure"
        type="text"
        placeholder="hh:mm"
        v-model.lazy="heure"
      />
      <styled-input
        class="s6"
        label="Durée (min)"
        id="creneauDuree"
        type="number"
        v-model.lazy="duree"
      />
      <styled-input
        class="s6"
        label="Heure fin"
        id="disabled"
        type="text"
        :modelValue="endHour"
        disabled
      />
      <styled-input
        class="s6"
        label="Bénévole minimum"
        id="minAttendee"
        type="number"
        :modelValue="creneau.minAttendee"
        :disabled="creneau.isMeal"
        @input="inputListener($event, 'minAttendee')"
      />
      <styled-input
        class="s6"
        label="Bénévole max"
        id="minAttendee"
        type="number"
        :disabled="!creneau.isMeal"
        :modelValue="creneau.maxAttendee"
        @input="inputListener($event, 'maxAttendee')"
      />

      <styled-input
        label="Description"
        id="description"
        type="textarea"
        class="materialize-textarea"
        :modelValue="creneau.description"
        @input="inputListener($event, 'description')"
      >
      </styled-input>
      <chips-input
        label="Compétences & préférences associées"
        id="compétence_selection"
        placeholder="Choisir une condition"
        secondary-placeholder="+ compétence"
        :autocomplete-list="autocompleteCompetencesList"
        :strict-autocomplete="true"
        v-model="competencesStrIdList"
      ></chips-input>
      <chips-input
        label="Bénévoles"
        id="bénevole_selection"
        placeholder="Choisir un bénévole"
        secondary-placeholder="+ bénévole"
        :autocomplete-list="autocompleteBenevolesList"
        :strict-autocomplete="true"
        v-model="benevoleStrIdList"
      ></chips-input>

      <div class="s6 checkbox">
        <checkbox
          label="Créneau repas"
          :modelValue="creneau.isMeal"
          help="Indique au solveur que ce créneau est un créneau repas"
          @input="checkboxListener($event, 'isMeal')"
        />
        <checkbox
          label="Bénévole fixe"
          :modelValue="creneau.fixedAttendee"
          help="Indique au solveur de ne pas changer l'affectation de ce créneau"
          @input="checkboxListener($event, 'fixedAttendee')"
        />
      </div>
      <styled-input
        label="Pénibilité"
        id="penibility"
        type="number"
        class="s6"
        :modelValue="creneau.penibility"
        @input="inputListener($event, 'penibility')"
        style="display: none"
      />
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, PropType } from "vue";
import Creneau from "@/models/Creneau";
import AutocompleteOptions from "@/models/AutocompleteOptions";
import styledInput from "./input.vue";
import chipsInput from "../components/SelectChipInput.vue";
import checkbox from "./checkBox.vue";
import dayjs from "dayjs";

import "@/assets/css/editor-panel.css";
import { MutationTypes } from "@/store/Mutations";
import Benevole from "@/models/Benevole";
import Competence from "@/models/Competence";

// TODO Understand why the component only syncro after a first edition of the date
export default defineComponent({
  name: "EditeurCreneau",
  components: { "chips-input": chipsInput, styledInput, checkbox },
  props: {
    creneau: {
      type: Object as PropType<Creneau>,
    },
  },
  data: function () {
    return {
      jour: "",
      heure: "",
      duree: "",
      benevoleStrIdList: [] as Array<string>,
      competencesStrIdList: [] as Array<string>,
    };
  },
  watch: {
    "creneau.start": function () {
      this.updateForm();
    },
    "creneau.end": function () {
      this.updateForm();
    },
    "creneau.benevoleIdList": function (val: Array<number>) {
      this.benevoleStrIdList = val.map((s) => s.toString());
    },
    creneau: function (val: Creneau) {
      this.competencesStrIdList = val.competencesIdList.map((s) => s.toString());
    },
    competencesStrIdList(val: Array<string>) {
      if (this.creneau) {
        const new_arr = val.map((s) => parseInt(s));
        const old_arr = this.creneau?.competencesIdList;
        if (
          new_arr.length !== old_arr?.length ||
          new_arr.reduce((acc: boolean, n: number) => acc && old_arr.includes(n), true)
        ) {
          this.updateCreneau("competencesIdList", new_arr);
        }
      }
    },
    benevoleStrIdList(new_val: Array<string>) {
      if (this.creneau) {
        const new_list = new_val.map((s) => parseInt(s));
        const old_list = this.creneau?.benevoleIdList;
        const addList = new_list.filter((i) => !old_list.includes(i));
        const removeList = old_list.filter((i) => !new_list.includes(i));
        addList.forEach(this.addBenevole2Creneau);
        removeList.forEach(this.removeBenevole2Creneau);
      }
    },
    jour: function () {
      this.updateDates();
    },
    heure: function () {
      this.updateDates();
    },
    duree: function () {
      this.updateDates();
    },
  },
  computed: {
    duration(): number {
      return parseFloat(this.duree) ?? 0;
    },
    concatStart(): string {
      return (
        this.jour +
        " " +
        this.heure
          .split(":")
          .map((c) => ("0" + c).slice(-2))
          .join(":")
      );
    },
    validStart(): boolean {
      return /\d{4}\/\d{1,2}\/\d{1,2}/.test(this.jour) && dayjs(this.concatStart).isValid();
    },
    startDate(): Date {
      return dayjs(this.concatStart, "YYYY/MM/DD HH:mm").toDate();
    },
    endDate(): Date {
      return dayjs(this.startDate).add(this.duration, "m").toDate();
    },
    endHour(): string {
      return this.endDate.toTimeString().substring(0, 5);
    },
    validDuree(): boolean {
      return !isNaN(parseFloat(this.duree));
    },
    autocompleteBenevolesList(): Array<AutocompleteOptions> {
      const precursor = this.$store.state.benevoleList
        .map((benevole) => {
          return {
            id: benevole.id + "",
            name: benevole.fullname,
            score: this.getbenevoleScore(benevole),
            collide: this.getBenevoleCollision(benevole),
          };
        })
        .sort((a, b) => {
          let s = a.score - b.score;
          if (s != 0) {
            return s;
          }
          // put the available volunteer first
          s = (a.collide ? 1 : 0) - (b.collide ? 1 : 0);
          if (s != 0) {
            return s;
          }
          return a.name.localeCompare(b.name);
        });
      const output = precursor.map((o) => {
        let classes = o.collide ? "unavailable" : "";
        if (o.score == 3) {
          classes = "error";
        }
        if (o.score == 2) {
          classes = "warning";
        }
        if (o.score == 1) {
          classes = "warning";
        }
        return {
          id: o.id,
          name: o.name,
          class: classes,
        };
      });
      return output;
    },
    competenceList(): Array<Competence> {
      const output = [];
      for (const idStr of this.competencesStrIdList) {
        const id = parseInt(idStr);
        if (id) {
          const c = this.$store.getters.getCompetenceById(id);
          if (c) output.push(c);
        }
      }
      return output;
    },
    autocompleteCompetencesList(): Array<AutocompleteOptions> {
      return this.$store.state.competenceList.map((competence) => {
        return { id: competence.id + "", name: competence.fullname };
      });
    },
  },
  methods: {
    getbenevoleScore(benevole: Benevole): number {
      const scoreList = this.competenceList.map((c) =>
        benevole.competenceIdList.includes(c.id) ? 0 : c.score
      );
      return scoreList.length == 0 ? 0 : Math.max(...scoreList);
    },
    getBenevoleCollision(benevole: Benevole) {
      if (this.creneau != undefined) {
        const current = this.creneau;
        const concurence = benevole.creneauIdList
          .filter((id) => id != this.creneau?.id)
          .map((id) => this.$store.getters.getCreneauById(id))
          .filter((o) => o != undefined) as Array<Creneau>;

        return concurence.map((c) => c.collide(current)).some((b) => b);
      } else {
        return false;
      }
    },
    updateDates: function (): void {
      if (this.creneau && this.validDuree && this.validStart) {
        if (Math.abs(this.startDate.getTime() - this.creneau.start.getTime()) > 1000)
          this.updateCreneau("start", this.startDate);
        if (Math.abs(this.endDate.getTime() - this.creneau.end.getTime()) > 1000)
          this.updateCreneau("end", this.endDate);
      }
    },
    updateCreneau<K extends keyof Creneau>(field: K, value: Creneau[K]) {
      if (this.creneau) {
        const payload = {
          id: this.creneau.id,
          field: field,
          value: value,
        };
        this.$emit("edit", payload);
      }
    },
    addBenevole2Creneau(id: number) {
      if (this.creneau)
        this.$store.commit(MutationTypes.addBenevole2Creneau, {
          creneauId: this.creneau.id,
          benevoleId: id,
        });
    },
    removeBenevole2Creneau(id: number) {
      if (this.creneau)
        this.$store.commit(MutationTypes.removeBenevole2Creneau, {
          creneauId: this.creneau.id,
          benevoleId: id,
        });
    },
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    inputListener(event: any, field: keyof Creneau) {
      this.updateCreneau(field, event.target.value);
    },
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    checkboxListener(event: any, field: keyof Creneau) {
      this.updateCreneau(field, event.target.checked);
    },
    updateForm: function () {
      if (this.creneau) {
        const startDate = dayjs(this.creneau.start);
        this.jour = startDate.format("YYYY/MM/DD");
        this.heure = startDate.format("HH:mm");
        this.duree = Math.round(
          (this.creneau.end.getTime() - this.creneau.start.getTime()) / 1000 / 60
        ).toString();
      }
    },
    emitDuplicateOrder: function () {
      this.$emit("duplicate", this.creneau);
    },
    emitCreationOrder: function () {
      this.$emit("create");
    },
    emitDeleteOrder: function () {
      this.$emit("delete", this.creneau);
    },
  },
  mounted() {
    this.updateForm();
    if (this.creneau) {
      this.benevoleStrIdList = this.creneau.benevoleIdList.map((s) => s.toString());
      this.competencesStrIdList = this.creneau.competencesIdList.map((s) => s.toString());
    }
  },
});
</script>

<style scoped>
.checkbox {
  padding: 4px;
}
</style>
