<template>
  <div class="default-media-picker" :class="rootClass">
    <div v-if="labelOutside && label" class="default-media-picker__label">
      {{ label }}<span v-if="required" class="text-negative"> *</span>
    </div>

    <q-file
      v-model="model"
      v-bind="$attrs"
      :accept="accept"
      :class="{ 'required-field': required }"
      :dense="dense"
      :error="!!error"
      :error-message="errorMessage"
      :label="labelOutside ? undefined : label"
      :placeholder="placeholder"
      :rules="rules"
      lazy-rules="ondemand"
      outlined
      @update:model-value="onFileChange"
    >
      <template #prepend>
        <slot name="prepend">
          <q-icon name="attach_file" />
        </slot>
      </template>
    </q-file>

    <div
      v-if="showPreview"
      class="default-media-picker__preview"
      :style="{ height: `${previewHeight}px` }"
    >
      <img
        v-if="previewUrl && isImage"
        alt="Pré-visualização do arquivo"
        class="default-media-picker__image"
        :src="previewUrl"
      />

      <video
        v-else-if="previewUrl && isVideo"
        class="default-media-picker__video"
        controls
        :src="previewUrl"
      />

      <q-icon
        v-else-if="model"
        color="grey-6"
        name="mdi-file-check-outline"
        size="48px"
      />

      <span v-else class="text-caption text-grey-6">
        Nenhum arquivo selecionado
      </span>
    </div>
  </div>
</template>

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

defineOptions({ inheritAttrs: false });

const {
  accept,
  dense,
  label,
  labelOutside,
  placeholder,
  previewHeight,
  rules,
  showPreview,
} = defineProps({
  accept: { type: String, default: "image/*,video/*,.pdf" },
  dense: { type: Boolean, default: false },
  label: { type: String, default: "Arquivo" },
  labelOutside: { type: Boolean, default: true },
  placeholder: { type: String, default: "" },
  previewHeight: { type: Number, default: 200 },
  rules: { type: Array, default: () => [] },
  showPreview: { type: Boolean, default: false },
});

const attrs = useAttrs();

const model = defineModel({ type: [File, null], default: null });

const error = defineModel("error", {
  type: [String, Object, Array, Boolean, null],
  default: null,
});

const previewUrl = defineModel("previewUrl", { type: String, default: null });

const localObjectUrl = ref(null);

const errorMessage = computed(() =>
  typeof error.value === "string" ? error.value : "",
);

const isImage = computed(
  () => model.value?.type?.startsWith("image/") ?? false,
);

const isVideo = computed(
  () => model.value?.type?.startsWith("video/") ?? false,
);

const required = computed(() => rules.some((rule) => rule?.$id === "required"));

const rootClass = computed(() => attrs.class);

const revokeObjectUrl = () => {
  if (localObjectUrl.value) {
    URL.revokeObjectURL(localObjectUrl.value);

    localObjectUrl.value = null;
  }
}

const onFileChange = (file) => {
  revokeObjectUrl();

  if (file instanceof File && (isImage.value || isVideo.value)) {
    localObjectUrl.value = URL.createObjectURL(file);
    previewUrl.value = localObjectUrl.value;
  } else {
    previewUrl.value = null;
  }
}

watch(model, (file) => onFileChange(file));

onUnmounted(() => {
  revokeObjectUrl();
});
</script>

<style scoped>
.default-media-picker__preview {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  margin-top: 8px;
  overflow: hidden;
  border: 1px solid #e0e0e0;
  border-radius: 4px;
  background: #fafafa;
}

.default-media-picker__image,
.default-media-picker__video {
  width: 100%;
  height: 100%;
  object-fit: contain;
}
</style>
