<template>
  <q-field
    v-model="model"
    v-bind="$attrs"
    borderless
    :rules="rules"
    :error="error"
    :error-message="errorMessage"
    class="custom-file-input"
  >
    <div class="column flex-center q-mb-sm full-width">
      <span v-if="label" class="text-grey-6 q-mb-xs">{{ label }}</span>
      <div
        class="image-preview-container"
        :class="{
          'has-image': preview,
          'is-dragging': isDragging,
        }"
        @click="pickFile"
        @dragover="handleDragOver"
        @dragleave="handleDragLeave"
        @drop="handleDrop"
      >
        <template v-if="!preview">
          <q-icon
            :name="
              isDragging
                ? 'mdi-upload'
                : type === 'image'
                  ? 'mdi-image-plus'
                  : 'mdi-file-plus'
            "
            size="48px"
            color="grey-6"
            class="absolute-center"
          />
          <div
            class="text-caption text-grey-6 text-center absolute-bottom q-pb-sm q-px-md"
          >
            {{
              isDragging
                ? $t("common.ui.file.drag_and_drop")
                : type == "image"
                  ? $t("common.ui.file.click_select_image")
                  : $t("common.ui.file.click_select")
            }}
          </div>
        </template>

        <q-img
          v-else-if="type === 'image'"
          :src="preview"
          fit="cover"
          class="full-height"
        />

        <div v-else class="position-relative column full-height flex-center">
          <q-icon name="mdi-file-check" size="48px" color="grey-6" />
          <div
            class="absolute-bottom text-caption text-grey-6 text-center q-mb-sm q-px-md"
          >
            {{ model.name }}
          </div>
        </div>

        <div v-if="preview" class="absolute-top-right q-ma-xs">
          <q-btn
            flat
            dense
            round
            color="negative"
            icon="mdi-close"
            @click.stop="clearFile"
          />
        </div>
      </div>

      <q-file
        v-show="false"
        ref="fileInputRef"
        v-model="model"
        :accept="accept"
      />
    </div>
  </q-field>
</template>

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

const { label, rules, accept, type, initialImage } = defineProps({
  label: {
    type: String,
    default: "Select Image",
  },
  rules: {
    type: Array,
    default: () => [],
  },
  accept: {
    type: String,
    default: "image/*",
  },
  type: {
    type: String,
    default: "image",
  },
  initialImage: {
    type: String,
    default: null,
  },
  error: {
    type: Boolean,
    default: false,
  },
  errorMessage: {
    type: String,
    default: "",
  },
});

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

const isDragging = ref(false);
const fileInputRef = useTemplateRef("fileInputRef");
const preview = ref(initialImage || null);
let objectUrl = null;

const cleanupObjectURL = () => {
  if (objectUrl) {
    URL.revokeObjectURL(objectUrl);
    objectUrl = null;
  }
};
onUnmounted(cleanupObjectURL);

const generateBase64 = (file) => {
  if (!file) {
    base64File.value = null;
    return;
  }
  const reader = new FileReader();
  reader.onload = (e) => {
    base64File.value = e.target.result;
  };
  reader.onerror = () => {
    console.error("FileReader failed to read file.");
    base64File.value = null;
  };
  reader.readAsDataURL(file);
};

watch(model, (newFile) => {
  cleanupObjectURL();

  if (newFile && newFile instanceof File) {
    if (type === "image") {
      objectUrl = URL.createObjectURL(newFile);
      preview.value = objectUrl;
    } else {
      preview.value = "file_selected";
    }
    generateBase64(newFile);
  } else {
    preview.value = initialImage || null;
    base64File.value = null;
  }
});

const pickFile = () => {
  fileInputRef.value?.pickFiles();
};

const clearFile = () => {
  model.value = null;
};

const handleDragOver = (event) => {
  event.preventDefault();
  event.dataTransfer.dropEffect = "copy";
  isDragging.value = true;
};

const handleDragLeave = () => {
  isDragging.value = false;
};

const handleDrop = (event) => {
  event.preventDefault();
  isDragging.value = false;

  const file = event.dataTransfer?.files?.[0];
  if (!file) return;

  const acceptedMime = accept;

  if (acceptedMime.endsWith("/*")) {
    const baseMime = acceptedMime.replace("/*", "");
    if (file.type.startsWith(baseMime + "/")) {
      model.value = file;
    }
  } else {
    if (
      acceptedMime
        .split(",")
        .map((m) => m.trim())
        .includes(file.type)
    ) {
      model.value = file;
    }
  }
};
</script>

<style lang="scss" scoped>
@use "sass:map";
@use "src/css/quasar.variables.scss";

.image-preview-container {
  // .body--dark & {
  //   --image-bg-color: #{map.get($colors-dark, "surface")};
  //   --image-border-color: #{map.get($colors-dark, "primary")};
  //   --image-border-hover-color: #{map.get($colors-dark, "primary-dark")};
  // }

  .body--light & {
    --image-bg-color: #{map.get($colors, "surface")};
    --image-border-color: #{map.get($colors, "primary")};
    --image-border-hover-color: #{map.get($colors, "primary-dark")};
  }

  width: 200px;
  height: 200px;
  border: 2px dashed var(--image-border-color);
  border-radius: 4px;
  position: relative;
  overflow: hidden;
  transition: all 0.3s;
  cursor: pointer;

  &.is-dragging {
    border-color: var(--image-border-hover-color);
    background-color: var(--image-bg-color);
    opacity: 0.8;
  }

  &:hover {
    border-color: var(--image-border-hover-color);
    background-color: var(--image-bg-color);
  }

  &.has-image {
    border-style: solid;
  }
}
</style>
