<script setup>
import { ref, computed, watch, onMounted, onBeforeUnmount } from "vue";
import { useNamespace, useLocale } from "@yh-ui/hooks";
import { useComponentTheme } from "@yh-ui/theme";
import {
  aiAttachmentsProps,
  aiAttachmentsEmits
} from "./ai-attachments";
import { YhAiFileCard } from "../../ai-file-card";
import { YhIcon } from "../../icon";
defineOptions({
  name: "YhAiAttachments"
});
const props = defineProps(aiAttachmentsProps);
const emit = defineEmits(aiAttachmentsEmits);
const ns = useNamespace("ai-attachments");
const { t } = useLocale();
const { themeStyle } = useComponentTheme(
  "ai-attachments",
  computed(() => props.themeOverrides)
);
const internalItems = ref([...props.items]);
watch(
  () => props.items,
  (newItems) => {
    internalItems.value = [...newItems];
  },
  { deep: true }
);
const normalizePlaceholder = (raw, type) => {
  if (typeof raw === "function") {
    return raw(type);
  }
  if (typeof raw === "string") {
    return { icon: "upload", title: raw };
  }
  return raw || {};
};
const computedPlaceholder = computed(() => {
  if (props.placeholder === void 0) {
    return {
      icon: "upload",
      title: t("ai.attachments.clickToUpload"),
      description: "Support file type: image, video, audio, document, etc."
    };
  }
  const norm = normalizePlaceholder(props.placeholder, "inline");
  return {
    icon: norm.icon ?? "upload",
    title: norm.title,
    description: norm.description
  };
});
const computedDropPlaceholder = computed(() => {
  if (props.placeholder === void 0) {
    return {
      icon: "upload",
      title: t("ai.attachments.dropTip"),
      description: void 0
    };
  }
  const norm = normalizePlaceholder(props.placeholder, "drop");
  return {
    icon: norm.icon ?? "upload",
    title: norm.title,
    description: norm.description
  };
});
const canUpload = computed(() => {
  if (props.disabled) return false;
  const limitValue = props.maxCount ?? props.limit;
  if (limitValue !== void 0 && internalItems.value.length >= limitValue) {
    return false;
  }
  return true;
});
const overflowClass = computed(() => {
  return ns.m(`overflow-${props.overflow}`);
});
const extensionIconMap = {
  pdf: "pdf",
  xlsx: "excel",
  xls: "excel",
  doc: "word",
  docx: "word",
  ppt: "ppt",
  pptx: "ppt",
  png: "image",
  jpg: "image",
  jpeg: "image",
  gif: "image",
  webp: "image",
  svg: "image",
  mp4: "video",
  webm: "video",
  mp3: "audio",
  wav: "audio",
  zip: "zip",
  md: "markdown",
  txt: "txt",
  java: "java",
  js: "javascript",
  ts: "javascript",
  py: "python"
};
function getItemIcon(item) {
  if (item.icon) return item.icon;
  const ext = (item.name || "").split(".").pop()?.toLowerCase();
  return ext ? extensionIconMap[ext] : void 0;
}
const fileInputRef = ref(null);
const isDragging = ref(false);
const dragCounter = ref(0);
const resolveDragTarget = () => {
  if (!props.dragTarget) return null;
  if (typeof props.dragTarget === "string") {
    return document.querySelector(props.dragTarget);
  }
  return props.dragTarget;
};
let externalDragTarget = null;
const onExternalDragEnter = (e) => handleDragEnter(e);
const onExternalDragLeave = (e) => handleDragLeave(e);
const onExternalDragOver = (e) => handleDragOver(e);
const onExternalDrop = (e) => handleDrop(e);
const bindExternalDragTarget = () => {
  const target = resolveDragTarget();
  if (!target) return;
  externalDragTarget = target;
  target.addEventListener("dragenter", onExternalDragEnter);
  target.addEventListener("dragleave", onExternalDragLeave);
  target.addEventListener("dragover", onExternalDragOver);
  target.addEventListener("drop", onExternalDrop);
};
const unbindExternalDragTarget = () => {
  if (!externalDragTarget) return;
  externalDragTarget.removeEventListener("dragenter", onExternalDragEnter);
  externalDragTarget.removeEventListener("dragleave", onExternalDragLeave);
  externalDragTarget.removeEventListener("dragover", onExternalDragOver);
  externalDragTarget.removeEventListener("drop", onExternalDrop);
  externalDragTarget = null;
};
onMounted(() => {
  if (props.dragTarget) {
    bindExternalDragTarget();
  }
});
onBeforeUnmount(() => {
  unbindExternalDragTarget();
});
watch(
  () => props.dragTarget,
  () => {
    unbindExternalDragTarget();
    if (props.dragTarget) {
      bindExternalDragTarget();
    }
  }
);
const dropContainerTarget = computed(() => {
  if (props.getDropContainer) {
    try {
      return props.getDropContainer();
    } catch {
      return null;
    }
  }
  return null;
});
const handleFileSelect = async (event) => {
  const input = event.target;
  const files = Array.from(input.files || []);
  if (files.length > 0) {
    await processFiles(files);
  }
  if (fileInputRef.value) {
    fileInputRef.value.value = "";
  }
};
const handleDragEnter = (e) => {
  e.preventDefault();
  dragCounter.value += 1;
  isDragging.value = true;
};
const handleDragLeave = (e) => {
  e.preventDefault();
  dragCounter.value = Math.max(0, dragCounter.value - 1);
  if (dragCounter.value === 0) {
    isDragging.value = false;
  }
};
const handleDragOver = (e) => {
  e.preventDefault();
};
const handleDrop = async (e) => {
  e.preventDefault();
  dragCounter.value = 0;
  isDragging.value = false;
  const files = Array.from(e.dataTransfer?.files || []);
  if (files.length > 0) {
    emit("upload-drop", files);
    await processFiles(files);
  }
};
const processFiles = async (files) => {
  const validFiles = files.filter((f) => f.type !== "" || f.name.includes("."));
  if (validFiles.length === 0) return;
  let filesToUpload = validFiles;
  const limitValue = props.maxCount ?? props.limit;
  if (limitValue !== void 0) {
    const totalCount = internalItems.value.length + validFiles.length;
    if (totalCount > limitValue) {
      emit("exceed", { files, maxCount: limitValue });
      const remaining = Math.max(0, limitValue - internalItems.value.length);
      filesToUpload = validFiles.slice(0, remaining);
    }
  }
  if (filesToUpload.length === 0) return;
  for (const file of filesToUpload) {
    if (props.beforeUpload) {
      const result = await props.beforeUpload(file);
      if (!result) continue;
    }
    const ext = (file.name || "").split(".").pop()?.toLowerCase();
    let guessedType = file.type.split("/")[0] || "";
    if (!guessedType && ext) {
      if (["png", "jpg", "jpeg", "gif", "webp", "svg"].includes(ext)) {
        guessedType = "image";
      } else if (["mp4", "webm", "ogg"].includes(ext)) {
        guessedType = "video";
      } else if (["mp3", "wav", "aac"].includes(ext)) {
        guessedType = "audio";
      } else if ([
        "pdf",
        "doc",
        "docx",
        "xls",
        "xlsx",
        "ppt",
        "pptx",
        "txt",
        "md",
        "csv",
        "log",
        "json",
        "xml",
        "yaml",
        "yml"
      ].includes(ext)) {
        guessedType = "document";
      }
    }
    if (!guessedType) guessedType = "file";
    const fileItem = {
      uid: Date.now() + Math.random(),
      name: file.name,
      byte: file.size,
      type: guessedType,
      status: "uploading",
      percent: 0
    };
    internalItems.value.push(fileItem);
    emit("upload-change", file, internalItems.value);
    if (props.httpRequest) {
      try {
        await props.httpRequest({ file });
        const index = internalItems.value.findIndex((item) => item.uid === fileItem.uid);
        if (index !== -1) {
          internalItems.value[index] = {
            ...internalItems.value[index],
            status: "done",
            percent: 100
          };
        }
        emit("upload-success", { success: true }, file, internalItems.value);
      } catch (error) {
        const index = internalItems.value.findIndex((item) => item.uid === fileItem.uid);
        if (index !== -1) {
          internalItems.value[index] = {
            ...internalItems.value[index],
            status: "error",
            error: String(error)
          };
        }
        emit("upload-error", error, file, internalItems.value);
      }
    }
  }
  emit("update:items", internalItems.value);
};
const triggerUpload = () => {
  if (!canUpload.value) return;
  fileInputRef.value?.click();
};
const handleDelete = (item, index) => {
  internalItems.value.splice(index, 1);
  emit("delete-card", item, index);
  emit("update:items", internalItems.value);
};
const listContainerRef = ref(null);
const scrollLeft = () => {
  if (listContainerRef.value) {
    listContainerRef.value.scrollLeft -= 200;
  }
};
const scrollRight = () => {
  if (listContainerRef.value) {
    listContainerRef.value.scrollLeft += 200;
  }
};
const showScrollButtons = computed(() => {
  if (props.overflow !== "scrollX") {
    return false;
  }
  return internalItems.value.length > 3;
});
</script>

<template>
  <div
    :class="[ns.b(), overflowClass]"
    :style="[themeStyle, props.overflow === 'scrollY' && props.scrollMaxHeight ? {
  maxHeight: props.scrollMaxHeight
} : {}]"
    v-bind="!props.dragTarget ? {
  onDragenter: handleDragEnter,
  onDragleave: handleDragLeave,
  onDragover: handleDragOver,
  onDrop: handleDrop
} : {}"
  >
    <!-- 文件列表 -->
    <div
      v-if="internalItems.length > 0"
      ref="listContainerRef"
      :class="ns.e('list')"
      :style="props.listStyle"
    >
      <div
        v-for="(item, index) in internalItems"
        :key="item.uid || index"
        :class="[ns.e('item'), ns.is('error', item.status === 'error')]"
      >
        <slot name="file-item" :item="item" :index="index">
          <YhAiFileCard
            :name="item.name"
            :byte="item.byte"
            :type="item.type"
            :icon="getItemIcon(item)"
            :src="item.url || item.thumbUrl"
            :description="item.status === 'error' ? item.error : item.description"
            :loading="item.status === 'uploading'"
            size="small"
          >
            <template #default>
              <!-- 上传进度（进度条 + 百分比） -->
              <div v-if="item.status === 'uploading'" :class="ns.e('progress-wrap')">
                <span :class="ns.e('progress-text')">{{ item.percent ?? 0 }}%</span>
                <div :class="ns.e('progress')">
                  <div
                    :class="ns.e('progress-bar')"
                    :style="{
  width: `${item.percent || 0}%`
}"
                  ></div>
                </div>
              </div>
              <!-- 删除按钮 -->
              <button
                v-if="item.status !== 'uploading'"
                :class="ns.e('delete-btn')"
                @click.stop="handleDelete(item, index)"
              >
                <YhIcon name="close" />
              </button>
            </template>
          </YhAiFileCard>
        </slot>
      </div>
    </div>

    <!-- 上传按钮 / 占位符 -->
    <div
      v-if="!hideUpload && canUpload"
      :class="[ns.e('upload'), ns.is('dragging', isDragging)]"
      @click="triggerUpload"
    >
      <slot name="upload-button" :can-upload="canUpload">
        <div :class="ns.e('upload-content')">
          <YhIcon :name="computedPlaceholder.icon || 'upload'" :size="props.uploadIconSize" />
          <div :class="ns.e('upload-text')">
            <div :class="ns.e('upload-title')">
              {{ isDragging ? computedDropPlaceholder.title : computedPlaceholder.title }}
            </div>
            <div v-if="computedPlaceholder.description" :class="ns.e('upload-description')">
              {{ computedPlaceholder.description }}
            </div>
          </div>
        </div>
      </slot>
    </div>

    <!-- 拖拽遮罩 -->
    <Teleport :to="dropContainerTarget" :disabled="!dropContainerTarget">
      <div v-if="isDragging" :class="ns.e('drop-mask')">
        <slot name="drop-area">
          <div :class="ns.e('drop-content')">
            <YhIcon :name="computedDropPlaceholder.icon || 'upload'" :size="props.uploadIconSize" />
            <div :class="ns.e('drop-text')">
              {{ computedDropPlaceholder.title || t("ai.attachments.dropTip") }}
            </div>
          </div>
        </slot>
      </div>
    </Teleport>

    <!-- 滚动按钮 -->
    <button
      v-if="showScrollButtons"
      :class="[ns.e('scroll-btn'), ns.e('scroll-prev')]"
      @click="scrollLeft"
    >
      <YhIcon name="arrow-left" />
    </button>
    <button
      v-if="showScrollButtons"
      :class="[ns.e('scroll-btn'), ns.e('scroll-next')]"
      @click="scrollRight"
    >
      <YhIcon name="arrow-right" />
    </button>

    <!-- 隐藏的文件输入 -->
    <input
      ref="fileInputRef"
      type="file"
      :class="ns.e('file-input')"
      multiple
      :disabled="disabled"
      @change="handleFileSelect"
    />
  </div>
</template>

<style>
@charset "UTF-8";
/**
 * YH-UI Sass Variables
 * 原具体的 CSS 变量输出规则 (:root, html.dark 等) 已移至 root.scss，以防止各组件样式编译时产生重复的全局变量 CSS 冗余。
 */
.yh-ai-attachments {
  --yh-ai-attachments-bg-color: var(--yh-bg-color, #fff);
  --yh-ai-attachments-border-color: var(--yh-border-color, #dcdfe6);
  --yh-ai-attachments-border-dashed-color: var(--yh-border-colordashed, #d9d9d9);
  --yh-ai-attachments-text-color: var(--yh-text-color, #303133);
  --yh-ai-attachments-text-secondary-color: var(--yh-text-color-secondary, #909399);
  --yh-ai-attachments-hover-bg-color: var(--yh-fill-color-light, #f5f7fa);
  --yh-ai-attachments-primary-color: var(--yh-color-primary, #409eff);
  --yh-ai-attachments-shadow: var(--yh-box-shadow, 0 2px 12px 0 rgba(0, 0, 0, 0.1));
  position: relative;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  padding: 8px;
  background: var(--yh-ai-attachments-bg-color);
  border-radius: 8px;
  box-sizing: border-box;
}
.yh-ai-attachments--overflow-scrollX {
  flex-wrap: nowrap;
  overflow-x: auto;
  overflow-y: hidden;
}
.yh-ai-attachments--overflow-scrollX .yh-ai-attachments__list {
  flex-wrap: nowrap;
  overflow-x: auto;
}
.yh-ai-attachments--overflow-scrollX .yh-ai-attachments__item {
  flex-shrink: 0;
}
.yh-ai-attachments--overflow-scrollY {
  flex-wrap: wrap;
  overflow-y: auto;
  max-height: 400px;
}
.yh-ai-attachments--overflow-wrap {
  flex-wrap: wrap;
}
.yh-ai-attachments__list {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}
.yh-ai-attachments__item {
  position: relative;
  width: 120px;
  min-width: 120px;
  height: 120px;
  flex-shrink: 0;
}
.yh-ai-attachments__item:hover .yh-ai-attachments__delete-btn {
  opacity: 1;
}
.yh-ai-attachments__item.is-error .yh-ai-file-card {
  border-color: var(--yh-color-danger, #f56c6c);
}
.yh-ai-attachments__item.is-error .yh-ai-file-card .yh-ai-file-card__description {
  color: var(--yh-color-danger, #f56c6c);
}
.yh-ai-attachments__item .yh-ai-file-card {
  width: 100%;
  height: 100%;
  min-width: 0;
  padding: 6px;
}
.yh-ai-attachments__item .yh-ai-file-card .yh-ai-file-card__info {
  min-width: 0;
  width: 100%;
}
.yh-ai-attachments__item .yh-ai-file-card .yh-ai-file-card__name {
  max-width: 100%;
  width: 100%;
  min-width: 0;
  font-size: 12px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.yh-ai-attachments__item .yh-ai-file-card .yh-ai-file-card__size {
  font-size: 11px;
}
.yh-ai-attachments__upload {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1 1 220px;
  width: auto;
  min-width: 220px;
  height: 120px;
  padding: 16px;
  border: 2px dashed var(--yh-ai-attachments-border-dashed-color);
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.3s ease;
  box-sizing: border-box;
}
.yh-ai-attachments__upload:hover {
  border-color: var(--yh-ai-attachments-primary-color);
  background: var(--yh-ai-attachments-hover-bg-color);
}
.yh-ai-attachments__upload.is-dragging {
  border-color: var(--yh-ai-attachments-primary-color);
  background: rgba(var(--yh-ai-attachments-primary-color), 0.1);
}
.yh-ai-attachments__upload-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  color: var(--yh-ai-attachments-text-secondary-color);
}
.yh-ai-attachments__upload-text {
  text-align: center;
}
.yh-ai-attachments__upload-title {
  font-size: 12px;
  line-height: 1.4;
  color: var(--yh-ai-attachments-text-color);
}
.yh-ai-attachments__upload-description {
  font-size: 12px;
  line-height: 1.4;
  color: var(--yh-ai-attachments-text-secondary-color);
}
.yh-ai-attachments__progress-wrap {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  background: rgba(255, 255, 255, 0.9);
  border-radius: 8px;
  z-index: 5;
}
.yh-ai-attachments__progress-text {
  font-size: 14px;
  font-weight: 600;
  color: var(--yh-ai-attachments-primary-color);
}
.yh-ai-attachments__progress {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 4px;
  background: var(--yh-fill-color, #f5f7fa);
  border-radius: 0 0 8px 8px;
  overflow: hidden;
}
.yh-ai-attachments__progress-wrap .yh-ai-attachments__progress {
  position: relative;
  width: 80px;
  left: auto;
  right: auto;
}
.yh-ai-attachments__progress-bar {
  height: 100%;
  background: var(--yh-ai-attachments-primary-color);
  transition: width 0.3s ease;
}
.yh-ai-attachments__delete-btn {
  position: absolute;
  top: 4px;
  right: 4px;
  width: 20px;
  height: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.5);
  color: #fff;
  border: none;
  border-radius: 50%;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.3s ease;
  z-index: 10;
}
.yh-ai-attachments__delete-btn:hover {
  background: rgba(0, 0, 0, 0.7);
}
.yh-ai-attachments__drop-mask {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(var(--yh-ai-attachments-primary-color), 0.1);
  border: 2px dashed var(--yh-ai-attachments-primary-color);
  border-radius: 8px;
  z-index: 100;
}
.yh-ai-attachments__drop-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  color: var(--yh-ai-attachments-primary-color);
}
.yh-ai-attachments__drop-text {
  font-size: 12px;
  font-weight: 500;
}
.yh-ai-attachments__scroll-btn {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--yh-ai-attachments-bg-color);
  border: 1px solid var(--yh-ai-attachments-border-color);
  border-radius: 50%;
  cursor: pointer;
  z-index: 10;
  transition: all 0.3s ease;
}
.yh-ai-attachments__scroll-btn:hover {
  background: var(--yh-ai-attachments-hover-bg-color);
  border-color: var(--yh-ai-attachments-primary-color);
}
.yh-ai-attachments__scroll-btn--scroll-prev {
  left: -16px;
}
.yh-ai-attachments__scroll-btn--scroll-next {
  right: -16px;
}
.yh-ai-attachments__file-input {
  display: none;
}
</style>
