<template>
  <div>
    <div class="image-cropper" :style="{ backgroundColor: bgColor }">
      <button
        v-if="!isCropping && !previewUrl"
        type="button"
        class="image-cropper__upload-btn"
        @click="triggerUpload"
      >
        <SvgIcon name="upload" color="" :size="64" />
      </button>

      <div v-if="isCropping && image" class="image-cropper__crop-stage">
        <div class="image-cropper__crop-frame">
          <Cropper
            :src="image"
            :stencil-component="circleStencil"
            :stencil-props="{ aspectRatio: 1 }"
            image-restriction="stencil"
            :auto-zoom="true"
            class="image-cropper__cropper"
            @change="onCrop"
          />
        </div>
        <div class="image-cropper__actions">
          <CircleButton
            tooltip="تایید"
            tooltip-position="top"
            bg-color="var(--color-primary)"
            size="2.5rem"
            @click.stop="confirmCrop"
          >
            <template #icon>
              <SvgIcon name="check" :size="20" color="#fff" />
            </template>
          </CircleButton>
          <CircleButton
            tooltip="انصراف"
            tooltip-position="top"
            bg-color="#fff"
            size="2.5rem"
            @click="cancelCrop"
          >
            <template #icon>
              <SvgIcon name="close" :size="20" color="var(--color-thd-gray)" />
            </template>
          </CircleButton>
        </div>
      </div>

      <div v-else-if="previewUrl && !isCropping" class="image-cropper__preview-wrap">
        <div class="image-cropper__preview-frame">
          <img :src="previewUrl" alt="" class="image-cropper__preview-img" />
          <div class="image-cropper__preview-mask" />
        </div>
        <div class="image-cropper__preview-action">
          <CircleButton
            tooltip="بارگذاری"
            tooltip-position="top"
            bg-color="#fff"
            size="2.5rem"
            @click="triggerUpload"
          >
            <template #icon>
              <SvgIcon name="upload" :size="20" color="var(--color-thd-gray)" />
            </template>
          </CircleButton>
        </div>
      </div>

      <div class="image-cropper__hint">
        <SvgIcon :name="helperIcon" color="#B8B8B8" :size="20" />
        <p>{{ helperText }}</p>
      </div>
    </div>

    <input ref="fileInput" type="file" accept="image/*" hidden @change="onFileChange" />

    <p v-if="error" class="image-cropper__error" :class="{ 'animate-shake': vibration }">
      {{ error }}
    </p>
  </div>
</template>

<script setup>
import 'vue-advanced-cropper/dist/style.css'
import SvgIcon from '@/components/icons/SvgIcon.vue'
import CircleButton from '@/components/CircleButton.vue'
import { markRaw, onBeforeUnmount, ref, watch } from 'vue'
import { Cropper, CircleStencil } from 'vue-advanced-cropper'

const props = defineProps({
  modelValue: { type: [Object, String], default: null },
  name: { type: String, default: '' },
  vibration: { type: Boolean, default: false },
  bgColor: { type: String, default: '#f3f4f6' },
  maxSize: { type: Number, default: 5 * 1024 * 1024 },
  helperText: {
    type: String,
    default: 'سایز تصویر رسمی 3x4 و حجم تصویر بیشتر از ۵ مگابایت نباشد.',
  },
  helperIcon: { type: String, default: 'warning' },
  error: { type: String, default: '' },
})

const emit = defineEmits(['crop', 'update:modelValue', 'error'])

const fileInput = ref(null)
const image = ref(null)
const cropResult = ref(null)
const isCropping = ref(false)
const internalPreviewUrl = ref(null)
const originalFileName = ref(null)
const previewUrl = ref(null)

const circleStencil = markRaw(CircleStencil)

const cleanup = () => {
  if (internalPreviewUrl.value) {
    URL.revokeObjectURL(internalPreviewUrl.value)
    internalPreviewUrl.value = null
  }
}

watch(
  () => props.modelValue,
  (val) => {
    if (val && typeof val === 'object' && val.url) {
      previewUrl.value = val.url
    } else if (typeof val === 'string') {
      previewUrl.value = val
    } else {
      previewUrl.value = null
    }
  },
  { immediate: true }
)

const triggerUpload = () => fileInput.value?.click()

const onFileChange = (event) => {
  cleanup()
  const file = event.target.files?.[0]
  if (!file) return

  if (props.maxSize > 0 && file.size > props.maxSize) {
    emit('error', 'حجم تصویر نباید بیشتر از ۵ مگابایت باشد.')
    event.target.value = null
    return
  }

  originalFileName.value = file.name
  internalPreviewUrl.value = URL.createObjectURL(file)
  image.value = internalPreviewUrl.value
  isCropping.value = true
  event.target.value = null
}

const onCrop = ({ canvas }) => {
  cropResult.value = canvas
}

const confirmCrop = async () => {
  if (!cropResult.value) return
  const blob = await new Promise((resolve) => cropResult.value.toBlob(resolve, 'image/png'))
  const croppedFile = new File([blob], originalFileName.value || 'avatar.png', {
    type: 'image/png',
    lastModified: Date.now(),
  })
  emit('crop', croppedFile)
  isCropping.value = false
  image.value = null
  cropResult.value = null
}

const cancelCrop = () => {
  isCropping.value = false
  image.value = null
  cleanup()
}

onBeforeUnmount(cleanup)
</script>

<style lang="scss" scoped>
.image-cropper {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  border-radius: 1.5rem;
  text-align: center;
  padding: 0.75rem 0.5rem;

  @media (min-width: 1024px) {
    min-height: 20rem;
    padding: 0.75rem;
  }

  @media (min-width: 1536px) {
    min-height: 23rem;
  }

  &__upload-btn {
    height: 8.15rem;
    width: 8.15rem;
    border-radius: 9999px;
    background: #fff;
    border: none;
    margin: 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    color: var(--color-thd-gray);

    @media (min-width: 1536px) {
      height: 10.95rem;
      width: 10.95rem;
    }
  }

  &__crop-stage {
    display: flex;
    flex-direction: column;
    align-items: center;
  }

  &__crop-frame {
    height: 8.15rem;
    width: 8.15rem;
    border-radius: 9999px;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid #d1d5db;

    @media (min-width: 1536px) {
      height: 10.95rem;
      width: 10.95rem;
    }
  }

  &__cropper {
    border-radius: 9999px;
  }

  &__actions {
    display: flex;
    gap: 0.625rem;
    align-items: center;
    justify-content: center;
    margin-top: 0.75rem;

    @media (min-width: 1280px) {
      margin-top: 1rem;
    }

    @media (min-width: 1536px) {
      margin-top: 1.25rem;
    }
  }

  &__preview-wrap {
    position: relative;
    width: fit-content;
    margin: 0 auto;
  }

  &__preview-frame {
    position: relative;
    border-radius: 9999px;
    height: 8.15rem;
    width: 8.15rem;
    margin: 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;

    @media (min-width: 1536px) {
      height: 10.95rem;
      width: 10.95rem;
    }
  }

  &__preview-img {
    height: 100%;
    width: 100%;
    object-fit: cover;
    border-radius: 9999px;
  }

  &__preview-mask {
    position: absolute;
    inset: 0;
    border-radius: 9999px;
    border: 1rem solid #000;
    opacity: 0.2;
    z-index: 0;
  }

  &__preview-action {
    position: absolute;
    left: 0.5rem;
    bottom: -0.25rem;
    z-index: 10;
  }

  &__hint {
    background: #fff;
    border-radius: 1rem;
    margin-top: 1.25rem;
    padding: 0.5rem;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.25rem;

    p {
      font-family: var(--font-family-fa);
      font-size: 0.6875rem;
      line-height: 1.5;
      text-align: right;
      margin: 0;

      @media (min-width: 1024px) {
        font-size: 0.7rem;
      }
    }
  }

  &__error {
    margin-top: 0.25rem;
    text-align: right;
    font-family: var(--font-family-fa);
    font-size: 0.6875rem;
    color: var(--color-error);

    @media (min-width: 1280px) {
      font-size: 0.875rem;
    }
  }

  &__hint-icon {
    color: var(--color-prim-gray);
    min-height: 1.25rem;
    min-width: 1.25rem;

    @media (min-width: 1024px) {
      min-height: 2rem;
      min-width: 2rem;
    }
  }
}
</style>
