<script setup lang="ts">
import { computed, ref } from 'vue';

import {
  CircleCheck,
  Delete,
  Document,
  UploadFilled,
} from '@element-plus/icons-vue';
import { ElButton, ElIcon, ElLink, ElMessage } from 'element-plus';

import { api } from '#/api/request';
import { $t } from '#/locales';

import {
  appendWorkflowFileValues,
  buildWorkflowFileValueFromUpload,
  formatWorkflowFileSize,
  normalizeWorkflowFileValues,
  validateWorkflowFileSelection,
  validateWorkflowFileValues,
  WORKFLOW_FILE_LIMITS,
} from './workflowFileValue';

const props = defineProps({
  disabled: {
    type: Boolean,
    default: false,
  },
  modelValue: {
    type: [Array, Object],
    default: undefined,
  },
});

const emit = defineEmits(['update:modelValue']);

const uploadLoading = ref(false);
const dragActive = ref(false);
const fileInputRef = ref<HTMLInputElement | null>(null);

const currentFiles = computed(() =>
  normalizeWorkflowFileValues(props.modelValue),
);
const maxSingleFileSizeText = formatWorkflowFileSize(
  WORKFLOW_FILE_LIMITS.maxSingleSize,
).replace('.0 ', ' ');

function triggerSelectFile() {
  if (props.disabled || uploadLoading.value) {
    return;
  }
  fileInputRef.value?.click();
}

async function uploadFiles(files: File[]) {
  if (props.disabled || files.length === 0) {
    return;
  }

  uploadLoading.value = true;
  try {
    validateWorkflowFileSelection(currentFiles.value, files);
    const uploadedFiles = [];
    for (const file of files) {
      const res = await api.upload('/api/v1/commons/upload', { file }, {});
      uploadedFiles.push(
        buildWorkflowFileValueFromUpload(file, res?.data?.path),
      );
    }
    if (props.disabled) {
      return;
    }
    const nextFiles = appendWorkflowFileValues(
      currentFiles.value,
      uploadedFiles,
    );
    validateWorkflowFileValues(nextFiles);
    emit('update:modelValue', nextFiles);
  } catch (error: any) {
    ElMessage.error(error?.message || '文件上传失败');
    console.error('工作流文件上传失败', error);
  } finally {
    uploadLoading.value = false;
  }
}

async function handleNativeFileChange(event: Event) {
  const input = event.target as HTMLInputElement;
  await uploadFiles([...(input.files || [])]);
  input.value = '';
}

function setDragActive(active: boolean) {
  if (!props.disabled) {
    dragActive.value = active;
  }
}

async function handleDrop(event: DragEvent) {
  dragActive.value = false;
  if (props.disabled || uploadLoading.value) {
    return;
  }
  await uploadFiles([...(event.dataTransfer?.files || [])]);
}

function removeFile(filePath: string) {
  if (props.disabled) {
    return;
  }
  const nextFiles = currentFiles.value.filter(
    (item) => item.filePath !== filePath,
  );
  emit('update:modelValue', nextFiles);
}
</script>

<template>
  <div class="workflow-file-input">
    <input
      ref="fileInputRef"
      class="workflow-file-input__native"
      type="file"
      :disabled="disabled"
      multiple
      @change="handleNativeFileChange"
    />

    <div
      v-if="currentFiles.length === 0"
      class="workflow-file-input__dropzone"
      :class="{ 'is-disabled': disabled, 'is-dragging': dragActive }"
      @dragenter.prevent="setDragActive(true)"
      @dragover.prevent="setDragActive(true)"
      @dragleave.prevent="setDragActive(false)"
      @drop.prevent="handleDrop"
    >
      <button
        class="workflow-file-input__upload-trigger"
        type="button"
        :disabled="disabled || uploadLoading"
        @click="triggerSelectFile"
      >
        <ElIcon class="workflow-file-input__upload-icon">
          <UploadFilled />
        </ElIcon>
        <span class="workflow-file-input__dropzone-copy">
          <span>
            {{
              disabled
                ? '未上传文件'
                : uploadLoading
                  ? '正在上传…'
                  : '拖入文件或点击上传'
            }}
          </span>
          <small>单个文件不超过 {{ maxSingleFileSizeText }}</small>
        </span>
      </button>
    </div>

    <div v-if="currentFiles.length > 0" class="workflow-file-input__list">
      <div
        v-for="item in currentFiles"
        :key="item.filePath"
        class="workflow-file-input__summary"
      >
        <ElIcon class="workflow-file-input__file-icon">
          <Document />
        </ElIcon>
        <div class="workflow-file-input__content">
          <div class="workflow-file-input__name">
            {{ item.fileName }}
          </div>
          <div class="workflow-file-input__meta">
            <span>{{ formatWorkflowFileSize(item.size) }}</span>
            <span class="workflow-file-input__ready">
              <ElIcon><CircleCheck /></ElIcon>
              已上传
            </span>
          </div>
        </div>
        <ElLink
          v-if="item.url || item.filePath"
          :href="item.url || item.filePath"
          target="_blank"
          type="primary"
        >
          {{ $t('button.view') }}
        </ElLink>
        <ElButton
          v-if="!disabled"
          :icon="Delete"
          text
          circle
          aria-label="删除文件"
          @click="removeFile(item.filePath)"
        />
      </div>
    </div>
  </div>
</template>

<style scoped>
.workflow-file-input {
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 100%;
}

.workflow-file-input__native {
  display: none;
}

.workflow-file-input__dropzone {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: 56px;
  color: var(--el-text-color-secondary);
  background: hsl(var(--surface-subtle));
  border: 1px dashed var(--el-border-color);
  border-radius: var(--radius-control);
  transition:
    color var(--motion-duration-base) var(--motion-ease-standard),
    background-color var(--motion-duration-base) var(--motion-ease-standard),
    border-color var(--motion-duration-base) var(--motion-ease-standard);
}

.workflow-file-input__dropzone:hover,
.workflow-file-input__dropzone.is-dragging {
  color: hsl(var(--primary));
  background: hsl(var(--primary) / 6%);
  border-color: hsl(var(--primary) / 48%);
}

.workflow-file-input__dropzone.is-disabled,
.workflow-file-input__dropzone.is-disabled:hover {
  color: var(--el-text-color-placeholder);
  background: var(--el-fill-color-light);
  border-color: var(--el-border-color-lighter);
}

.workflow-file-input__upload-trigger {
  display: inline-flex;
  flex: 1;
  gap: var(--space-2);
  align-items: center;
  align-self: stretch;
  min-width: 0;
  padding: var(--space-2) var(--space-3);
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  background: transparent;
  border: 0;
}

.workflow-file-input__upload-trigger:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px hsl(var(--primary) / 12%);
}

.workflow-file-input__upload-trigger:disabled {
  cursor: default;
  opacity: 0.72;
}

.workflow-file-input__upload-icon {
  font-size: 18px;
}

.workflow-file-input__dropzone-copy {
  display: flex;
  flex-flow: row wrap;
  gap: var(--space-1) var(--space-2);
  align-items: center;
  line-height: 1.4;
}

.workflow-file-input__dropzone-copy small {
  font-size: 11px;
  color: var(--el-text-color-placeholder);
}

.workflow-file-input__list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.workflow-file-input__summary {
  display: flex;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-3);
  background: var(--el-fill-color-blank);
  border: 1px solid var(--el-border-color-light);
  border-radius: var(--radius-control);
}

.workflow-file-input__file-icon {
  flex: 0 0 auto;
  font-size: 18px;
  color: hsl(var(--primary));
}

.workflow-file-input__content {
  flex: 1;
  min-width: 0;
}

.workflow-file-input__name {
  font-size: 13px;
  font-weight: 600;
  color: var(--el-text-color-primary);
  overflow-wrap: anywhere;
}

.workflow-file-input__meta {
  display: flex;
  gap: 10px;
  align-items: center;
  margin-top: 6px;
  font-size: 12px;
  color: var(--el-text-color-secondary);
}

.workflow-file-input__ready {
  display: inline-flex;
  gap: var(--space-1);
  align-items: center;
  color: var(--el-color-success);
}
</style>
