<script setup lang="ts">
/**
 * 通用资源列表：按 resourceSchemas 渲染筛选/行信息/真只读。
 * 复杂模块已拆专用页；本页兜底简单 CRUD 与只读日志类资源。
 */
import { computed, reactive, ref } from 'vue'
import { onLoad, onReachBottom, onShow } from '@dcloudio/uni-app'
import { useDialog, useToast } from '@wot-ui/ui'
import AppNavbar from '@/components/layout/AppNavbar.vue'
import AppPageShell from '@/components/layout/AppPageShell.vue'
import AdminFilterBar from '../../components/AdminFilterBar.vue'
import AdminStatusBadge from '../../components/AdminStatusBadge.vue'
import AdminDetailDrawer from '../../components/AdminDetailDrawer.vue'
import { useAuth } from '@/composables/useAuth'
import { useAppMode } from '@/composables/useAppMode'
import { adminCreate, adminDelete, adminList, normalizeAdminList, normalizePagination } from '@/api'
import { resolveMediaUrl } from '@/utils/request'
import { getResourceSchema, pickField } from '../../config/resourceSchemas'

/** 已拆专用页的资源：进入通用 list 时自动跳转 */
const DEDICATED: Record<string, string> = {
  posts: '/subPackages/admin/pages/posts/list',
  works: '/subPackages/admin/pages/works/list',
  videos: '/subPackages/admin/pages/videos/list',
  attachments: '/subPackages/admin/pages/attachments/index',
  inquiries: '/subPackages/admin/pages/inquiries/list',
  'oss-configs': '/subPackages/admin/pages/oss-configs/index',
  about: '/subPackages/admin/pages/about/index',
  settings: '/subPackages/admin/pages/settings/index',
  logs: '/subPackages/admin/pages/logs/operation',
  'access-logs': '/subPackages/admin/pages/logs/access',
  users: '/subPackages/admin/pages/users/list',
}

const toast = useToast()
const dialog = useDialog()
const { isAuthenticated } = useAuth()
const { canEnterAdmin } = useAppMode()

const resource = ref('')
const loading = ref(false)
const list = ref<Record<string, unknown>[]>([])
const keyword = ref('')
const filters = ref<Record<string, unknown>>({})
const page = ref(1)
const pageSize = ref(20)
const total = ref(0)
const finished = ref(false)

const drawerShow = ref(false)
const drawerRows = ref<{ label: string, value: string }[]>([])
const inlineForm = reactive<Record<string, unknown>>({})
const inlineOpen = ref(false)

const schema = computed(() => getResourceSchema(resource.value))
const pageTitle = computed(() => schema.value.title)
const showFab = computed(() => {
  if (!resource.value) return false
  if (schema.value.readonly || schema.value.noEdit) return false
  if (schema.value.inlineCreate) return false
  return true
})
const canDelete = computed(() => !schema.value.readonly)
const canEdit = computed(() => !schema.value.readonly && !schema.value.noEdit)

function guardAdmin() {
  if (!isAuthenticated() || !canEnterAdmin.value) {
    uni.reLaunch({ url: '/pages/mine/index' })
    return false
  }
  return true
}

function itemTitle(item: Record<string, unknown>) {
  return pickField(item, schema.value.titleKeys) || String(item.id || '未命名')
}

function itemSubtitle(item: Record<string, unknown>) {
  return pickField(item, schema.value.subtitleKeys)
}

function coverUrl(item: Record<string, unknown>) {
  const key = schema.value.coverKey
  if (!key) return ''
  return resolveMediaUrl(String(item[key] || ''))
}

function matchLocal(item: Record<string, unknown>) {
  const kw = keyword.value.trim().toLowerCase()
  if (!kw) return true
  const keys = schema.value.localSearchKeys || schema.value.titleKeys || []
  return keys.some(k => String(item[k] ?? '').toLowerCase().includes(kw))
}

function matchFilters(item: Record<string, unknown>) {
  for (const [k, v] of Object.entries(filters.value)) {
    if (v === '' || v == null) continue
    if (k === 'keyword') continue
    if (String(item[k] ?? '') !== String(v)) return false
  }
  return true
}

async function load(reset = true) {
  if (!resource.value || !guardAdmin()) return
  if (reset) {
    page.value = 1
    finished.value = false
    list.value = []
  }
  loading.value = true
  try {
    const params: Record<string, unknown> = { ...filters.value }
    if (keyword.value) params.keyword = keyword.value
    if (schema.value.serverPaging) {
      params.page = page.value
      params.pageSize = schema.value.pageSize || pageSize.value
    }
    const data = await adminList(resource.value, params)
    if (schema.value.serverPaging) {
      const p = normalizePagination(data, page.value, schema.value.pageSize || pageSize.value)
      list.value = reset ? p.list : [...list.value, ...p.list]
      total.value = p.total
      finished.value = list.value.length >= p.total
    }
    else {
      let rows = normalizeAdminList(data)
      rows = rows.filter(matchLocal).filter(matchFilters)
      list.value = rows
      total.value = rows.length
      finished.value = true
    }
  }
  catch (e: unknown) {
    toast.error(e instanceof Error ? e.message : '加载失败')
  }
  finally {
    loading.value = false
  }
}

onLoad((q) => {
  resource.value = String(q?.resource || '')
  const jump = DEDICATED[resource.value]
  if (jump) {
    uni.redirectTo({ url: jump })
    return
  }
  pageSize.value = schema.value.pageSize || 20
})

onShow(() => {
  if (DEDICATED[resource.value]) return
  load(true)
})

onReachBottom(() => {
  if (!schema.value.serverPaging || finished.value || loading.value) return
  page.value += 1
  load(false)
})

const goCreate = () => {
  if (schema.value.inlineCreate) {
    Object.keys(inlineForm).forEach(k => delete inlineForm[k])
    schema.value.fields.forEach((f) => {
      inlineForm[f.key] = f.type === 'switch' ? 1 : f.type === 'number' ? 0 : ''
    })
    inlineOpen.value = true
    return
  }
  // 专用表单入口
  if (resource.value === 'video-albums') {
    uni.navigateTo({ url: '/subPackages/admin/pages/video-albums/form?mode=create' })
    return
  }
  if (resource.value === 'snippets') {
    uni.navigateTo({ url: '/subPackages/admin/pages/snippets/form?mode=create' })
    return
  }
  if (resource.value === 'columns') {
    uni.navigateTo({ url: '/subPackages/admin/pages/columns/form?mode=create' })
    return
  }
  uni.navigateTo({
    url: `/subPackages/admin/pages/resource/form?resource=${resource.value}&mode=create`,
  })
}

const goEdit = (item: Record<string, unknown>) => {
  if (schema.value.readonly) {
    openDetail(item)
    return
  }
  if (!canEdit.value) {
    openDetail(item)
    return
  }
  // 视频专辑编辑走专用 form（含视频多选）
  if (resource.value === 'video-albums' && item.id != null) {
    uni.navigateTo({
      url: `/subPackages/admin/pages/video-albums/form?mode=edit&id=${item.id}`,
    })
    return
  }
  if (resource.value === 'snippets' && item.id != null) {
    uni.navigateTo({
      url: `/subPackages/admin/pages/snippets/form?mode=edit&id=${item.id}`,
    })
    return
  }
  if (resource.value === 'columns' && item.id != null) {
    uni.navigateTo({
      url: `/subPackages/admin/pages/columns/form?mode=edit&id=${item.id}`,
    })
    return
  }
  const id = item.id
  if (id == null) return
  uni.navigateTo({
    url: `/subPackages/admin/pages/resource/form?resource=${resource.value}&mode=edit&id=${id}`,
  })
}

function openDetail(item: Record<string, unknown>) {
  drawerRows.value = Object.entries(item)
    .filter(([k]) => !['password'].includes(k))
    .slice(0, 30)
    .map(([label, value]) => ({
      label,
      value: typeof value === 'object' ? JSON.stringify(value) : String(value ?? ''),
    }))
  drawerShow.value = true
}

const onDelete = (item: Record<string, unknown>) => {
  if (!canDelete.value) return
  // 系统 PPT 模板禁止删除
  if (resource.value === 'ppt-templates' && item.isSystem) {
    toast.show('系统模板不可删除')
    return
  }
  const id = item.id
  if (id == null) return
  dialog.confirm({
    title: '确认删除',
    msg: `确定删除「${itemTitle(item)}」吗？`,
  }).then(async () => {
    try {
      await adminDelete(resource.value, id as string | number)
      toast.success('已删除')
      load(true)
    }
    catch (e: unknown) {
      toast.error(e instanceof Error ? e.message : '删除失败')
    }
  }).catch(() => {})
}

async function submitInline() {
  try {
    await adminCreate(resource.value, { ...inlineForm })
    toast.success('已创建')
    inlineOpen.value = false
    load(true)
  }
  catch (e: unknown) {
    toast.error(e instanceof Error ? e.message : '创建失败')
  }
}
</script>

<template>
  <AppPageShell admin>
    <AppNavbar :title="pageTitle" show-back />
    <view class="page-pad">
      <AdminFilterBar
        v-if="schema.filters?.length || schema.localSearchKeys?.length || schema.serverPaging"
        v-model:keyword="keyword"
        v-model="filters"
        :filters="schema.filters || []"
        @search="load(true)"
        @reset="load(true)"
      />

      <view v-if="schema.inlineCreate" class="inline-create admin-card">
        <wd-button size="small" type="primary" @click="goCreate">新增</wd-button>
      </view>

      <view
        v-for="item in list"
        :key="String(item.id)"
        class="admin-card row"
        @click="goEdit(item)"
      >
        <image v-if="coverUrl(item)" class="cover" :src="coverUrl(item)" mode="aspectFill" />
        <view class="main">
          <view class="title-row">
            <text class="title">{{ itemTitle(item) }}</text>
            <AdminStatusBadge
              v-if="schema.statusKey"
              :value="item[schema.statusKey] as string | number | boolean"
              :map="schema.statusMap"
            />
          </view>
          <text v-if="itemSubtitle(item)" class="sub text-art-muted">{{ itemSubtitle(item) }}</text>
        </view>
        <wd-button
          v-if="canDelete"
          size="small"
          type="warning"
          plain
          @click.stop="onDelete(item)"
        >
          删除
        </wd-button>
      </view>
      <wd-loading v-if="loading" />
      <wd-empty v-if="!loading && !list.length" description="暂无数据" />
      <text v-if="schema.serverPaging && !finished && list.length" class="more text-art-muted">上拉加载更多</text>
    </view>

    <view v-if="showFab" class="fab" @click="goCreate">
      <wd-icon name="plus" size="24px" color="#fff" />
    </view>

    <AdminDetailDrawer v-model:show="drawerShow" :title="pageTitle" :rows="drawerRows" />

    <!-- 页内新建（邮箱后缀等） -->
    <AdminDetailDrawer v-model:show="inlineOpen" title="新建">
      <view v-for="f in schema.fields" :key="f.key" class="inline-field">
        <wd-input
          v-if="f.type === 'text' || f.type === 'number'"
          :model-value="String(inlineForm[f.key] ?? '')"
          :label="f.label"
          :placeholder="f.placeholder || f.label"
          clearable
          @update:model-value="(v: string | number) => inlineForm[f.key] = v"
        />
        <view v-else-if="f.type === 'switch'" class="switch-row">
          <text>{{ f.label }}</text>
          <wd-switch :model-value="!!inlineForm[f.key]" @change="(v: boolean) => inlineForm[f.key] = v ? 1 : 0" />
        </view>
      </view>
      <template #footer>
        <wd-button block type="primary" @click="submitInline">保存</wd-button>
      </template>
    </AdminDetailDrawer>
  </AppPageShell>
</template>

<style scoped lang="scss">
.page-pad { padding: 24rpx 32rpx 120rpx; }
.inline-create { padding: 16rpx 24rpx; margin-bottom: 16rpx; }
.row {
  padding: 24rpx 28rpx;
  margin-bottom: 16rpx;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16rpx;
}
.cover {
  width: 88rpx;
  height: 88rpx;
  border-radius: 12rpx;
  flex-shrink: 0;
  background: rgba(255, 255, 255, 0.06);
}
.main { flex: 1; display: flex; flex-direction: column; gap: 8rpx; min-width: 0; }
.title-row { display: flex; align-items: center; gap: 12rpx; }
.title {
  font-size: 30rpx;
  color: rgb(var(--art-text));
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1;
}
.sub { font-size: 24rpx; }
.more { display: block; text-align: center; font-size: 22rpx; padding: 16rpx; }
.fab {
  position: fixed;
  right: 40rpx;
  bottom: calc(120rpx + env(safe-area-inset-bottom));
  width: 96rpx;
  height: 96rpx;
  border-radius: 50%;
  background: rgb(var(--art-accent));
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.35);
  z-index: 100;
}
.inline-field { margin-bottom: 12rpx; }
.switch-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16rpx 0;
  color: rgb(var(--art-text));
}
</style>
