<template>
  <!-- step0 -->
  <show-pane
    v-if="flag.showPane"
    :context="this.context"
    :options="this.options"
    :defaultz="this.defaultz"
    :entity="this.entity"
    :flag="this.flag"
    :use-date-pane="this.useDatePane"
    @open-edit-pane="openEditPane"
    ref="showAddress"
  />

  <!-- step 1 -->
  <teleport to="body" v-if="inModal">
    <modal
      v-if="flag.suggestPane"
      modal-dialog-class="modal-dialog-scrollable modal-xl"
      @close="resetPane"
    >
      <template #header>
        <h2 class="modal-title">
          {{ trans(getTextTitle) }}
          <span v-if="flag.loading" class="loading">
            <i class="fa fa-circle-o-notch fa-spin fa-fw" />
            <span class="sr-only">{{ trans(ADDRESS_LOADING) }}</span>
          </span>
        </h2>
      </template>

      <template #body>
        <suggest-pane
          :context="this.context"
          :options="this.options"
          :defaultz="this.defaultz"
          :entity="this.entity"
          :flag="this.flag"
          @pick-address="this.pickAddress"
          ref="suggestAddress"
        />
      </template>

      <template #footer>
        <button @click="openEditPane" class="btn btn-create">
          {{ trans(CREATE_A_NEW_ADDRESS) }}
        </button>
      </template>
    </modal>
  </teleport>
  <template v-else>
    <div v-if="flag.suggestPane" class="mt-4 flex-grow-1">
      <suggest-pane
        :context="this.context"
        :options="this.options"
        :defaultz="this.defaultz"
        :entity="this.entity"
        :flag="this.flag"
        :inside-modal="false"
        @pick-address="this.pickAddress"
        ref="suggestAddress"
      >
        <template #before v-if="!bypassFirstStep">
          <a class="btn btn-cancel" @click="resetPane">
            {{ trans(CANCEL) }}
          </a>
        </template>
        <template #action>
          <li>
            <button @click="openEditPane" class="btn btn-create">
              {{ trans(CREATE_A_NEW_ADDRESS) }}
            </button>
          </li>
        </template>
      </suggest-pane>
    </div>
  </template>

  <!-- step 2 -->
  <teleport to="body" v-if="inModal">
    <modal
      v-if="flag.editPane"
      modal-dialog-class="modal-dialog-scrollable modal-xl"
      @close="resetPane"
    >
      <template #header>
        <h2 class="modal-title">
          {{ trans(getTextTitle) }}
          <span v-if="flag.loading" class="loading">
            <i class="fa fa-circle-o-notch fa-spin fa-fw" />
            <span class="sr-only">{{ trans(ADDRESS_LOADING) }}</span>
          </span>
        </h2>
      </template>

      <template #body>
        <edit-pane
          :context="this.context"
          :options="this.options"
          :defaultz="this.defaultz"
          :entity="this.entity"
          :flag="this.flag"
          :errors="this.errors"
          :check-errors="this.checkErrors"
          @get-cities="getCities"
          @get-reference-addresses="getReferenceAddresses"
        />
      </template>

      <template #footer>
        <!--<button class="btn btn-cancel change-icon" @click="resetPane">{{ trans(CANCEL) }}</button>-->
        <button
          v-if="!this.context.edit && this.useDatePane"
          class="btn btn-update change-icon"
          @click="closeEditPane"
        >
          {{ trans(NEXT) }}
          <i class="fa fa-fw fa-arrow-right" />
        </button>
        <button v-else class="btn btn-save" @click="closeEditPane">
          {{ trans(SAVE) }}
        </button>
      </template>
    </modal>
  </teleport>
  <template v-else>
    <div v-if="flag.editPane" class="mt-4 flex-grow-1">
      <edit-pane
        :context="this.context"
        :options="this.options"
        :defaultz="this.defaultz"
        :entity="this.entity"
        :flag="this.flag"
        :errors="this.errors"
        :check-errors="this.checkErrors"
        :inside-modal="false"
        @get-cities="getCities"
        @get-reference-addresses="getReferenceAddresses"
      >
        <template #before>
          <a class="btn btn-cancel" @click="resetPane">
            {{ trans(CANCEL) }}
          </a>
        </template>
        <template #action>
          <li v-if="!this.context.edit && this.useDatePane">
            <button class="btn btn-update change-icon" @click="closeEditPane">
              {{ trans(NEXT) }}
              <i class="fa fa-fw fa-arrow-right" />
            </button>
          </li>
          <li v-else>
            <button class="btn btn-save" @click="closeEditPane">
              {{ trans(SAVE) }}
            </button>
          </li>
        </template>
      </edit-pane>
    </div>
  </template>

  <!-- step 3 -->
  <teleport to="body" v-if="inModal">
    <modal
      v-if="flag.datePane"
      modal-dialog-class="modal-dialog-scrollable modal-xl"
      @close="resetPane"
    >
      <template #header>
        <h2 class="modal-title">
          {{ trans(getTextTitle) }}
          <span v-if="flag.loading" class="loading">
            <i class="fa fa-circle-o-notch fa-spin fa-fw" />
            <span class="sr-only">{{ trans(ADDRESS_LOADING) }}</span>
          </span>
        </h2>
      </template>

      <template #body>
        <date-pane
          :context="this.context"
          :options="this.options"
          :defaultz="this.defaultz"
          :entity="this.entity"
          :flag="this.flag"
          ref="dateAddress"
        />
      </template>

      <template #footer>
        <button class="btn btn-misc" @click="openEditPane">
          <i class="fa fa-fw fa-arrow-left" />
          {{ trans(PREVIOUS) }}
        </button>
        <button class="btn btn-save" @click="closeDatePane">
          {{ trans(SAVE) }}
        </button>
        <!-- -->
      </template>
    </modal>
  </teleport>
  <template v-else>
    <div v-if="flag.datePane" class="mt-4 flex-grow-1">
      <date-pane
        :context="this.context"
        :options="this.options"
        :defaultz="this.defaultz"
        :entity="this.entity"
        :flag="this.flag"
        :inside-modal="false"
        ref="dateAddress"
      >
        <template #before>
          <button class="btn btn-misc" @click="openEditPane">
            <i class="fa fa-fw fa-arrow-left" />
            {{ trans(PREVIOUS) }}
          </button>
        </template>
        <template #action>
          <li>
            <button class="btn btn-save" @click="closeDatePane">
              {{ trans(SAVE) }}
            </button>
          </li>
        </template>
      </date-pane>
    </div>
  </template>
</template>

<script>
import Modal from "ChillMainAssets/vuejs/_components/Modal";
import {
  duplicateAddress,
  fetchCountries,
  fetchCities,
  fetchReferenceAddresses,
  getAddress,
  patchAddress,
  postAddress,
  postPostalCode,
} from "../api";
import {
  CREATE_A_NEW_ADDRESS,
  ADDRESS_LOADING,
  ACTIVITY_CREATE_ADDRESS,
  ACTIVITY_EDIT_ADDRESS,
  CANCEL,
  SAVE,
  PREVIOUS,
  NEXT,
  trans,
} from "translator";
import ShowPane from "./ShowPane.vue";
import SuggestPane from "./SuggestPane.vue";
import EditPane from "./EditPane.vue";
import DatePane from "./DatePane.vue";

export default {
  name: "AddAddress",
  setup() {
    return {
      trans,
      CREATE_A_NEW_ADDRESS,
      ADDRESS_LOADING,
      CANCEL,
      SAVE,
      PREVIOUS,
      NEXT,
    };
  },
  props: ["context", "options", "addressChangedCallback"],
  components: {
    Modal,
    ShowPane,
    SuggestPane,
    EditPane,
    DatePane,
  },
  emits: {
    pickAddress: null,
  },
  data() {
    return {
      flag: {
        showPane: true, // begin with showPane
        suggestPane: false,
        editPane: false,
        datePane: false,
        loading: false,
        success: false,
      },
      errors: [],
      defaultz: {
        button: {
          text: {
            create: "add_an_address_title",
            edit: "edit_address",
          },
          type: { create: "btn-create", edit: "btn-update" },
          displayText: true,
        },
        title: { create: "add_an_address_title", edit: "edit_address" },
        openPanesInModal: true,
        stickyActions: false,
        // show a message when no address.
        // if set to undefined, the value will be equivalent to false if stickyActions is false, true otherwise.
        showMessageWhenNoAddress: undefined,
        useDate: {
          validFrom: false,
          validTo: false,
        },
        onlyButton: false,
      },
      entity: {
        address: {}, // <== loaded and returned
        loaded: {
          countries: [],
          cities: [],
          addresses: [],
        },
        selected: {
          // <== make temporary changes
          isNoAddress: false,
          country: {},
          city: {},
          postcode: {
            code: null,
            name: null,
          },
          address: {},
          writeNew: {
            address: false,
            postcode: false,
          },
          valid: {
            from: new Date(),
            to: null,
          },
        },
        addressMap: {
          // Note: LeafletJs demands [lat, lon]
          // cfr https://macwright.com/lonlat/
          center: [
            this.context.defaults.map_center.x,
            this.context.defaults.map_center.y,
          ],
          zoom: this.context.defaults.map_center.z,
        },
      },
      errorMsg: [],
    };
  },
  computed: {
    inModal() {
      return typeof this.options.openPanesInModal !== "undefined"
        ? this.options.openPanesInModal
        : this.defaultz.openPanesInModal;
    },
    validFrom() {
      return typeof this.options.useDate !== "undefined" &&
        typeof this.options.useDate.validFrom !== "undefined"
        ? this.options.useDate.validFrom
        : this.defaultz.useDate.validFrom;
    },
    validTo() {
      return typeof this.options.useDate !== "undefined" &&
        typeof this.options.useDate.validTo !== "undefined"
        ? this.options.useDate.validTo
        : this.defaultz.useDate.validTo;
    },
    useDatePane() {
      return this.validFrom || this.validTo ? true : false;
    },
    hasSuggestions() {
      if (typeof this.context.suggestions !== "undefined") {
        console.log("hasSuggestions", this.context.suggestions);
        return this.context.suggestions.length > 0;
      }
      return false;
    },
    displaySuggestions() {
      return !this.context.edit && this.hasSuggestions;
    },
    getTextTitle() {
      if (
        typeof this.options.title !== "undefined" &&
        (this.options.title.edit !== null || this.options.title.create !== null)
      ) {
        console.log("this.options.title", this.options.title);

        return this.context.edit
          ? ACTIVITY_EDIT_ADDRESS
          : ACTIVITY_CREATE_ADDRESS;
      }
      return this.context.edit
        ? this.defaultz.title.edit
        : this.defaultz.title.create;
    },
    bypassFirstStep() {
      // exception: passing step0 if new address and pane are not in modal
      return !this.context.edit && !this.inModal;
    },
    forceRedirect() {
      return !(
        this.context.backUrl === null ||
        typeof this.context.backUrl === "undefined"
      );
    },
  },
  mounted() {
    //console.log('validFrom', this.validFrom);
    //console.log('validTo', this.validTo);
    //console.log('useDatePane', this.useDatePane);
    //console.log('Mounted now !');

    if (this.context.edit) {
      console.log("getInitialAddress", this.context.addressId);
      this.getInitialAddress(this.context.addressId);
    }
    this.openShowPane();
  },
  methods: {
    /*
     *  Opening and closing Panes when interacting with buttons
     */
    openShowPane() {
      if (this.flag.editPane === false && this.bypassFirstStep) {
        console.log("bypassFirstStep");
        this.closeShowPane();
        this.openEditPane();
      } else {
        this.flag.showPane = true;
        //console.log('step0: open the Show Panel');
      }
    },
    closeShowPane() {
      // Show pane can be closed only when openPanesInModal is false
      if (!this.inModal) {
        this.flag.showPane = false;
        console.log("step0: close the Show Panel");
      }
    },
    openSuggestPane() {
      this.flag.suggestPane = true;
      console.log("step1: open the Suggestion Panel");
    },
    closeSuggestPane() {
      this.flag.suggestPane = false;
      console.log("step1: close the Suggestion Panel");
    },
    openEditPane() {
      if (this.flag.suggestPane === false && this.displaySuggestions) {
        console.log("displaySuggestions");
        this.openSuggestPane();
      } else {
        if (this.flag.datePane === false) {
          this.initForm(); // reset form except if we come back from datePane
        }
        this.getCountries(); // will open editPane when resolve promise
      }
    },
    closeEditPane() {
      this.flag.editPane = false;
      console.log("step2: close the Edit Panel");
      if (!this.context.edit && this.useDatePane) {
        this.openDatePane();
      } else {
        this.applyChanges();
        if (!this.forceRedirect) {
          this.openShowPane();
        }
      }
    },
    openDatePane() {
      this.flag.datePane = true;
      console.log("step3: open the Date Panel");
    },
    closeDatePane() {
      this.applyChanges();
      this.flag.datePane = false;
      console.log("step3: close the Date Panel");
    },
    resetPane() {
      console.log("resetPane");
      this.flag.suggestPane = false;
      this.flag.editPane = false;
      this.flag.datePane = false;
      this.openShowPane();
    },

    /*
     *  What happens when submitting last Pane:
     *  - redirect or reset pane,
     *  - change context to editing
     */
    afterLastPaneAction(params) {
      this.initForm();
      if (this.forceRedirect) {
        console.log("redirect to backUrl");
        window.location.assign(this.context.backUrl);
      } else {
        console.log("don't redirect");
        this.resetPane();
        if (!this.context.edit) {
          this.context.edit = true;
          this.context.addressId = params.addressId;
          console.log("context is now edit, with address", params.addressId);
        }
      }
    },

    /*
     *  Async Fetch datas
     */
    getInitialAddress(id) {
      this.flag.loading = true;
      getAddress(id)
        .then(
          (address) =>
            new Promise((resolve) => {
              this.entity.address = address;
              this.flag.loading = false;
              resolve();
            }),
        )
        .catch((error) => {
          this.errorMsg.push(error.message);
          this.flag.loading = false;
        });
    },

    getCountries() {
      this.flag.loading = true;
      fetchCountries()
        .then(
          (countries) =>
            new Promise((resolve) => {
              this.entity.loaded.countries = countries.results;
              if (this.flag.showPane === true) {
                this.closeShowPane();
              }
              if (this.flag.suggestPane === true) {
                this.closeSuggestPane();
              }
              if (this.flag.datePane === true) {
                this.flag.datePane = false;
              }
              console.log("step2: open the Edit panel");
              this.flag.editPane = true;
              this.flag.loading = false;
              resolve();
            }),
        )
        .catch((error) => {
          this.errorMsg.push(error.message);
          this.flag.loading = false;
        });
    },

    getCities(country) {
      this.flag.loading = true;
      fetchCities(country)
        .then(
          (cities) =>
            new Promise((resolve) => {
              this.entity.loaded.cities = cities.results.filter(
                (c) => c.origin !== 3,
              ); // filter out user-defined cities
              this.flag.loading = false;
              resolve();
            }),
        )
        .catch((error) => {
          this.errorMsg.push(error.message);
          this.flag.loading = false;
        });
    },

    getReferenceAddresses(city) {
      this.flag.loading = true;
      fetchReferenceAddresses(city)
        .then(
          (addresses) =>
            new Promise((resolve) => {
              this.entity.loaded.addresses = addresses.results;
              this.flag.loading = false;
              resolve();
            }),
        )
        .catch((error) => {
          this.errorMsg.push(error.message);
          this.flag.loading = false;
        });
    },

    checkErrors() {
      this.errors = [];
      if (this.entity.selected.country === null) {
        this.errors.push("Un pays doit être sélectionné.");
      }
      if (this.entity.selected.city === null) {
        this.errors.push("Une ville doit être sélectionnée.");
      } else {
        if (Object.keys(this.entity.selected.city).length === 0) {
          this.errors.push("Une ville doit être sélectionnée.");
        }
      }
      if (!this.entity.selected.isNoAddress) {
        if (
          this.entity.selected.address.street === null ||
          this.entity.selected.address.street === "" ||
          this.entity.selected.address.streetNumber === null ||
          this.entity.selected.address.streetNumber === ""
        ) {
          this.errors.push("Une adresse doit être sélectionnée.");
        }
      }
    },

    /*
     *   Make form ready for new changes
     */
    initForm() {
      console.log("init form");

      this.entity.loaded.addresses = [];
      this.entity.loaded.cities = [];
      this.entity.loaded.countries = [];

      this.entity.selected.confidential = this.context.edit
        ? this.entity.address.confidential
        : false;
      this.entity.selected.isNoAddress =
        this.context.edit && this.entity.address.text === "" ? true : false;

      this.entity.selected.country = this.context.edit
        ? this.entity.address.country
        : {};
      this.entity.selected.postcode = this.context.edit
        ? this.entity.address.postcode
        : {};
      this.entity.selected.city = this.context.edit
        ? this.entity.address.postcode
        : {};

      this.entity.selected.address = {};
      this.entity.selected.address.street = this.context.edit
        ? this.entity.address.street
        : "";
      this.entity.selected.address.streetNumber = this.context.edit
        ? this.entity.address.streetNumber
        : "";
      this.entity.selected.address.floor = this.context.edit
        ? this.entity.address.floor
        : "";
      this.entity.selected.address.corridor = this.context.edit
        ? this.entity.address.corridor
        : "";
      this.entity.selected.address.steps = this.context.edit
        ? this.entity.address.steps
        : "";
      this.entity.selected.address.flat = this.context.edit
        ? this.entity.address.flat
        : "";
      this.entity.selected.address.buildingName = this.context.edit
        ? this.entity.address.buildingName
        : "";
      this.entity.selected.address.distribution = this.context.edit
        ? this.entity.address.distribution
        : "";
      this.entity.selected.address.extra = this.context.edit
        ? this.entity.address.extra
        : "";

      this.entity.selected.writeNew.address =
        this.context.edit &&
        this.entity.address.addressReference === null &&
        this.entity.address.street.length > 0;
      this.entity.selected.writeNew.postcode = false; // NB: this used to be this.context.edit, but think it was erroneous;
      console.log(
        "!! just set writeNew.postcode to",
        this.entity.selected.writeNew.postcode,
      );

      this.checkErrors();
    },

    /*
     *  When changes are validated (get out step2 edit pane, button valid),
     *  apply some transformations before asyncing with backend
     *  from entity.selected to entity.address
     */
    applyChanges() {
      console.log("apply changes");
      let newAddress = {
        confidential: this.entity.selected.confidential,
        isNoAddress: this.entity.selected.isNoAddress,
        street: this.entity.selected.isNoAddress
          ? ""
          : this.entity.selected.address.street,
        streetNumber: this.entity.selected.isNoAddress
          ? ""
          : this.entity.selected.address.streetNumber,
        postcode: { id: this.entity.selected.city.id },
        floor: this.entity.selected.address.floor,
        corridor: this.entity.selected.address.corridor,
        steps: this.entity.selected.address.steps,
        flat: this.entity.selected.address.flat,
        buildingName: this.entity.selected.address.buildingName,
        distribution: this.entity.selected.address.distribution,
        extra: this.entity.selected.address.extra,
      };

      if (this.entity.selected.address.point !== undefined) {
        newAddress = Object.assign(newAddress, {
          point: this.entity.selected.address.point.coordinates,
        });
      } else {
        if (this.entity.selected.postcode.coordinates) {
          newAddress = Object.assign(newAddress, {
            point: this.entity.selected.postcode.coordinates,
          });
        }
      }

      // add the address reference, if any
      if (this.entity.selected.address.addressReference !== undefined) {
        newAddress = Object.assign(newAddress, {
          addressReference: this.entity.selected.address.addressReference,
        });
      } else {
        newAddress = Object.assign(newAddress, {
          addressReference: null,
        });
      }

      if (this.validFrom) {
        console.log(
          "add validFrom in fetch body",
          this.entity.selected.valid.from,
        );
        newAddress = Object.assign(newAddress, {
          validFrom: {
            datetime: `${this.entity.selected.valid.from.toISOString().split("T")[0]}T00:00:00+0100`,
          },
        });
      }
      if (this.validTo && null !== this.entity.selected.valid.to) {
        console.log("add validTo in fetch body", this.entity.selected.valid.to);
        newAddress = Object.assign(newAddress, {
          validTo: {
            datetime: `${this.entity.selected.valid.to.toISOString().split("T")[0]}T00:00:00+0100`,
          },
        });
      }
      if (this.entity.selected.writeNew.postcode) {
        let newPostcode = this.entity.selected.postcode;
        newPostcode = Object.assign(newPostcode, {
          country: { id: this.entity.selected.country.id },
        }); //TODO why not assign postcodeBody here = Object.assign(postcodeBody, {'origin': 3}); ?
        console.log("writeNew postcode is true! newPostcode: ", newPostcode);
        newAddress = Object.assign(newAddress, {
          newPostcode: newPostcode,
        });
      }

      if (!this.context.edit) {
        this.addNewAddress(newAddress).then((payload) =>
          this.addressChangedCallback(payload),
        );
      } else {
        this.updateAddress({
          addressId: this.context.addressId,
          newAddress: newAddress,
        }).then((payload) => this.addressChangedCallback(payload));
      }
    },

    /*
     *  Async POST transactions,
     *  creating new address, and receive backend datas when promise is resolved
     */
    addNewAddress(payload) {
      console.log("addNewAddress", payload);
      this.flag.loading = true;

      if ("newPostcode" in payload) {
        let postcodeBody = payload.newPostcode;
        postcodeBody = Object.assign(postcodeBody, { origin: 3 });
        console.log("juste before post new postcode", postcodeBody);
        return postPostalCode(postcodeBody).then((postalCode) => {
          console.log("new postcode created", postalCode.id);
          payload.postcode = { id: postalCode.id };
          return this.postNewAddress(payload);
        });
      } else {
        return this.postNewAddress(payload);
      }
    },

    postNewAddress(payload) {
      console.log("postNewAddress", payload);
      return postAddress(payload)
        .then(
          (address) =>
            new Promise((resolve) => {
              this.entity.address = address;
              this.flag.loading = false;
              this.flag.success = true;
              resolve({
                address,
                targetOrigin: this.context.target,
                // for "legacy" use:
                target: this.context.target.name,
                targetId: this.context.target.id,
                addressId: this.entity.address.address_id,
              });
            }),
        )
        .catch((error) => {
          this.errorMsg.push(error);
          this.flag.loading = false;
        });
    },

    /*
     *  Async PATCH transactions,
     *  then update existing address with backend datas when promise is resolved
     */
    updateAddress(payload) {
      this.flag.loading = true;

      // TODO change the condition because it writes new postal code in edit mode now: !writeNewPostalCode
      // BUG réécrit un postcode à chaque édition !
      if ("newPostcode" in payload.newAddress) {
        let postcodeBody = payload.newAddress.newPostcode;
        postcodeBody = Object.assign(postcodeBody, { origin: 3 });

        console.log("juste before post new postcode", postcodeBody);
        return postPostalCode(postcodeBody).then((postalCode) => {
          console.log("new postcode created", postalCode.id);
          payload.newAddress.postcode = { id: postalCode.id };
          return this.patchExistingAddress(payload);
        });
      } else {
        return this.patchExistingAddress(payload);
      }
    },

    patchExistingAddress(payload) {
      console.log("patchExistingAddress", payload);
      return patchAddress(payload.addressId, payload.newAddress)
        .then(
          (address) =>
            new Promise((resolve) => {
              this.entity.address = address;
              this.flag.loading = false;
              this.flag.success = true;
              return resolve({
                address,
                targetOrigin: this.context.target,
                // for "legacy" use:
                target: this.context.target.name,
                targetId: this.context.target.id,
                addressId: this.entity.address.address_id,
              });
            }),
        )
        .catch((error) => {
          this.errorMsg.push(error);
          this.flag.loading = false;
        });
    },

    /**
     *
     * Called when the event pick-address is emitted, which is, by the way,
     * called when an address suggestion is picked.
     *
     * @param address the address selected
     */
    pickAddress(address) {
      console.log("pickAddress", address);
      duplicateAddress(address).then((newAddress) => {
        this.entity.address = newAddress;
        this.flag.loading = false;
        this.flag.success = true;
        let payload = {
          address: newAddress,
          targetOrigin: this.context.target,
          // for "legacy" use:
          target: this.context.target.name,
          targetId: this.context.target.id,
          addressId: this.entity.address.address_id,
        };
        this.addressChangedCallback(payload);
        this.closeSuggestPane();
      });
    },
  },
};
</script>

<style lang="scss">
div.entity-address {
  position: relative;
  div.loading {
    position: absolute;
    right: 0;
    top: -55px;
  }
}
h4.h3 {
  font-weight: bold;
}
</style>
