<template>
  <div v-if="action === 'create' || (action === 'edit' && dataLoaded)">
    <div class="mb-3">
      <div class="input-group has-validation">
        <div class="form-floating">
          <input
            class="form-control form-control-lg"
            :class="{ 'is-invalid': violations.hasViolation('lastName') }"
            id="lastname"
            v-model="lastName"
            :placeholder="trans(PERSON_MESSAGES_PERSON_LASTNAME)"
          />
          <label for="lastname" class="required">{{
            trans(PERSON_MESSAGES_PERSON_LASTNAME)
          }}</label>
        </div>
      </div>
      <div
        v-for="err in violations.violationTitles('lastName')"
        class="invalid-feedback was-validated-force"
      >
        {{ err }}
      </div>
    </div>

    <div v-if="queryItems">
      <ul class="list-suggest add-items inline">
        <li
          v-for="(qi, i) in queryItems"
          :key="i"
          @click="addQueryItem('lastName', qi)"
        >
          <span class="person-text">{{ qi }}</span>
        </li>
      </ul>
    </div>

    <div class="mb-3">
      <div class="input-group has-validation">
        <div class="form-floating">
          <input
            class="form-control form-control-lg"
            :class="{ 'is-invalid': violations.hasViolation('firstName') }"
            id="firstname"
            v-model="firstName"
            :placeholder="trans(PERSON_MESSAGES_PERSON_FIRSTNAME)"
          />
          <label for="firstname" class="required">{{
            trans(PERSON_MESSAGES_PERSON_FIRSTNAME)
          }}</label>
        </div>
      </div>
      <div
        v-for="err in violations.violationTitles('firstName')"
        class="invalid-feedback was-validated-force"
      >
        {{ err }}
      </div>
    </div>

    <div v-if="queryItems">
      <ul class="list-suggest add-items inline">
        <li
          v-for="(qi, i) in queryItems"
          :key="i"
          @click="addQueryItem('firstName', qi)"
        >
          <span class="person-text">{{ qi }}</span>
        </li>
      </ul>
    </div>

    <template v-if="action === 'create'">
      <div v-for="(a, i) in config.altNames" :key="a.key" class="mb-3">
        <div class="input-group has-validation">
          <div class="form-floating">
            <input
              class="form-control form-control-lg"
              :id="a.key"
              :name="'label_' + a.key"
              value=""
              @input="onAltNameInput($event, a.key)"
            />
            <label :for="'label_' + a.key">{{
              localizeString(a.labels)
            }}</label>
          </div>
        </div>
      </div>
    </template>

    <template v-if="action === 'create'">
      <div
        v-for="worker in config.identifiers"
        :key="worker.definition_id"
        class="mb-3"
      >
        <div class="input-group has-validation">
          <div class="form-floating">
            <input
              class="form-control form-control-lg"
              :class="{
                'is-invalid': violations.hasViolationWithParameter(
                  'identifiers',
                  'definition_id',
                  worker.definition_id.toString(),
                ),
              }"
              type="text"
              :name="'worker_' + worker.definition_id"
              :placeholder="localizeString(worker.label)"
              @input="onIdentifierInput($event, worker.definition_id)"
            />
            <label
              :for="'worker_' + worker.definition_id"
              :class="{ required: worker.presence == 'REQUIRED' }"
              >{{ localizeString(worker.label) }}</label
            >
          </div>
          <div
            v-for="err in violations.violationTitlesWithParameter(
              'identifiers',
              'definition_id',
              worker.definition_id.toString(),
            )"
            class="invalid-feedback was-validated-force"
          >
            {{ err }}
          </div>
        </div>
      </div>
    </template>

    <div class="mb-3">
      <div class="input-group has-validation">
        <div class="form-floating">
          <select
            class="form-select form-select-lg"
            :class="{ 'is-invalid': violations.hasViolation('gender') }"
            id="gender"
            v-model="gender"
          >
            <option selected disabled>
              {{ trans(PERSON_MESSAGES_PERSON_GENDER_PLACEHOLDER) }}
            </option>
            <option v-for="g in config.genders" :value="g.id" :key="g.id">
              {{ g.label }}
            </option>
          </select>
          <label for="gender" class="required">{{
            trans(PERSON_MESSAGES_PERSON_GENDER_TITLE)
          }}</label>
        </div>
        <div
          v-for="err in violations.violationTitles('gender')"
          class="invalid-feedback was-validated-force"
        >
          {{ err }}
        </div>
      </div>
    </div>

    <div class="mb-3" v-if="showCenters && config.centers.length > 1">
      <div class="input-group">
        <div class="form-floating">
          <select
            class="form-select form-select-lg"
            :class="{ 'is-invalid': violations.hasViolation('center') }"
            id="center"
            v-model="center"
          >
            <option selected disabled>
              {{ trans(PERSON_MESSAGES_PERSON_CENTER_PLACEHOLDER) }}
            </option>
            <option v-for="c in config.centers" :value="c" :key="c.id">
              {{ c.name }}
            </option>
          </select>
          <label for="center" class="required">{{
            trans(PERSON_MESSAGES_PERSON_CENTER_TITLE)
          }}</label>
        </div>
        <div
          v-for="err in violations.violationTitles('center')"
          class="invalid-feedback was-validated-force"
        >
          {{ err }}
        </div>
      </div>
    </div>

    <div class="mb-3">
      <div class="input-group has-validation">
        <div class="form-floating">
          <select
            class="form-select form-select-lg"
            :class="{ 'is-invalid': violations.hasViolation('civility') }"
            id="civility"
            v-model="civility"
          >
            <option selected disabled>
              {{ trans(PERSON_MESSAGES_PERSON_CIVILITY_PLACEHOLDER) }}
            </option>
            <option v-for="c in config.civilities" :value="c.id" :key="c.id">
              {{ localizeString(c.name) }}
            </option>
          </select>
          <label for="civility">{{
            trans(PERSON_MESSAGES_PERSON_CIVILITY_TITLE)
          }}</label>
        </div>
        <div
          v-for="err in violations.violationTitles('civility')"
          class="invalid-feedback was-validated-force"
        >
          {{ err }}
        </div>
      </div>
    </div>

    <div class="mb-3">
      <div class="input-group has-validation">
        <span class="input-group-text">
          <i class="bi bi-cake2-fill"></i>
        </span>
        <div class="form-floating">
          <input
            class="form-control form-control-lg"
            :class="{ 'is-invalid': violations.hasViolation('birthdate') }"
            name="birthdate"
            type="date"
            v-model="birthDate"
            :placeholder="trans(BIRTHDATE)"
            :aria-label="trans(BIRTHDATE)"
          />
          <label for="birthdate">{{ trans(BIRTHDATE) }}</label>
        </div>
        <div
          v-for="err in violations.violationTitles('birthdate')"
          class="invalid-feedback was-validated-force"
        >
          {{ err }}
        </div>
      </div>
    </div>

    <div class="mb-3">
      <div class="input-group has-validation">
        <span class="input-group-text" id="phonenumber">
          <i class="fa fa-fw fa-phone"></i>
        </span>
        <div class="form-floating">
          <input
            class="form-control form-control-lg"
            :class="{ 'is-invalid': violations.hasViolation('phonenumber') }"
            v-model="phonenumber"
            :placeholder="trans(PERSON_MESSAGES_PERSON_PHONENUMBER)"
            :aria-label="trans(PERSON_MESSAGES_PERSON_PHONENUMBER)"
            aria-describedby="phonenumber"
          />
          <label for="phonenumber">{{
            trans(PERSON_MESSAGES_PERSON_PHONENUMBER)
          }}</label>
        </div>
        <div
          v-for="err in violations.violationTitles('phonenumber')"
          class="invalid-feedback was-validated-force"
        >
          {{ err }}
        </div>
      </div>
    </div>

    <div class="mb-3">
      <div class="input-group has-validation">
        <span class="input-group-text" id="mobilenumber">
          <i class="fa fa-fw fa-mobile"></i>
        </span>
        <div class="form-floating">
          <input
            class="form-control form-control-lg"
            :class="{ 'is-invalid': violations.hasViolation('mobilenumber') }"
            v-model="mobilenumber"
            :placeholder="trans(PERSON_MESSAGES_PERSON_MOBILENUMBER)"
            :aria-label="trans(PERSON_MESSAGES_PERSON_MOBILENUMBER)"
            aria-describedby="mobilenumber"
          />
          <label for="mobilenumber">{{
            trans(PERSON_MESSAGES_PERSON_MOBILENUMBER)
          }}</label>
        </div>
        <div
          v-for="err in violations.violationTitles('mobilenumber')"
          class="invalid-feedback was-validated-force"
        >
          {{ err }}
        </div>
      </div>
    </div>

    <div class="mb-3">
      <div class="input-group has-validation">
        <span class="input-group-text" id="email">
          <i class="fa fa-fw fa-at"></i>
        </span>
        <div class="form-floating">
          <input
            class="form-control form-control-lg"
            :class="{ 'is-invalid': violations.hasViolation('email') }"
            v-model="email"
            :placeholder="trans(PERSON_MESSAGES_PERSON_EMAIL)"
            :aria-label="trans(PERSON_MESSAGES_PERSON_EMAIL)"
            aria-describedby="email"
          />
          <label for="email">{{ trans(PERSON_MESSAGES_PERSON_EMAIL) }}</label>
        </div>
        <div
          v-for="err in violations.violationTitles('email')"
          class="invalid-feedback was-validated-force"
        >
          {{ err }}
        </div>
      </div>
    </div>

    <div v-if="action === 'create'" class="input-group mb-3 form-check">
      <input
        class="form-check-input"
        type="checkbox"
        v-model="showAddressForm"
        name="showAddressForm"
      />
      <label class="form-check-label">
        {{ trans(PERSON_MESSAGES_PERSON_ADDRESS_SHOW_ADDRESS_FORM) }}
      </label>
    </div>

    <div
      v-if="action === 'create' && showAddressFormValue"
      class="form-floating mb-3"
    >
      <p>{{ trans(PERSON_MESSAGES_PERSON_ADDRESS_WARNING) }}</p>
      <AddAddress
        :context="addAddress.context"
        :options="addAddress.options"
        :addressChangedCallback="submitNewAddress"
        ref="addAddress"
      />
    </div>
  </div>
  <div v-else></div>
</template>

<script setup lang="ts">
import { ref, reactive, computed, onMounted } from "vue";
import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
import AddAddress from "ChillMainAssets/vuejs/Address/components/AddAddress.vue";
import {
  createPerson,
  editPerson,
  getCentersForPersonCreation,
  getCivilities,
  getGenders,
  getPerson,
  getPersonAltNames,
  getPersonIdentifiers,
  personToWritePerson,
  WritePersonViolationMap,
} from "../../_api/OnTheFly";
import {
  trans,
  BIRTHDATE,
  PERSON_EDIT_ERROR_WHILE_SAVING,
  PERSON_MESSAGES_PERSON_LASTNAME,
  PERSON_MESSAGES_PERSON_FIRSTNAME,
  PERSON_MESSAGES_PERSON_GENDER_PLACEHOLDER,
  PERSON_MESSAGES_PERSON_GENDER_TITLE,
  PERSON_MESSAGES_PERSON_CENTER_PLACEHOLDER,
  PERSON_MESSAGES_PERSON_CENTER_TITLE,
  PERSON_MESSAGES_PERSON_CIVILITY_PLACEHOLDER,
  PERSON_MESSAGES_PERSON_CIVILITY_TITLE,
  PERSON_MESSAGES_PERSON_PHONENUMBER,
  PERSON_MESSAGES_PERSON_MOBILENUMBER,
  PERSON_MESSAGES_PERSON_EMAIL,
  PERSON_MESSAGES_PERSON_ADDRESS_SHOW_ADDRESS_FORM,
  PERSON_MESSAGES_PERSON_ADDRESS_WARNING,
} from "translator";
import { Center, Civility, Gender } from "ChillMainAssets/types";
import {
  AltName,
  Person,
  PersonWrite,
  PersonIdentifierWorker,
} from "ChillPersonAssets/types";
import { isValidationException } from "ChillMainAssets/lib/api/apiMethods";
import { useToast } from "vue-toast-notification";
import {
  getTimezoneOffsetString,
  ISOToDate,
} from "ChillMainAssets/chill/js/date";
import { useViolationList } from "ChillMainAssets/vuejs/_composables/violationList";

interface PersonEditComponentConfig {
  id?: number | null;
  action: "edit" | "create";
  query: string;
}

const props = withDefaults(defineProps<PersonEditComponentConfig>(), {
  id: null,
});

const emit =
  defineEmits<(e: "onPersonCreated", payload: { person: Person }) => void>();

defineExpose({ postPerson });

const toast = useToast();

const person = reactive<PersonWrite>({
  type: "person",
  firstName: "",
  lastName: "",
  altNames: [],
  addressId: null,
  birthdate: null,
  deathdate: null,
  phonenumber: "",
  mobilenumber: "",
  email: "",
  gender: null,
  center: null,
  civility: null,
  identifiers: [],
});

const config = reactive<{
  altNames: AltName[];
  civilities: Civility[];
  centers: Center[];
  genders: Gender[];
  identifiers: PersonIdentifierWorker[];
}>({
  altNames: [],
  civilities: [],
  centers: [],
  genders: [],
  identifiers: [],
});

const showCenters = ref(false);
const showAddressFormValue = ref(false);

const addAddress = reactive({
  options: {
    button: {
      text: { create: "person.address.create_address" },
      size: "btn-sm",
    },
    title: { create: "person.address.create_address" },
  },
  context: {
    target: {},
    edit: false,
    addressId: null as number | null,
    defaults: (window as any).addaddress,
  },
});

const firstName = computed({
  get: () => person.firstName,
  set: (value: string) => {
    person.firstName = value;
  },
});
const lastName = computed({
  get: () => person.lastName,
  set: (value: string) => {
    person.lastName = value;
  },
});
const gender = computed({
  get: () => (person.gender ? person.gender.id : null),
  set: (value: string | null) => {
    person.gender = value
      ? { id: Number.parseInt(value), type: "chill_main_gender" }
      : null;
  },
});
const civility = computed({
  get: () => (person.civility ? person.civility.id : null),
  set: (value: number | null) => {
    person.civility =
      value !== null ? { id: value, type: "chill_main_civility" } : null;
  },
});
const birthDate = computed({
  get: () => (person.birthdate ? person.birthdate.datetime.split("T")[0] : ""),
  set: (value: string) => {
    const date = ISOToDate(value);
    if (null === date) {
      person.birthdate = null;
      return;
    }
    const offset = getTimezoneOffsetString(
      date,
      Intl.DateTimeFormat().resolvedOptions().timeZone,
    );
    if (person.birthdate) {
      person.birthdate.datetime = value + "T00:00:00" + offset;
    } else {
      person.birthdate = { datetime: value + "T00:00:00" + offset };
    }
  },
});
const phonenumber = computed({
  get: () => person.phonenumber,
  set: (value: string) => {
    person.phonenumber = value;
  },
});
const mobilenumber = computed({
  get: () => person.mobilenumber,
  set: (value: string) => {
    person.mobilenumber = value;
  },
});
const email = computed({
  get: () => person.email,
  set: (value: string) => {
    person.email = value;
  },
});
const showAddressForm = computed({
  get: () => showAddressFormValue.value,
  set: (value: boolean) => {
    showAddressFormValue.value = value;
  },
});
const center = computed({
  get: () => {
    const c = config.centers.find(
      (c) => person.center !== null && person.center.id === c.id,
    );
    return typeof c === "undefined" ? null : c;
  },
  set: (value: Center | null) => {
    if (null !== value) {
      person.center = {
        id: value.id,
        type: value.type,
      };
    } else {
      person.center = null;
    }
  },
});

/**
 * Find the query items to display for suggestion
 */
const queryItems = computed(() => {
  const words: null | string[] = props.query ? props.query.split(" ") : null;

  if (null === words) {
    return null;
  }

  const firstNameWords = (person.firstName || "")
    .trim()
    .toLowerCase()
    .split(" ");
  const lastNameWords = (person.lastName || "").trim().toLowerCase().split(" ");

  return words
    .filter((word) => !firstNameWords.includes(word.toLowerCase()))
    .filter((word) => !lastNameWords.includes(word.toLowerCase()));
});

const dataLoaded = ref<boolean>(false);

async function loadData() {
  if (props.id !== undefined && props.id !== null) {
    const p = await getPerson(props.id);
    const w = personToWritePerson(p);
    person.firstName = w.firstName;
    person.lastName = w.lastName;
    person.altNames.push(...w.altNames);
    person.civility = w.civility;
    person.addressId = w.addressId;
    person.birthdate = w.birthdate;
    person.deathdate = w.deathdate;
    person.phonenumber = w.phonenumber;
    person.mobilenumber = w.mobilenumber;
    person.email = w.email;
    person.gender = w.gender;
    person.center = w.center;
    person.civility = w.civility;
    person.identifiers.push(...w.identifiers);
    dataLoaded.value = true;
  }
}

function onAltNameInput(event: Event, key: string): void {
  const target = event.target as HTMLInputElement;
  const value = target.value;
  const updateAltNamesKey = person.altNames.findIndex((a) => a.key === key);
  if (-1 === updateAltNamesKey) {
    person.altNames.push({ key, value });
  } else {
    person.altNames[updateAltNamesKey].value = value;
  }
}

function onIdentifierInput(event: Event, definition_id: number): void {
  const target = event.target as HTMLInputElement;
  const value = target.value;
  const updateIdentifierKey = person.identifiers.findIndex(
    (w) => w.definition_id === definition_id,
  );
  if (-1 === updateIdentifierKey) {
    person.identifiers.push({
      type: "person_identifier",
      definition_id,
      value: { content: value },
    });
  } else {
    person.identifiers[updateIdentifierKey].value = { content: value };
  }
}

function addQueryItem(field: "lastName" | "firstName", queryItem: string) {
  switch (field) {
    case "lastName":
      person.lastName = person.lastName
        ? (person.lastName += ` ${queryItem}`)
        : queryItem;
      break;
    case "firstName":
      person.firstName = person.firstName
        ? (person.firstName += ` ${queryItem}`)
        : queryItem;
      break;
  }
}

const violations = useViolationList<WritePersonViolationMap>();

function submitNewAddress(payload: { addressId: number }) {
  person.addressId = payload.addressId;
}

async function postPerson(): Promise<Person> {
  try {
    if (props.action === "create") {
      const createdPerson = await createPerson(person);
      emit("onPersonCreated", { person: createdPerson });

      return Promise.resolve(createdPerson);
    } else if (props.id !== null) {
      const updatedPerson = await editPerson(person, props.id);
      emit("onPersonCreated", { person: updatedPerson });

      return Promise.resolve(updatedPerson);
    }
  } catch (e: unknown) {
    if (isValidationException<WritePersonViolationMap>(e)) {
      violations.setValidationException(e);
    } else {
      toast.error(trans(PERSON_EDIT_ERROR_WHILE_SAVING));
    }
  }
  throw "'action' is not create, or edit with a not-null id";
}

onMounted(() => {
  getPersonAltNames().then((altNames) => {
    config.altNames = altNames;
  });
  getCivilities().then((civilities) => {
    config.civilities = civilities;
  });
  getGenders().then((genders) => {
    config.genders = genders;
  });
  getPersonIdentifiers().then((identifiers) => {
    config.identifiers = identifiers.filter(
      (w: PersonIdentifierWorker) =>
        w.presence === "ON_CREATION" || w.presence === "REQUIRED",
    );
  });
  if (props.action !== "create") {
    loadData();
  } else {
    getCentersForPersonCreation().then((params) => {
      config.centers = params.centers.filter((c: Center) => c.isActive);
      showCenters.value = params.showCenters;
      if (showCenters.value && config.centers.length === 1) {
        // if there is only one center, preselect it
        person.center = {
          id: config.centers[0].id,
          type: config.centers[0].type ?? "center",
        };
      }
    });
  }
});
</script>

<style lang="scss" scoped>
.was-validated-force {
  display: block;
}
</style>
