<template>
  <q-card-section class="avatar-step no-padding">
    <div class="column items-center q-gutter-y-xl q-pt-md">
      <div class="column items-center q-gutter-y-sm">
        <div
          class="photo-circle"
          :class="form.avatar ? 'photo-circle--done' : 'photo-circle--pending'"
          @click="openPicker"
        >
          <img
            v-if="avatarPreview"
            :src="avatarPreview"
            class="avatar-preview__image"
          />

          <q-icon v-else color="white" name="photo_camera" size="52px" />
        </div>

        <input
          ref="uploadInputRef"
          accept="image/jpeg,image/png,image/webp"
          class="hidden"
          type="file"
          @change="onFileSelected"
        />

        <q-btn
          v-if="!form.avatar"
          color="primary-button"
          no-caps
          padding="10px 28px"
          rounded
          :label="$t('auth.avatar_step_button')"
          @click="openPicker"
        />

        <q-btn
          v-if="form.avatar"
          color="grey-6"
          flat
          no-caps
          padding="10px 16px"
          rounded
          :label="$t('auth.avatar_step_remove')"
          @click="removeAvatar"
        />

        <div class="text-text text-center font12">
          {{ $t("auth.avatar_step_title") }}
        </div>

        <div class="avatar-step__hint text-center text-grey-7 font11">
          {{ $t("auth.avatar_step_hint") }}
        </div>
      </div>
    </div>
  </q-card-section>
</template>

<script setup>
import { Camera, CameraResultType, CameraSource } from "@capacitor/camera";
import { computed, onBeforeUnmount, ref, watch } from "vue";
import { useQuasar } from "quasar";
import { useI18n } from "vue-i18n";

const $q = useQuasar();
const { t } = useI18n();

const form = defineModel({ type: Object, required: true });

const uploadInputRef = ref(null);
const avatarPreview = ref(null);

const isNativeApp = computed(() => {
  return $q.platform.is.capacitor || $q.platform.is.cordova;
});

const maxAvatarSize = 5 * 1024 * 1024;
const acceptedAvatarTypes = ["image/jpeg", "image/png", "image/webp"];

const base64ToFile = (base64, filename, mimeType = "image/jpeg") => {
  const byteString = atob(base64);
  const ab = new ArrayBuffer(byteString.length);
  const ia = new Uint8Array(ab);

  for (let i = 0; i < byteString.length; i++) {
    ia[i] = byteString.charCodeAt(i);
  }

  return new File([ab], filename, { type: mimeType });
};

//

const openNativePicker = async () => {
  try {
    const photo = await Camera.getPhoto({
      resultType: CameraResultType.Base64,
      source: CameraSource.Prompt,
      quality: 85,
      correctOrientation: true,
      promptLabelHeader: t("auth.avatar_step_prompt_header"),
      promptLabelPhoto: t("auth.avatar_step_prompt_gallery"),
      promptLabelPicture: t("auth.avatar_step_prompt_camera"),
      promptLabelCancel: t("auth.avatar_step_prompt_cancel"),
    });

    const base64 = photo.base64String.replace(/\s/g, "");
    const mime = `image/${photo.format}`;
    const file = base64ToFile(base64, `avatar.${photo.format}`, mime);

    validateAndSetFile(file);
  } catch (err) {
    const msg = (err?.message || "").toLowerCase();

    if (
      !msg.includes("cancel") &&
      !msg.includes("dismiss") &&
      !msg.includes("no image")
    ) {
      $q.notify({
        type: "negative",
        message: t("auth.avatar_step_invalid"),
      });
    }
  }
};

const openPicker = () => {
  if (isNativeApp.value) {
    openNativePicker();

    return;
  }

  uploadInputRef.value?.click();
};

const revokePreview = () => {
  if (!avatarPreview.value) return;

  URL.revokeObjectURL(avatarPreview.value);

  avatarPreview.value = null;
};

//

const onFileSelected = (event) => {
  const file = event.target.files?.[0] ?? null;

  validateAndSetFile(file);

  event.target.value = "";
};

const removeAvatar = () => {
  revokePreview();

  form.value.avatar = null;

  if (uploadInputRef.value) {
    uploadInputRef.value.value = "";
  }
};

const validateAndSetFile = (file) => {
  if (!file) return;

  if (!acceptedAvatarTypes.includes(file.type) || file.size > maxAvatarSize) {
    $q.notify({
      type: "negative",
      message: t("auth.avatar_step_invalid"),
    });

    return;
  }

  revokePreview();

  form.value.avatar = file;

  avatarPreview.value = URL.createObjectURL(file);
};

watch(
  () => form.value.avatar,
  (avatar) => {
    if (!avatar) {
      revokePreview();
    }
  },
);

onBeforeUnmount(() => {
  revokePreview();
});
</script>

<style lang="scss" scoped>
.avatar-step {
  min-height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.photo-circle {
  width: 130px;
  height: 130px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  overflow: hidden;
  transition: transform 0.15s ease;

  &:active {
    transform: scale(0.95);
  }

  &--pending {
    background-color: rgba(139, 92, 246, 0.18);
  }

  &--done {
    background: linear-gradient(-90deg, #ec48d1 5%, #6b11cb 65%, #2574fc 100%);
  }
}

.avatar-preview__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.avatar-step__hint {
  max-width: 220px;
  line-height: 1.3;
}
</style>
