<template>
  <div
    :class="attrs.class"
    :style="attrs.style"
  >
    <DefaultSelect
      v-model="selectedCity"
      v-bind="selectAttrs"
      class="full-width"
      clearable
      fill-input
      hide-selected
      stack-label
      use-input
      :label="label"
      :loading="loading"
      :options="cityOptions"
      :placeholder="placeholder"
      @filter="filterFn"
    >
      <template #no-option>
        <q-item>
          <q-item-section class="text-grey">
            {{ $t("http.errors.no_records_found") }}
          </q-item-section>
        </q-item>
      </template>
    </DefaultSelect>
  </div>
</template>

<script setup>
import {
  computed,
  onMounted,
  ref,
  useAttrs,
  watch,
} from "vue";

import { getCities } from "src/api/city";
import { normalizeString } from "src/helpers/utils";
import { useI18n } from "vue-i18n";

import DefaultSelect from "src/components/defaults/DefaultSelect.vue";

defineOptions({
  inheritAttrs: false,
});

const emit = defineEmits([
  "selectedStateId",
]);

const {
  country,
  initialId,
  label,
  placeholder,
  state,
} = defineProps({
  country: {
    type: Object,
    default: () => ({
      label: "Brasil",
      value: 1,
    }),
  },
  initialId: {
    type: Number,
    default: null,
  },
  label: {
    type: String,
    default: () =>
      useI18n().t("ui.navigation.city"),
  },
  placeholder: {
    type: String,
    default: () =>
      `${useI18n().t("common.actions.search")} ${useI18n().t(
        "ui.navigation.city",
      )}`,
  },
  state: {
    type: Object,
    default: null,
  },
});

const attrs = useAttrs();

const selectedCity = defineModel({
  type: Object,
});

const baseOptions = ref([]);
const cityOptions = ref([]);
const loading = ref(true);

const selectAttrs = computed(() => {
  const {
    // eslint-disable-next-line no-unused-vars
    class: _,
    // eslint-disable-next-line no-unused-vars
    style: __,
    ...rest
  } = attrs;

  return rest;
});

const ensureOnlyPossibleOptions = (
  countryId,
  stateId,
) => {
  if (!stateId) {
    cityOptions.value = baseOptions.value;

    return;
  }

  cityOptions.value = baseOptions.value.filter(
    (city) =>
      city.state_id === stateId &&
      (!countryId || city.country_id === countryId),
  );
};

const filterFn = (value, update) => {
  update(() => {
    ensureOnlyPossibleOptions(
      country?.value,
      state?.value,
    );

    const needle = normalizeString(value);

    cityOptions.value = cityOptions.value.filter(
      (city) =>
        normalizeString(city.label).includes(needle) ||
        normalizeString(city.code ?? "").includes(needle),
    );
  });
};

const selectCityById = (id) => {
  if (selectedCity.value?.value === id) {
    return;
  }

  selectedCity.value =
    baseOptions.value.find(
      (city) => city.value === id,
    ) ?? null;
};

const selectCityByName = (name) => {
  if (selectedCity.value?.label === name) {
    return;
  }

  selectedCity.value =
    baseOptions.value.find(
      (city) => city.label === name,
    ) ?? null;
};

watch(
  () => state,
  (value, oldValue) => {
    if (
      value?.value !== oldValue?.value &&
      value?.value !== selectedCity.value?.state_id
    ) {
      selectedCity.value = null;
    }

    ensureOnlyPossibleOptions(
      country?.value,
      value?.value,
    );
  },
  {
    immediate: true,
  },
);

watch(selectedCity, (city) => {
  if (city?.state_id) {
    emit(
      "selectedStateId",
      city.state_id,
    );
  }
});

onMounted(async () => {
  try {
    const cities = await getCities();

    baseOptions.value = cities.map((city) => ({
      country_id: city.country_id,
      label: city.name,
      state_id: city.state_id,
      value: city.id,
    }));

    ensureOnlyPossibleOptions(
      country?.value,
      state?.value,
    );

    if (initialId) {
      selectCityById(initialId);
    }
  } catch (error) {
    console.error(error);
  } finally {
    loading.value = false;
  }
});

defineExpose({
  selectCityById,
  selectCityByName,
});
</script>

<style scoped>
div {
  min-width: 0;
}

:deep(.q-field) {
  min-width: 0;
  width: 100%;
}
</style>