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

import { Page, useVbenDrawer } from '@vben/common-ui';

import { EditOutlined, FolderAddOutlined, FolderOutlined } from '@ant-design/icons-vue';
import {
  Button,
  Card,
  Empty,
  Input,
  Modal,
  Pagination,
  Spin,
  Tabs,
  message,
} from 'ant-design-vue';

import type { ContextMenuItem } from '#/components/context-menu';
import { showContextMenu } from '#/components/context-menu';
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 {
  addFileToGroup,
  batchArchiveFiles,
  deleteFileGalleryItem,
  getFileGalleryList,
  moveFileGalleryType,
  renameFileGalleryItem,
  syncFileGalleryFromOss,
} from '#/api/core/file-gallery';
import { useFileGroupCache } from '#/composables/use-file-group-cache';

import FileDetailDrawer from './components/file-detail-drawer.vue';

defineOptions({ name: 'FileManagement' });

const loading = ref(false);
const archiving = ref(false);
const syncing = ref(false);
const renameOpen = ref(false);
const renameValue = ref('');
const renameTarget = ref<FileGalleryItem | null>(null);
const items = ref<FileGalleryItem[]>([]);
const total = ref(0);
const page = ref(1);
const pageSize = ref(24);
const pageSizeOptions = ['12', '24', '48', '96'];

const {
  activeType,
  activeGroupId,
  keyword,
  tabs,
  groupTabs,
  setActiveType,
  setActiveGroupId,
  buildListParams,
  fileTypes,
  typesLoading,
} = useFileGalleryFilter('file-management-active-type');

const { loadGroupOptions: refreshGroupOptionsForMenu } = useFileGroupCache();

const [DetailDrawer, detailDrawerApi] = useVbenDrawer({
  connectedComponent: FileDetailDrawer,
});

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

async function load() {
  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;
  }
}

async function handleSync() {
  syncing.value = true;
  try {
    const res = await syncFileGalleryFromOss();
    const data = (res as any)?.data ?? res;
    message.success(
      `同步完成：扫描 ${data?.scanned ?? 0} 个，新增 ${data?.synced ?? 0} 个，跳过 ${data?.skipped ?? 0} 个`,
    );
    await load();
  } finally {
    syncing.value = false;
  }
}

async function handleArchive() {
  archiving.value = true;
  try {
    const res = await batchArchiveFiles();
    const data = (res as any)?.data ?? res;
    message.success(
      `归档完成：修正分类 ${data?.reclassified ?? 0} 个，补全文件名 ${data?.file_name_filled ?? 0} 个`,
    );
    await load();
  } finally {
    archiving.value = false;
  }
}

function handleDelete(item: FileGalleryItem) {
  Modal.confirm({
    title: '确认删除',
    content: '删除后将同时移除 OSS 中的文件，此操作不可恢复，是否继续？',
    okText: '删除',
    okType: 'danger',
    cancelText: '取消',
    async onOk() {
      await deleteFileGalleryItem(item.id);
      message.success('删除成功');
      await load();
    },
  });
}

function openDetail(item: FileGalleryItem) {
  detailDrawerApi.setData({ id: item.id });
  detailDrawerApi.open();
}

function handleRename(item: FileGalleryItem) {
  renameTarget.value = item;
  renameValue.value = item.file_name || item.original_name || '';
  renameOpen.value = true;
}

async function submitRename() {
  const name = renameValue.value.trim();
  if (!name) {
    message.warning('文件名不能为空');
    return;
  }
  if (!renameTarget.value) {
    return;
  }
  await renameFileGalleryItem(renameTarget.value.id, name);
  message.success('重命名成功');
  renameOpen.value = false;
  renameTarget.value = null;
  await load();
}

async function handleMoveType(item: FileGalleryItem, type: number) {
  await moveFileGalleryType(item.id, type);
  message.success('分类已更新');
  await load();
}

async function handleAddToGroup(item: FileGalleryItem, groupId: number) {
  await addFileToGroup(item.id, groupId);
  message.success('已加入分组');
  if (!item.group_ids) {
    item.group_ids = [];
  }
  if (!item.group_ids.includes(groupId)) {
    item.group_ids.push(groupId);
  }
}

async function buildContextMenus(item: FileGalleryItem): Promise<ContextMenuItem[]> {
  const groups = await refreshGroupOptionsForMenu();
  const joinedIds = new Set(item.group_ids ?? []);
  const groupChildren: ContextMenuItem[] = groups.map((groupItem) => ({
    key: `group-${groupItem.id}`,
    label: groupItem.name,
    disabled: joinedIds.has(groupItem.id),
    handler: () => handleAddToGroup(item, groupItem.id),
  }));

  const moveChildren: ContextMenuItem[] = fileTypes.value.map((typeItem) => ({
    key: `move-${typeItem.value}`,
    label: typeItem.name,
    iconName: typeItem.icon,
    disabled: item.type === typeItem.value,
    handler: () => handleMoveType(item, typeItem.value),
  }));

  return [
    {
      key: 'rename',
      label: '重命名',
      icon: EditOutlined,
      handler: () => handleRename(item),
    },
    {
      key: 'add-group',
      label: '加入分组',
      icon: FolderAddOutlined,
      children: groupChildren.length ? groupChildren : [{ key: 'empty', label: '暂无分组', disabled: true }],
    },
    {
      key: 'move',
      label: '移动分类',
      icon: FolderOutlined,
      children: moveChildren,
    },
  ];
}

async function onContextMenu(e: MouseEvent, item: FileGalleryItem) {
  const menus = await buildContextMenus(item);
  showContextMenu(e, menus, item);
}

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

function onGroupTabChange(key: string | number) {
  setActiveGroupId(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 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 itemIcon(item: FileGalleryItem) {
  return item.type_icon || resolveTypeIcon(item.type, fileTypes.value);
}

watch(typesLoading, (val) => {
  if (!val) {
    void load();
  }
});
</script>

<template>
  <Page auto-content-height title="文件管理">
    <Card>
      <Tabs :active-key="String(activeType)" @change="onTabChange">
        <Tabs.TabPane v-for="tab in tabs" :key="String(tab.value)">
          <template #tab>
            <span class="tab-label">
              <MIcon v-if="tab.icon" :icon="tab.icon" size="14" />
              {{ tab.label }}
            </span>
          </template>
        </Tabs.TabPane>
      </Tabs>

      <Tabs
        v-if="groupTabs.length > 1"
        :active-key="String(activeGroupId)"
        class="group-tabs"
        size="small"
        @change="onGroupTabChange"
      >
        <Tabs.TabPane v-for="tab in groupTabs" :key="String(tab.value)" :tab="tab.label" />
      </Tabs>

      <div class="toolbar">
        <Input.Search
          v-model:value="keyword"
          allow-clear
          placeholder="按文件名搜索"
          style="width: 260px"
          @search="onSearch"
          @change="(e) => onSearch(e.target.value ?? '')"
        />
        <Button :loading="syncing" @click="handleSync">从 OSS 同步</Button>
        <Button type="primary" :loading="archiving" @click="handleArchive">一键归档</Button>
        <Button :loading="loading" @click="load">刷新</Button>
      </div>

      <Spin :spinning="loading">
        <div v-if="items.length" class="gallery-grid">
          <div
            v-for="item in items"
            :key="item.id"
            class="gallery-card"
            @click="openDetail(item)"
            @contextmenu.prevent="onContextMenu($event, item)"
          >
            <img v-if="item.type === 0" :src="item.url" alt="" class="gallery-thumb" />
            <div v-else class="gallery-file-icon">
              <MIcon :icon="itemIcon(item)" size="36" />
              <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="12" class="meta-icon" />
                {{ item.file_name || item.original_name || '-' }}
              </div>
              <div>{{ item.type_text || '-' }} · {{ item.file_size_text || '-' }}</div>
              <div>{{ item.created_at || '-' }}</div>
            </div>
            <Button danger size="small" block @click.stop="handleDelete(item)">删除</Button>
          </div>
        </div>
        <Empty v-else description="暂无文件，可点击「从 OSS 同步」导入" />
      </Spin>

      <div v-if="total > 0" class="gallery-pagination">
        <Pagination
          :current="page"
          :page-size="pageSize"
          :page-size-options="pageSizeOptions"
          :total="total"
          show-size-changer
          :show-total="(t) => `共 ${t} 条`"
          @change="onPageChange"
          @show-size-change="onShowSizeChange"
        />
      </div>
    </Card>

    <DetailDrawer />

    <Modal
      v-model:open="renameOpen"
      title="重命名"
      ok-text="保存"
      cancel-text="取消"
      @ok="submitRename"
    >
      <Input v-model:value="renameValue" placeholder="请输入文件名" />
    </Modal>
  </Page>
</template>

<style scoped lang="scss">
/* 素材库卡片：用主题变量适配明/暗色，避免硬编码浅色底 */
.tab-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.group-tabs {
  margin-bottom: 12px;
}

.toolbar {
  display: flex;
  gap: 12px;
  margin-bottom: 16px;
  flex-wrap: wrap;
}

.gallery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 16px;
}

.gallery-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px;
  border: 1px solid hsl(var(--border));
  border-radius: 8px;
  background: hsl(var(--card));
  color: hsl(var(--card-foreground));
  cursor: pointer;
}

.gallery-thumb {
  width: 100%;
  height: 120px;
  object-fit: cover;
  border-radius: 6px;
  background: hsl(var(--muted) / 0.45);
}

.gallery-file-icon {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 120px;
  border-radius: 6px;
  background: hsl(var(--muted) / 0.45);
  color: hsl(var(--muted-foreground));
  padding: 8px;

  .file-name {
    font-size: 12px;
    text-align: center;
    word-break: break-all;
    line-height: 1.3;
    max-height: 32px;
    overflow: hidden;
  }
}

.gallery-meta {
  font-size: 12px;
  color: hsl(var(--muted-foreground));
  text-align: center;

  .file-title {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
    color: hsl(var(--foreground));
    font-weight: 500;
    margin-bottom: 4px;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .meta-icon {
    flex-shrink: 0;
  }
}

.gallery-pagination {
  margin-top: 20px;
  display: flex;
  justify-content: center;
}
</style>
