<template>
  <fieldset class="mb-3" id="actionType">
    <div class="row">
      <legend class="col-sm-4 col-form-label">
        {{ $t("action.label") }}
      </legend>
      <div class="col-sm-8">
        <VueMultiselect
          v-model="action"
          :options="actions.options"
          @select="selectAction"
          @remove="unselectAction"
          :multiple="true"
          :close-on-select="false"
          :placeholder="$t('action.placeholder')"
          :custom-label="formatSocialAction"
          track-by="id"
          :searchable="true"
        />
      </div>
    </div>
  </fieldset>

  <fieldset class="mb-3" id="goal">
    <div class="row">
      <legend class="col-sm-4 col-form-label">
        {{ $t("goal.label") }}
      </legend>
      <div class="col-sm-8">
        <VueMultiselect
          v-model="goal"
          :options="goals.options"
          @select="selectGoal"
          @remove="unselectGoal"
          :multiple="true"
          :close-on-select="false"
          :placeholder="$t('goal.placeholder')"
          label="title"
          :custom-label="transTitle"
          track-by="id"
          :searchable="true"
        />
      </div>
    </div>
  </fieldset>

  <fieldset class="mb-3" id="result">
    <div class="row">
      <legend class="col-sm-4 col-form-label">
        {{ $t("result.label") }}
      </legend>
      <div class="col-sm-8">
        <VueMultiselect
          v-model="result"
          :options="results.options"
          @select="selectResult"
          @remove="unselectResult"
          :multiple="true"
          :close-on-select="false"
          :placeholder="$t('result.placeholder')"
          label="title"
          :custom-label="transTitle"
          track-by="id"
          :searchable="true"
        />
      </div>
    </div>
  </fieldset>
</template>

<script>
import VueMultiselect from "vue-multiselect";
import {
  getSocialActions,
  getGoalByAction,
  getResultByAction,
  getResultByGoal,
} from "./api";

export default {
  name: "App",
  components: {
    VueMultiselect,
  },
  i18n: {
    messages: {
      fr: {
        action: {
          label: "Types d'actions",
          placeholder: "Choisissez une ou plusieurs actions",
        },
        goal: {
          label: "Objectifs",
          placeholder: "Choisissez un ou plusieurs objectifs",
        },
        result: {
          label: "Résultats",
          placeholder: "Choisissez un ou plusieurs résultats",
        },
      },
    },
  },
  data() {
    return {
      actions: {
        options: [], // array with multiselect options
        value: [], // array with selected values
        hiddenField: document.getElementById(
          "export_filters_social_work_type_filter_form_actionType",
        ),
      },
      goals: {
        options: [],
        value: [],
        hiddenField: document.getElementById(
          "export_filters_social_work_type_filter_form_goal",
        ),
      },
      results: {
        options: [],
        value: [],
        hiddenField: document.getElementById(
          "export_filters_social_work_type_filter_form_result",
        ),
      },
    };
  },
  computed: {
    action: {
      get() {
        return this.actions.value;
      },
      set(value) {
        this.actions.value = value;
        this.rebuildHiddenFieldValues("actions");
      },
    },
    goal: {
      get() {
        return this.goals.value;
      },
      set(value) {
        this.goals.value = value;
        this.rebuildHiddenFieldValues("goals");
      },
    },
    result: {
      get() {
        return this.results.value;
      },
      set(value) {
        this.results.value = value;
        this.rebuildHiddenFieldValues("results");
      },
    },
  },
  async mounted() {
    await this.getSocialActionsList();

    if ("" !== this.actions.hiddenField.value) {
      const actionIds = this.actions.hiddenField.value.split(",");
      for (const aid of actionIds) {
        let action = this.actions.options.find(
          (a) => Number.parseInt(aid) === a.id,
        );
        if (undefined !== action) {
          this.action.push(action);
          await this.selectAction(action);
        }
      }
    }

    if ("" !== this.goals.hiddenField.value) {
      const goalsIds = this.goals.hiddenField.value
        .split(",")
        .map((s) => Number.parseInt(s));
      for (const gid of goalsIds) {
        let goal = this.goals.options.find((g) => gid === g.id);
        if (undefined !== goal) {
          this.goal.push(goal);
          await this.selectGoal(goal);
        }
      }
    }

    if ("" !== this.results.hiddenField.value) {
      const resultsIds = this.results.hiddenField.value
        .split(",")
        .map((s) => Number.parseInt(s));
      for (const rid of resultsIds) {
        let result = this.results.options.find((r) => rid === r.id);
        if (undefined !== result) {
          this.result.push(result);
        }
      }
    }
  },
  methods: {
    async getSocialActionsList() {
      let actions = await getSocialActions();
      this.actions.options = actions.toSorted(function (a, b) {
        if (a.issue.ordering === b.issue.ordering) {
          if (a.ordering === b.ordering) {
            return 0;
          }
          if (a.ordering < b.ordering) {
            return -1;
          }
          return 1;
        }

        if (a.issue.ordering < b.issue.ordering) {
          return -1;
        }

        return 1;
      });

      return Promise.resolve();
    },

    formatSocialAction({ text, issue }) {
      return text + " (" + issue.text + ")";
    },

    /**
     * Select/unselect in Action Multiselect
     * @param value
     */
    async selectAction(value) {
      //console.log('----'); console.log('select action', value.id);
      let children = this.getChildrensFromParent(value);
      this.addSelectedElement("actions", children);

      let parentAndChildren = [...[value], ...children];
      const promises = [];
      parentAndChildren.forEach((elem) => {
        promises.push(
          getGoalByAction(elem.id).then((goals) => {
            this.addElementInData("goals", goals);
            return Promise.resolve();
          }),
        );
        promises.push(
          getResultByAction(elem.id).then((results) => {
            this.addElementInData("results", results);
            return Promise.resolve();
          }),
        );
      });

      await Promise.all(promises);
      return Promise.resolve();
    },

    unselectAction(value) {
      getGoalByAction(value.id).then((goals) => {
        [this.results.options, this.results.value] = this.removeElementInData(
          "goals",
          goals,
        );
      });
      getResultByAction(value.id).then((results) => {
        [this.results.options, this.results.value] = this.removeElementInData(
          "results",
          results,
        );
      });
    },

    /**
     * Select/unselect in Goal Multiselect
     * @param value
     */
    async selectGoal(value) {
      return getResultByGoal(value.id).then((results) => {
        this.addElementInData("results", results);
      });
    },

    unselectGoal(value) {
      getResultByGoal(value.id).then(
        (results) =>
          ([this.results.options, this.results.value] =
            this.removeElementInData("results", results)),
      );
    },

    // selectResult(value) {
    //console.log('----'); console.log('select result', value.id);
    // },

    // unselectResult(value) {
    //console.log('----'); console.log('unselect result', value.id);
    // },

    /**
     * Choose parent action will involve retaining the "children" actions.
     * @param value
     * @return array
     */
    getChildrensFromParent(value) {
      if (null === value.parent) {
        let excludeParent = this.actions.options.filter(
          (o) => o.parent !== null,
        );
        let children = excludeParent.filter((o) => o.parent.id === value.id);
        //console.log("get childrens", children.map(e => e.id));
        return children;
      }
      return [];
    },

    /**
     * Add response elements in data target
     * @param target string -> 'actions', 'goals' or 'results'
     * @param response array of objects with fetch results
     */
    addElementInData(target, response) {
      let data = this[target];
      let dump = [];
      response.forEach((elem) => {
        let found = data.options.some((e) => e.id === elem.id);
        if (!found) {
          data.options.push(elem);
          dump.push(elem.id);
        }
      });
      if (dump.length > 0) {
        //console.log('push ' + dump.length + ' elems in', target, dump);
      }
      data.options.sort();
    },

    /**
     * Remove response elements from data target
     * @param target string -> 'actions', 'goals' or 'results'
     * @param response array of objects with fetch results
     * @returns data.<target>.options
     */
    removeElementInData(target, response) {
      let data = this[target];
      let dump = [];
      response.forEach((elem) => {
        let found = data.options.some((e) => e.id === elem.id);
        if (found) {
          data.options = data.options.filter((e) => e.id !== elem.id);
          dump.push(elem.id);

          this.removeSelectedElement(target, elem);
        }
      });
      if (dump.length > 0) {
        //console.log('remove ' + dump.length + ' elems from ' + target + ' options', dump);
      }
      return [data.options, data.value];
    },

    /**
     *
     * @param target
     * @param elements
     */
    addSelectedElement(target, elements) {
      let data = this[target];
      let dump = [];
      elements.forEach((elem) => {
        let selected = data.value.some((e) => e.id === elem.id);
        if (!selected) {
          data.value.push(elem);
          dump.push(elem.id);

          // add in hiddenField
          this.rebuildHiddenFieldValues(target);
        }
      });
      if (dump.length > 0) {
        //console.log('add ' + dump.length + ' selected elems in', target, dump);
      }
    },

    /**
     * Remove element from selected and from hiddenField
     * @param target
     * @param elem
     */
    removeSelectedElement(target, elem) {
      let data = this[target];
      let selected = data.value.some((e) => e.id === elem.id);
      if (selected) {
        // remove from selected
        data.value = data.value.filter((e) => e.id !== elem.id);
        //console.log('remove ' + elem.id + ' from selected ' + target);

        // remove from hiddenField
        this.rebuildHiddenFieldValues(target);

        // in any cases, remove should be recursive
        this.unselectToNextField(target, elem);
      }
    },

    /**
     * When unselect Action, it could remove elements in goals multiselect.
     * In that case, we have to unselect Goal to remove elements in results too.
     * @param target
     * @param elem
     */
    unselectToNextField(target, elem) {
      if (target === "goals") {
        //console.log('!!!! target is goal: unselect goal', elem.id);
        this.unselectGoal(elem);
        //console.log('!!!! done');
      }
    },

    /**
     * Rebuild values serie (string) in target HiddenField
     * @param target
     */
    rebuildHiddenFieldValues(target) {
      let data = this[target];
      //console.log('rebuild hiddenFields ' + target + ' values :');
      data.hiddenField.value = ""; // reset
      data.value.forEach((elem) => {
        data.hiddenField.value = this.addIdToValue(
          data.hiddenField.value,
          elem.id,
        );
      });
      //console.log(data.hiddenField);
    },

    addIdToValue(string, id) {
      let array = string ? string.split(",") : [];
      array.push(id.toString());
      let str = array.join();
      return str;
    },

    transTitle({ title }) {
      return title.fr; //TODO multilang
    },
  },
};
</script>

<style scoped></style>
