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

import { Empty, Input, Modal, Pagination, Spin, Tabs } from 'ant-design-vue';

import MIcon from '#/components/icon/icon.vue';
import { useFileGalleryFilter } from '#/composables/use-file-gallery-filter';
import { resolveTypeIcon } from '#/composables/use-file-type-icon';
import type { FileGalleryItem } from '#/api/core/file-gallery';
import { getFileGalleryList } from '#/api/core/file-gallery';

const props = withDefaults(
  defineProps<{
    open: boolean;
    multiple?: boolean;
    maxCount?: number;
    /**
     * 限制可选择的文件类型（按 xk_file_type.value 过滤）
     * - 不传：显示全部类型（默认行为，保持向后兼容）
     * - 传数组：只展示指定类型 tab，且自动锁定到第一个匹配的类型
     * 例如图片：[0]；图片+视频：[0, 1]；文件：[6]
     */
    acceptTypes?: number[];
  }>(),
  {
    multiple: false,
    maxCount: 1,
    acceptTypes: () => [] as number[],
  },
);

const emit = defineEmits<{
  'update:open': [value: boolean];
  select: [urls: string[]];
}>();

const loading = ref(false);
const items = ref<FileGalleryItem[]>([]);
const total = ref(0);
const page = ref(1);
const pageSize = ref(24);
const pageSizeOptions = ['12', '24', '48'];
const selected = ref<string[]>([]);

/** 按 acceptTypes 隔离 localStorage，避免图片/视频选择器互相污染 */
const storageKey = computed(
  () =>
    `file-picker-type-${
      props.acceptTypes?.length ? props.acceptTypes.join('-') : 'all'
    }`,
);

const {
  activeType,
  keyword,
  tabs,
  setActiveType,
  buildListParams,
  fileTypes,
  typesLoading,
} = useFileGalleryFilter(() => storageKey.value);

/**
 * 实际渲染的 tabs（按 acceptTypes 过滤）
 */
const visibleTabs = computed(() => {
  if (!props.acceptTypes || props.acceptTypes.length === 0) {
    return tabs.value;
  }
  return tabs.value.filter(
    (tab) => tab.value !== -1 && props.acceptTypes.includes(Number(tab.value)),
  );
});

/**
 * 强制锁定到 acceptTypes 首项（打开弹窗 / 类型字典就绪时调用）
 */
function lockAcceptType() {
  const arr = props.acceptTypes;
  if (!arr || arr.length === 0) {
    return;
  }
  if (activeType.value !== arr[0]) {
    setActiveType(arr[0]!);
  }
}

/**
 * 是否允许拉列表：有 acceptTypes 时需等类型字典加载完且 visibleTabs 非空
 */
function canLoadList() {
  if (typesLoading.value) {
    return false;
  }
  if (props.acceptTypes && props.acceptTypes.length > 0) {
    return visibleTabs.value.length > 0;
  }
  return true;
}

async function load() {
  if (!canLoadList()) {
    return;
  }
  loading.value = true;
  try {
    const res = await getFileGalleryList(
      buildListParams(page.value, pageSize.value),
    );
    const data = (res as any)?.data ?? res;
    items.value = data?.items ?? [];
    total.value = data?.total ?? 0;
  } finally {
    loading.value = false;
  }
}

watch(
  () => props.acceptTypes,
  (arr) => {
    if (!arr || arr.length === 0) {
      return;
    }
    lockAcceptType();
    page.value = 1;
    void load();
  },
  { immediate: true },
);

let searchTimer: ReturnType<typeof setTimeout> | null = null;

watch(
  () => props.open,
  (val) => {
    if (val) {
      selected.value = [];
      page.value = 1;
      // 打开时再次按 acceptTypes 重锁，避免沿用其它选择器的视频 tab
      lockAcceptType();
      void load();
    }
  },
);

watch(typesLoading, (val) => {
  if (!val && props.open) {
    lockAcceptType();
    void load();
  }
});

function toggleSelect(url: string) {
  if (props.multiple) {
    const idx = selected.value.indexOf(url);
    if (idx >= 0) {
      selected.value = selected.value.filter((u) => u !== url);
      return;
    }
    if (selected.value.length >= props.maxCount) {
      return;
    }
    selected.value = [...selected.value, url];
    return;
  }
  selected.value = [url];
}

function isSelected(url: string) {
  return selected.value.includes(url);
}

function handleOk() {
  if (!selected.value.length) {
    return;
  }
  emit('select', [...selected.value]);
  emit('update:open', false);
}

function handleCancel() {
  emit('update:open', false);
}

function onPageChange(p: number, size?: number) {
  page.value = p;
  if (size) {
    pageSize.value = size;
  }
  void load();
}

function onShowSizeChange(_current: number, size: number) {
  page.value = 1;
  pageSize.value = size;
  void load();
}

function onTabChange(key: string | number) {
  setActiveType(Number(key));
  page.value = 1;
  void load();
}

function onSearch(value: string) {
  keyword.value = value;
  page.value = 1;
  if (searchTimer) {
    clearTimeout(searchTimer);
  }
  searchTimer = setTimeout(() => {
    void load();
  }, 300);
}

function itemIcon(item: FileGalleryItem) {
  return item.type_icon || resolveTypeIcon(item.type, fileTypes.value);
}

/** 图片类型展示缩略图（xk_file.type：0=图片；兼容历史错误写入的 1） */
function isImageItem(item: FileGalleryItem) {
  return Number(item.type) === 0 || Number(item.type) === 1;
}
</script>

<template>
  <Modal
    :open="open"
    title="从文件库选择"
    width="820px"
    :ok-button-props="{ disabled: !selected.length }"
    @ok="handleOk"
    @cancel="handleCancel"
  >
    <Tabs :active-key="String(activeType)" size="small" @change="onTabChange">
      <Tabs.TabPane v-for="tab in visibleTabs" :key="String(tab.value)">
        <template #tab>
          <span class="tab-label">
            <MIcon v-if="tab.icon" :icon="tab.icon" size="12" />
            {{ tab.label }}
          </span>
        </template>
      </Tabs.TabPane>
    </Tabs>

    <div class="picker-toolbar">
      <Input.Search
        v-model:value="keyword"
        allow-clear
        placeholder="按文件名搜索"
        size="small"
        @search="onSearch"
        @change="(e) => onSearch(e.target.value ?? '')"
      />
    </div>

    <Spin :spinning="loading || typesLoading">
      <div v-if="items.length" class="gallery-grid">
        <div
          v-for="item in items"
          :key="item.id"
          class="gallery-card"
          :class="{ active: isSelected(item.url) }"
          @click="toggleSelect(item.url)"
        >
          <img
            v-if="isImageItem(item)"
            :src="item.url"
            alt=""
            class="gallery-thumb"
          />
          <div v-else class="gallery-file-icon">
            <MIcon :icon="itemIcon(item)" size="28" />
            <span class="file-name">{{
              item.file_name || item.original_name || '未命名'
            }}</span>
          </div>
          <div class="gallery-meta">
            <div class="file-title">
              <MIcon :icon="itemIcon(item)" size="11" class="meta-icon" />
              {{ item.file_name || item.original_name || '-' }}
            </div>
            <div>{{ item.file_size_text || '-' }}</div>
          </div>
        </div>
      </div>
      <Empty v-else description="暂无文件" />
      <div v-if="total > 0" class="gallery-pagination">
        <Pagination
          :current="page"
          :page-size="pageSize"
          :page-size-options="pageSizeOptions"
          :total="total"
          size="small"
          show-less-items
          show-size-changer
          :show-total="(t) => `共 ${t} 条`"
          @change="onPageChange"
          @show-size-change="onShowSizeChange"
        />
      </div>
    </Spin>
  </Modal>
</template>

<style scoped lang="scss">
@use './picker-card-theme.scss' as picker;

.tab-label {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.picker-toolbar {
  margin-bottom: 12px;
}

.gallery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(118px, 1fr));
  gap: 12px;
  max-height: 420px;
  overflow-y: auto;
  padding: 4px;
}

.gallery-card {
  @include picker.picker-card-base;
  padding: 6px;

  &.active {
    border-color: #165dff;
    background: #f2f7ff;
    box-shadow: 0 0 0 2px rgba(22, 93, 255, 0.15);
  }
}

.gallery-thumb {
  width: 100%;
  height: 88px;
  object-fit: cover;
  border-radius: 6px;
  background: #f5f5f5;
}

.gallery-file-icon {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 88px;
  border-radius: 6px;
  background: #f5f5f5;
  color: #4e5969;
  padding: 6px;

  .file-name {
    font-size: 11px;
    text-align: center;
    word-break: break-all;
    line-height: 1.2;
    max-height: 28px;
    overflow: hidden;
  }
}

.gallery-meta {
  font-size: 11px;
  color: #86909c;

  .file-title {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    color: #1d2129;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

.gallery-pagination {
  margin-top: 16px;
  display: flex;
  justify-content: center;
}

.dark {
  .gallery-card {
    @include picker.picker-card-dark-props;

    &.active {
      border-color: #60a5fa;
      background: #374151;
      box-shadow: 0 0 0 2px rgba(96, 165, 250, 0.2);
    }
  }

  .gallery-thumb,
  .gallery-file-icon {
    background: #374151;
  }

  .gallery-meta {
    @include picker.picker-text-secondary-dark;

    .file-title {
      color: #e5e7eb;
    }
  }
}
</style>
