<script lang="ts" setup>
import type { UploadProps } from 'element-plus';

import { ref, watch } from 'vue';

import { useAppConfig } from '@easyflow/hooks';
import { useAccessStore } from '@easyflow/stores';

import { Plus } from '@element-plus/icons-vue';
import { ElIcon, ElImage, ElMessage, ElUpload } from 'element-plus';

import { $t } from '#/locales';
import {
  normalizeUploadError,
  resolveUploadPath,
} from '#/utils/upload-response';

const props = defineProps({
  action: {
    type: String,
    default: '/api/v1/commons/upload',
  },
  fileSize: {
    type: Number,
    default: 2,
  },
  allowedImageTypes: {
    type: Array<string>,
    default: () => ['image/gif', 'image/jpeg', 'image/png', 'image/webp'],
  },
  modelValue: { type: String, default: '' },
  shape: {
    type: String,
    default: 'circle',
  },
  size: {
    type: Number,
    default: 100,
  },
  theme: {
    type: String,
    default: 'default',
  },
});

const emit = defineEmits(['success', 'error', 'update:modelValue']);
const accessStore = useAccessStore();
const headers = ref({
  'easyflow-token': accessStore.accessToken,
});

const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
const localImageUrl = ref(props.modelValue);
const handleAvatarSuccess: UploadProps['onSuccess'] = (
  response,
  uploadFile,
) => {
  try {
    const imageUrl = resolveUploadPath(response);
    localImageUrl.value = URL.createObjectURL(uploadFile.raw!);
    emit('success', imageUrl);
    emit('update:modelValue', imageUrl);
  } catch (error) {
    const normalizedError = normalizeUploadError(error);
    ElMessage.error(normalizedError.message);
    emit('error', normalizedError);
  }
};
const handleAvatarError: UploadProps['onError'] = (error) => {
  const normalizedError = normalizeUploadError(error);
  ElMessage.error(normalizedError.message);
  emit('error', normalizedError);
};

watch(
  () => props.modelValue,
  (newVal) => {
    localImageUrl.value = newVal;
  },
  { immediate: true },
);

const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
  if (!props.allowedImageTypes.includes(rawFile.type)) {
    const formatTypes = props.allowedImageTypes
      .map((type: string) => {
        const parts = type.split('/');
        return parts[1] ? parts[1].toUpperCase() : '';
      })
      .filter(Boolean);
    ElMessage.error(
      $t('cropper.message.avatarFormat', { format: formatTypes.join(', ') }),
    );
    return false;
  } else if (rawFile.size / 1024 / 1024 > props.fileSize) {
    ElMessage.error(
      $t('cropper.message.avatarSize', { limit: props.fileSize }),
    );
    return false;
  }
  return true;
};
</script>

<template>
  <ElUpload
    class="avatar-uploader"
    :class="[
      `avatar-uploader--${props.theme}`,
      { 'avatar-uploader--rounded': props.shape === 'rounded' },
    ]"
    :style="{ '--avatar-size': `${props.size}px` }"
    :action="`${apiURL}${props.action}`"
    :headers="headers"
    :show-file-list="false"
    :on-success="handleAvatarSuccess"
    :on-error="handleAvatarError"
    :before-upload="beforeAvatarUpload"
  >
    <ElImage
      v-if="localImageUrl"
      :src="localImageUrl"
      class="avatar"
      fit="cover"
    />
    <ElIcon v-else class="avatar-uploader-icon"><Plus /></ElIcon>
  </ElUpload>
</template>

<style scoped>
.avatar-uploader .avatar {
  display: block;
  width: var(--avatar-size);
  height: var(--avatar-size);
}
</style>

<style>
.avatar-uploader .el-upload {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--avatar-size);
  height: var(--avatar-size);
  overflow: hidden;
  cursor: pointer;
  background: hsl(var(--modal-surface-strong) / 92%);
  border: 1px solid hsl(var(--input) / 92%);
  border-radius: 50%;
  box-shadow:
    inset 0 1px 0 hsl(0deg 0% 100% / 72%),
    0 16px 28px -24px hsl(var(--foreground) / 24%);
  transition:
    transform var(--el-transition-duration-fast),
    border-color var(--el-transition-duration-fast),
    box-shadow var(--el-transition-duration-fast);
}

.avatar-uploader .el-upload:hover {
  border-color: hsl(var(--primary) / 52%);
  box-shadow:
    inset 0 1px 0 hsl(0deg 0% 100% / 76%),
    0 20px 36px -26px hsl(var(--modal-preview-glow) / 30%);
  transform: translateY(-1px);
}

.avatar-uploader--soft-panel .el-upload {
  background:
    radial-gradient(
      circle at top,
      hsl(var(--modal-preview-glow) / 14%),
      transparent 60%
    ),
    linear-gradient(
      145deg,
      hsl(var(--modal-preview-surface) / 98%),
      hsl(var(--modal-preview-surface-strong) / 90%)
    );
  border-color: hsl(var(--modal-preview-border) / 90%);
}

.avatar-uploader--rounded .el-upload {
  border-radius: 26px;
}

.el-icon.avatar-uploader-icon {
  width: var(--avatar-size);
  height: var(--avatar-size);
  font-size: 28px;
  color: hsl(var(--text-muted));
  text-align: center;
}
</style>
