import { useEffect, useState } from 'react';
import Taro from '@tarojs/taro';
import { Button, Input, Text, Textarea, View } from '@tarojs/components';
import {
  adoptPublicQuestionBank,
  executeContentImport,
  loadContentEntriesAdmin,
  loadImportFieldMapping,
  loadImportJobDetail,
  loadImportIssues,
  loadImportJobs,
  loadImportPostCheck,
  loadImportTemplate,
  loadPublicQuestionBankConflicts,
  loadPublicQuestionBanks,
  loadTenantContentNotifications,
  previewContentImport,
  resolvePublicQuestionBankConflict,
  resolvePublicQuestionBankConflicts,
  runImportPostCheck,
  syncPublicQuestionBank,
  updateTenantContentNotificationStatus,
  type ContentEntryAdminItem,
  type ImportFieldMapping,
  type ImportJobDetail,
  type ImportIssueItem,
  type ImportJobItem,
  type ImportPreviewResult,
  type ImportTemplateItem,
  type ImportType,
  type ImportSourceFormat,
  type PublicQuestionBankConflictItem,
  type PublicQuestionBankConflictsResult,
  type PublicQuestionBankItem,
  type TenantContentNotificationItem,
} from '@/services/tenantAdmin';
import '../admin.css';

function displayBankName(item: PublicQuestionBankItem) {
  return item.sourceQuestionBankName || item.name || '公共题库';
}

function displayRegionName(item: PublicQuestionBankItem) {
  return item.sourceRegionName || item.regionName || '通用';
}

function adoptionIdOf(item: PublicQuestionBankItem) {
  return item.adoptedId || (item.adoption && String(item.adoption.id || '')) || '';
}

const importTypes: ImportType[] = ['questions', 'vocabulary', 'handbook', 'scoreline', 'videos'];
const importFormats: ImportSourceFormat[] = ['json', 'csv', 'excel'];

function readLocalFileAsBase64(file: File) {
  return new Promise<string>((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      const result = typeof reader.result === 'string' ? reader.result : '';
      resolve(result.includes(',') ? result.slice(result.indexOf(',') + 1) : result);
    };
    reader.onerror = () => reject(new Error('文件读取失败'));
    reader.readAsDataURL(file);
  });
}

function readLocalFileAsText(file: File) {
  return new Promise<string>((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(typeof reader.result === 'string' ? reader.result : '');
    reader.onerror = () => reject(new Error('文件读取失败'));
    reader.readAsText(file, 'utf-8');
  });
}

function openH5FilePicker(format: ImportSourceFormat) {
  return new Promise<{ fileName: string; text?: string; fileBase64?: string }>((resolve, reject) => {
    if (process.env.TARO_ENV !== 'h5' || typeof document === 'undefined') {
      reject(new Error('当前端暂未接入文件选择，请粘贴 JSON/CSV 内容后预览导入。'));
      return;
    }
    const input = document.createElement('input');
    input.type = 'file';
    input.accept = format === 'excel' ? '.xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' : format === 'csv' ? '.csv,text/csv,text/plain' : '.json,application/json,text/plain';
    input.onchange = async () => {
      const file = input.files?.[0];
      if (!file) {
        reject(new Error('未选择文件'));
        return;
      }
      try {
        if (format === 'excel') {
          resolve({ fileName: file.name, fileBase64: await readLocalFileAsBase64(file) });
        } else {
          resolve({ fileName: file.name, text: await readLocalFileAsText(file) });
        }
      } catch (error) {
        reject(error);
      }
    };
    input.click();
  });
}

function base64ToUint8Array(base64: string) {
  const binary = atob(base64);
  const bytes = new Uint8Array(binary.length);
  for (let index = 0; index < binary.length; index += 1) {
    bytes[index] = binary.charCodeAt(index);
  }
  return bytes;
}

function downloadTemplateFile(template: ImportTemplateItem) {
  if (process.env.TARO_ENV !== 'h5' || typeof document === 'undefined') {
    throw new Error('当前端暂不支持直接下载模板，请先使用模板预览。');
  }
  if (!template.contentBase64) throw new Error('模板内容为空，请重新加载模板。');
  const blob = new Blob([base64ToUint8Array(template.contentBase64)], {
    type: template.mimeType || 'application/octet-stream',
  });
  const url = URL.createObjectURL(blob);
  const link = document.createElement('a');
  link.href = url;
  link.download = template.fileName || `import-template.${template.format || 'json'}`;
  document.body.appendChild(link);
  link.click();
  link.remove();
  URL.revokeObjectURL(url);
}

function safeJsonPreview(value: unknown, maxLength = 360) {
  try {
    return JSON.stringify(value, null, 2).slice(0, maxLength);
  } catch {
    return String(value).slice(0, maxLength);
  }
}

function conflictItems(value: PublicQuestionBankConflictsResult | null) {
  return Array.isArray(value?.conflicts) ? value.conflicts : [];
}

function notificationStatusText(item: TenantContentNotificationItem) {
  if (item.status === 'resolved') return '已处理';
  if (item.status === 'dismissed') return '已忽略';
  if (item.status === 'read') return '已读';
  return '未读';
}

function objectRecord(value: unknown): Record<string, unknown> {
  return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {};
}

function importJobActive(item?: ImportJobItem | null) {
  return !!item && ['pending', 'importing'].includes(String(item.status || ''));
}

function statusCountText(value?: Record<string, number>) {
  if (!value || !Object.keys(value).length) return '-';
  return Object.entries(value).map(([key, count]) => `${key}:${count}`).join(' · ');
}

function parseJsonImportText(text: string) {
  try {
    return JSON.parse(text);
  } catch {
    throw new Error('JSON 内容格式不正确，请先修正后再预览。');
  }
}

export default function TenantContentPage() {
  const [entries, setEntries] = useState<ContentEntryAdminItem[]>([]);
  const [jobs, setJobs] = useState<ImportJobItem[]>([]);
  const [publicBanks, setPublicBanks] = useState<PublicQuestionBankItem[]>([]);
  const [notifications, setNotifications] = useState<TenantContentNotificationItem[]>([]);
  const [selectedJobId, setSelectedJobId] = useState('');
  const [selectedImportType, setSelectedImportType] = useState<ImportType>('questions');
  const [sourceFormat, setSourceFormat] = useState<ImportSourceFormat>('json');
  const [sourceName, setSourceName] = useState('');
  const [importText, setImportText] = useState('');
  const [fileBase64, setFileBase64] = useState('');
  const [allowPartial, setAllowPartial] = useState(false);
  const [executionMode, setExecutionMode] = useState<'sync' | 'async'>('sync');
  const [copyLimit, setCopyLimit] = useState('200');
  const [issues, setIssues] = useState<ImportIssueItem[]>([]);
  const [mapping, setMapping] = useState<ImportFieldMapping | null>(null);
  const [mappingOverrides, setMappingOverrides] = useState<Record<string, string>>({});
  const [template, setTemplate] = useState<ImportTemplateItem | null>(null);
  const [previewResult, setPreviewResult] = useState<ImportPreviewResult | null>(null);
  const [jobDetail, setJobDetail] = useState<ImportJobDetail | null>(null);
  const [postCheck, setPostCheck] = useState<Record<string, unknown> | null>(null);
  const [conflicts, setConflicts] = useState<PublicQuestionBankConflictsResult | null>(null);
  const [busy, setBusy] = useState('');
  const [error, setError] = useState('');

  function reload() {
    Promise.all([
      loadContentEntriesAdmin().catch(() => ({ items: [] })),
      loadImportJobs(20).catch(() => ({ items: [] })),
      loadPublicQuestionBanks().catch(() => ({ items: [] })),
      loadTenantContentNotifications({ limit: 10 }).catch(() => ({ items: [] })),
    ]).then(([entryPayload, jobPayload, bankPayload, notificationPayload]) => {
      setEntries(entryPayload.items || []);
      setJobs(jobPayload.items || []);
      setPublicBanks(bankPayload.items || []);
      setNotifications(notificationPayload.items || []);
    }).catch(nextError => setError(nextError instanceof Error ? nextError.message : '内容数据加载失败'));
  }

  useEffect(() => {
    reload();
  }, []);

  useEffect(() => {
    if (!selectedJobId || !importJobActive(jobDetail?.item)) return undefined;
    const timer = setInterval(() => {
      refreshSelectedJob(false);
    }, 3000);
    return () => clearInterval(timer);
  }, [selectedJobId, jobDetail?.item?.status]);

  async function confirm(title: string, content: string) {
    const result = await Taro.showModal({ title, content, confirmText: '确认', cancelText: '取消' });
    return result.confirm;
  }

  async function refreshSelectedJob(showError = true, jobId = selectedJobId) {
    if (!jobId) return;
    try {
      const detailPayload = await loadImportJobDetail(jobId);
      setJobDetail(detailPayload);
      if (detailPayload.recentIssues) setIssues(detailPayload.recentIssues);
      setPostCheck(detailPayload.importPostCheck ? { importPostCheck: detailPayload.importPostCheck } : null);
      if (detailPayload.item?.importType && importTypes.includes(detailPayload.item.importType as ImportType)) {
        setSelectedImportType(detailPayload.item.importType as ImportType);
      }
      if (!importJobActive(detailPayload.item)) reload();
    } catch (nextError) {
      if (showError) setError(nextError instanceof Error ? nextError.message : '导入任务详情刷新失败');
    }
  }

  async function selectJob(item: ImportJobItem) {
    const importType = importTypes.includes(item.importType as ImportType) ? item.importType as ImportType : 'questions';
    setSelectedJobId(item.id);
    setSelectedImportType(importType);
    setJobDetail(null);
    setPostCheck(null);
    setError('');
    try {
      const [detailPayload, issuePayload, mappingPayload, templatePayload, postCheckPayload] = await Promise.all([
        loadImportJobDetail(item.id).catch(() => null),
        loadImportIssues(item.id).catch(() => ({ items: [] })),
        loadImportFieldMapping(importType).catch(() => ({ item: null })),
        loadImportTemplate(importType, 'json').catch(() => ({ item: null })),
        loadImportPostCheck(item.id).catch(() => ({ item: null })),
      ]);
      setJobDetail(detailPayload);
      setIssues(detailPayload?.recentIssues || issuePayload.items || []);
      setMapping(mappingPayload.item || null);
      setTemplate(templatePayload.item || null);
      setPostCheck(detailPayload?.importPostCheck ? { importPostCheck: detailPayload.importPostCheck } : postCheckPayload.item || null);
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '导入任务详情加载失败');
    }
  }

  async function submitPostCheck() {
    if (!selectedJobId) {
      setError('请先选择一个导入任务。');
      return;
    }
    const ok = await confirm('导入后复检', '确认对该导入任务执行后端复检？复检结果会写入任务摘要和审计日志。');
    if (!ok) return;
    setBusy('post-check');
    setError('');
    try {
      const payload = await runImportPostCheck(selectedJobId);
      setPostCheck(payload.item || null);
      Taro.showToast({ title: '复检完成', icon: 'success' });
      await refreshSelectedJob(false, selectedJobId);
      reload();
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '导入复检失败');
    } finally {
      setBusy('');
    }
  }

  async function previewTemplate(type = selectedImportType, format = sourceFormat) {
    setError('');
    try {
      const [mappingPayload, templatePayload] = await Promise.all([
        loadImportFieldMapping(type),
        loadImportTemplate(type, format === 'excel' ? 'csv' : format),
      ]);
      setSelectedImportType(type);
      if (format !== 'excel') setSourceFormat(format);
      setMapping(mappingPayload.item || null);
      setMappingOverrides(Object.fromEntries(
        (mappingPayload.item?.fields || [])
          .map(field => [field.field || '', (field.aliases || []).join(', ')] as const)
          .filter(([key]) => key),
      ));
      setTemplate(templatePayload.item || null);
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '模板加载失败');
    }
  }

  async function downloadCurrentTemplate() {
    setError('');
    try {
      const payload = template && template.importType === selectedImportType && template.format === sourceFormat
        ? { item: template }
        : await loadImportTemplate(selectedImportType, sourceFormat === 'excel' ? 'csv' : sourceFormat);
      if (!payload.item) throw new Error('模板不存在。');
      downloadTemplateFile(payload.item);
      setTemplate(payload.item);
      Taro.showToast({ title: '已下载', icon: 'success' });
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '模板下载失败');
    }
  }

  async function chooseImportFile() {
    setError('');
    try {
      const file = await openH5FilePicker(sourceFormat);
      setSourceName(file.fileName);
      if (file.text !== undefined) {
        setImportText(file.text);
        setFileBase64('');
      }
      if (file.fileBase64 !== undefined) {
        setFileBase64(file.fileBase64);
        setImportText('');
      }
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '文件选择失败');
    }
  }

  function mappingOverridePayload() {
    return Object.fromEntries(
      Object.entries(mappingOverrides)
        .map(([field, aliases]) => [field, aliases.split(/[,\n，]/).map(item => item.trim()).filter(Boolean)])
        .filter(([, aliases]) => (aliases as string[]).length > 0),
    ) as Record<string, string[]>;
  }

  function importRequestBody(options: { usePreviewJob?: boolean } = {}) {
    const body: Record<string, unknown> = {
      sourceFormat,
      sourceName: sourceName || `${selectedImportType}.${sourceFormat}`,
      allowPartial,
      fieldMappingOverrides: mappingOverridePayload(),
      fieldMappingPreset: mapping,
    };
    if (options.usePreviewJob && previewResult?.job?.id) {
      body.previewJobId = previewResult.job.id;
      body.executionMode = executionMode;
      return body;
    }
    if (sourceFormat === 'json') {
      const parsed = parseJsonImportText(importText.trim());
      if (Array.isArray(parsed)) {
        body.items = parsed;
      } else {
        const payload = objectRecord(parsed);
        if (!Object.keys(payload).length) throw new Error('JSON 内容必须是数组或对象。');
        Object.entries(payload).forEach(([key, value]) => {
          if (!['sourceFormat', 'sourceName', 'allowPartial', 'executionMode', 'fieldMappingOverrides', 'fieldMappingPreset', 'previewJobId', 'jobId'].includes(key)) {
            body[key] = value;
          }
        });
      }
    } else if (sourceFormat === 'csv') {
      body.csvText = importText.trim();
    } else {
      body.fileBase64 = fileBase64;
    }
    return body;
  }

  function validateImportInput() {
    if (sourceFormat === 'excel' && !fileBase64) {
      setError('请先选择 .xlsx 文件。');
      return false;
    }
    if (sourceFormat !== 'excel' && !importText.trim()) {
      setError(sourceFormat === 'json' ? '请粘贴 JSON 内容或选择 .json 文件。' : '请粘贴 CSV 内容或选择 .csv 文件。');
      return false;
    }
    return true;
  }

  async function submitPreviewImport() {
    if (!validateImportInput()) return;
    setBusy('import-preview');
    setError('');
    try {
      const payload = await previewContentImport(selectedImportType, importRequestBody());
      setPreviewResult(payload);
      setSelectedJobId(payload.job?.id || '');
      setIssues((payload.issues || []) as ImportIssueItem[]);
      Taro.showToast({ title: '预览完成', icon: 'success' });
      reload();
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '导入预览失败');
    } finally {
      setBusy('');
    }
  }

  async function submitExecuteImport() {
    if (!previewResult?.job?.id && !validateImportInput()) return;
    const ok = await confirm('执行导入', executionMode === 'async' ? '确认提交异步导入任务？worker 会处理队列并写入审计。' : '确认同步执行导入？后端会按预览结果、权限和幂等规则写入数据。');
    if (!ok) return;
    setBusy('import-execute');
    setError('');
    try {
      const payload = await executeContentImport(selectedImportType, importRequestBody({ usePreviewJob: true }));
      const nextJobId = String(payload.item?.jobId || payload.item?.id || previewResult?.job?.id || '');
      if (nextJobId) setSelectedJobId(nextJobId);
      Taro.showToast({ title: executionMode === 'async' ? '已入队' : '导入完成', icon: 'success' });
      if (nextJobId) await refreshSelectedJob(false, nextJobId);
      reload();
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '执行导入失败');
    } finally {
      setBusy('');
    }
  }

  async function submitAdopt(item: PublicQuestionBankItem) {
    if (!item.grantId) {
      setError('该公共题库缺少授权 ID，无法采纳。');
      return;
    }
    const ok = await confirm('采纳公共题库', `确认采纳 ${displayBankName(item)}？系统会复制题目快照并创建本租户入口和题目集合。`);
    if (!ok) return;
    setBusy(`adopt:${item.grantId}`);
    setError('');
    try {
      await adoptPublicQuestionBank({
        grantId: item.grantId,
        entryName: displayBankName(item),
        collectionName: `${displayBankName(item)}题目`,
        copyLimit: Math.max(1, Math.min(Number(copyLimit || 200), 1000)),
      });
      Taro.showToast({ title: '已采纳', icon: 'success' });
      reload();
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '公共题库采纳失败');
    } finally {
      setBusy('');
    }
  }

  async function submitSync(item: PublicQuestionBankItem) {
    const adoptionId = adoptionIdOf(item);
    if (!adoptionId) {
      setError('请先采纳公共题库，再执行同步。');
      return;
    }
    const ok = await confirm('同步公共题库', `确认同步 ${displayBankName(item)} 的平台新增/更新题目？租户自改题目会被冲突保护。`);
    if (!ok) return;
    setBusy(`sync:${adoptionId}`);
    setError('');
    try {
      const payload = await syncPublicQuestionBank({
        adoptionId,
        copyLimit: Math.max(1, Math.min(Number(copyLimit || 1000), 1000)),
      });
      setConflicts(payload.sync || null);
      const notificationPayload = await loadTenantContentNotifications({ limit: 10 });
      setNotifications(notificationPayload.items || []);
      Taro.showToast({ title: '同步完成', icon: 'success' });
      reload();
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '公共题库同步失败');
    } finally {
      setBusy('');
    }
  }

  async function updateNotificationStatus(item: TenantContentNotificationItem, status: 'read' | 'dismissed') {
    if (!item.id) return;
    setBusy(`notification:${item.id}:${status}`);
    setError('');
    try {
      await updateTenantContentNotificationStatus({ notificationIds: [item.id], status });
      const payload = await loadTenantContentNotifications({ limit: 10 });
      setNotifications(payload.items || []);
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '通知状态更新失败');
    } finally {
      setBusy('');
    }
  }

  async function showConflicts(item: PublicQuestionBankItem) {
    const adoptionId = adoptionIdOf(item);
    if (!adoptionId) {
      setError('该公共题库尚未采纳，没有冲突记录。');
      return;
    }
    setError('');
    try {
      const payload = await loadPublicQuestionBankConflicts(adoptionId);
      setConflicts(payload.item || null);
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '冲突记录加载失败');
    }
  }

  async function resolveConflict(item: PublicQuestionBankConflictItem, resolution: 'accept_platform' | 'keep_local') {
    const adoptionId = conflicts?.adoptionId;
    if (!adoptionId || !item.sourceQuestionId) {
      setError('缺少冲突上下文，无法处理。');
      return;
    }
    const ok = await confirm(
      resolution === 'accept_platform' ? '采纳平台版本' : '保留租户版本',
      resolution === 'accept_platform'
        ? '确认用平台公共题库的新版本覆盖该租户副本？系统会写入新题目版本并记录审计。'
        : '确认保留租户本地改写版本？系统会记录该冲突已处理，后续同步不再反复提示同一版本。',
    );
    if (!ok) return;
    setBusy(`resolve:${item.sourceQuestionId}:${resolution}`);
    setError('');
    try {
      await resolvePublicQuestionBankConflict({
        adoptionId,
        sourceQuestionId: item.sourceQuestionId,
        resolution,
      });
      const payload = await loadPublicQuestionBankConflicts(adoptionId);
      setConflicts(payload.item || null);
      const notificationPayload = await loadTenantContentNotifications({ limit: 10 });
      setNotifications(notificationPayload.items || []);
      Taro.showToast({ title: '已处理', icon: 'success' });
      reload();
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '冲突处理失败');
    } finally {
      setBusy('');
    }
  }

  async function resolveVisibleConflicts(resolution: 'accept_platform' | 'keep_local') {
    const adoptionId = conflicts?.adoptionId;
    const sourceQuestionIds = conflictItems(conflicts)
      .map(item => item.sourceQuestionId || '')
      .filter(Boolean)
      .slice(0, 50);
    if (!adoptionId || !sourceQuestionIds.length) {
      setError('当前没有可批量处理的冲突。');
      return;
    }
    const ok = await confirm(
      resolution === 'accept_platform' ? '批量采纳平台版本' : '批量保留租户版本',
      resolution === 'accept_platform'
        ? `确认批量用平台公共题库的新版本覆盖 ${sourceQuestionIds.length} 道租户副本？系统会逐条写入审计。`
        : `确认批量保留 ${sourceQuestionIds.length} 道租户本地版本？后续同步不再反复提示同一版本。`,
    );
    if (!ok) return;
    setBusy(`resolve-batch:${resolution}`);
    setError('');
    try {
      await resolvePublicQuestionBankConflicts({
        adoptionId,
        sourceQuestionIds,
        resolution,
        limit: sourceQuestionIds.length,
      });
      const payload = await loadPublicQuestionBankConflicts(adoptionId);
      setConflicts(payload.item || null);
      const notificationPayload = await loadTenantContentNotifications({ limit: 10 });
      setNotifications(notificationPayload.items || []);
      Taro.showToast({ title: '批量处理完成', icon: 'success' });
      reload();
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '批量冲突处理失败');
    } finally {
      setBusy('');
    }
  }

  const postCheckSummary = postCheck?.importPostCheck && typeof postCheck.importPostCheck === 'object'
    ? postCheck.importPostCheck as Record<string, unknown>
    : postCheck;
  const postCheckIssueCount = Array.isArray(postCheckSummary?.issues) ? postCheckSummary.issues.length : 0;
  const selectedJob = jobDetail?.item || jobs.find(item => item.id === selectedJobId) || null;

  return (
    <View className='admin-page'>
      <View className='admin-shell'>
        <View className='admin-header'>
          <Text className='admin-kicker'>Content</Text>
          <Text className='admin-title'>题库内容</Text>
          <Text className='admin-subtitle'>入口、分类、导入任务、公共题库采纳和同步状态。</Text>
        </View>

        <View className='admin-section'>
          <Text className='admin-section-title'>内容入口</Text>
          <View className='admin-list'>
            {entries.map(item => (
              <View className='admin-row' key={item.id}>
                <Text className='admin-row-main'>{item.name}</Text>
                <Text className='admin-row-meta'>{item.entryType || 'entry'} · {item.visibility || 'visible'} · {item.isActive === false ? '停用' : '启用'}</Text>
              </View>
            ))}
          </View>
          {!entries.length ? <View className='admin-empty'>暂无内容入口。</View> : null}
        </View>

        <View className='admin-section'>
          <Text className='admin-section-title'>导入操作</Text>
          <View className='admin-actions'>
            <Input className='admin-input' placeholder='采纳/同步复制题量，默认 200' type='number' value={copyLimit} onInput={event => setCopyLimit(String(event.detail.value || ''))} />
            <Button className='admin-button primary' loading={busy === 'post-check'} onClick={submitPostCheck}>复检任务</Button>
            <Button className='admin-button' onClick={() => refreshSelectedJob()}>刷新任务</Button>
          </View>
          <View className='admin-tabs'>
            {importTypes.map(type => (
              <Button key={type} className={`admin-button ${selectedImportType === type ? 'active' : ''}`} onClick={() => previewTemplate(type, sourceFormat)}>{type}</Button>
            ))}
          </View>
          <View className='admin-tabs'>
            {importFormats.map(format => (
              <Button key={format} className={`admin-button ${sourceFormat === format ? 'active' : ''}`} onClick={() => {
                setSourceFormat(format);
                setPreviewResult(null);
                setIssues([]);
                previewTemplate(selectedImportType, format);
              }}>{format}</Button>
            ))}
          </View>
          <View className='admin-form-grid'>
            <Input className='admin-input' placeholder='来源名称，例如 天津英语题库.json' value={sourceName} onInput={event => setSourceName(String(event.detail.value || ''))} />
            <View className='admin-actions compact'>
              <Button className='admin-button' onClick={() => setAllowPartial(prev => !prev)}>{allowPartial ? '允许部分导入' : '必须全量有效'}</Button>
              <Button className='admin-button' onClick={() => setExecutionMode(prev => prev === 'sync' ? 'async' : 'sync')}>{executionMode === 'sync' ? '同步执行' : '异步入队'}</Button>
              <Button className='admin-button' onClick={chooseImportFile}>选择文件</Button>
            </View>
          </View>
          {sourceFormat === 'excel' ? (
            <View className='admin-empty'>已选择：{sourceName || '暂无文件'} · {fileBase64 ? '文件内容已读取，点击预览会交给后端解析。' : '请选择 .xlsx 文件，或切换 JSON/CSV 粘贴内容。'}</View>
          ) : (
            <Textarea
              className='admin-textarea'
              placeholder={sourceFormat === 'json' ? '粘贴题目/单词/手册/分数线/视频 JSON 内容' : '粘贴 CSV 内容，首行为表头'}
              value={importText}
              onInput={event => setImportText(String(event.detail.value || ''))}
            />
          )}
          <View className='admin-actions compact'>
            <Button className='admin-button primary' loading={busy === 'import-preview'} onClick={submitPreviewImport}>后端预览</Button>
            <Button className='admin-button primary' loading={busy === 'import-execute'} onClick={submitExecuteImport}>执行导入</Button>
            <Button className='admin-button' onClick={() => {
              setImportText(template?.contentPreview || '');
              setSourceFormat((template?.format as ImportSourceFormat) || 'json');
              setSourceName(template?.fileName || '');
              setPreviewResult(null);
              setIssues([]);
            }}>填入模板</Button>
            <Button className='admin-button' onClick={downloadCurrentTemplate}>下载模板</Button>
          </View>
          <View className='admin-grid'>
            <View className='admin-metric'><Text className='admin-metric-label'>当前任务</Text><Text className='admin-metric-value'>{selectedJobId ? selectedJobId.slice(0, 8) : '-'}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>任务状态</Text><Text className='admin-metric-value'>{String(selectedJob?.status || '-')}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>执行模式</Text><Text className='admin-metric-value'>{String(selectedJob?.executionMode || executionMode)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>问题行</Text><Text className='admin-metric-value'>{String(issues.length)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>字段数</Text><Text className='admin-metric-value'>{String(mapping?.fields?.length || 0)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>预览有效</Text><Text className='admin-metric-value'>{String(previewResult?.job?.validCount ?? '-')}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>复检状态</Text><Text className='admin-metric-value'>{String(postCheckSummary?.status || '-')}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>复检问题</Text><Text className='admin-metric-value'>{String(postCheckIssueCount)}</Text></View>
          </View>
          {jobDetail ? (
            <View className='admin-list'>
              <View className='admin-row'>
                <Text className='admin-row-main'>任务详情 · {selectedJob?.sourceName || selectedJob?.id}</Text>
                <Text className='admin-row-meta'>状态 {selectedJob?.status || '-'} · 模式 {selectedJob?.executionMode || '-'} · 尝试 {String(jobDetail.worker?.attemptCount ?? selectedJob?.attemptCount ?? 0)}/{String(jobDetail.worker?.maxAttempts ?? selectedJob?.maxAttempts ?? '-')}</Text>
                <Text className='admin-row-meta'>Item 状态 {statusCountText(jobDetail.itemStatusSummary)} · Issue {statusCountText(jobDetail.issueSummary)}</Text>
                <Text className='admin-row-meta'>入队 {String(jobDetail.worker?.queuedAt || '-')} · 锁定 {String(jobDetail.worker?.lockedAt || '-')} · 下次重试 {String(jobDetail.worker?.nextAttemptAt || '-')}</Text>
                {selectedJob?.errorMessage ? <Text className='admin-row-meta'>错误：{selectedJob.errorMessage}</Text> : null}
                {jobDetail.importPostCheck ? <Text className='admin-row-meta'>复检：{String(jobDetail.importPostCheck.status || '-')} · {String((jobDetail.importPostCheck.issues as unknown[] | undefined)?.length || 0)} 个问题</Text> : null}
              </View>
            </View>
          ) : null}
        </View>

        <View className='admin-section'>
          <Text className='admin-section-title'>导入任务</Text>
          <View className='admin-list'>
            {jobs.map(item => (
              <View className='admin-row' key={item.id}>
                <Text className='admin-row-main'>{item.importType || 'import'} · {item.status || 'unknown'}</Text>
                <Text className='admin-row-meta'>总数 {item.totalCount || 0} · 有效 {item.validCount || 0} · 已处理 {(item.insertedCount || 0) + (item.updatedCount || 0) + (item.skippedCount || 0)} · 错误 {item.errorCount || 0} · 警告 {item.warningCount || 0}</Text>
                <Text className='admin-row-meta'>模式 {item.executionMode || 'sync'} · 尝试 {String(item.attemptCount ?? 0)}/{String(item.maxAttempts ?? '-')} · 任务 {item.id}</Text>
                <Text className='admin-row-meta'>{item.updatedAt || item.createdAt || ''}</Text>
                <View className='admin-row-actions'>
                  <Button className='admin-mini-button' onClick={() => selectJob(item)}>详情</Button>
                  <Button className='admin-mini-button' onClick={() => {
                    setSelectedJobId(item.id);
                    setSelectedImportType(importTypes.includes(item.importType as ImportType) ? item.importType as ImportType : 'questions');
                  }}>选择复检</Button>
                </View>
              </View>
            ))}
          </View>
          {!jobs.length ? <View className='admin-empty'>暂无导入任务。</View> : null}
        </View>

        <View className='admin-section'>
          <Text className='admin-section-title'>问题与模板</Text>
          <View className='admin-list'>
            {issues.slice(0, 8).map(item => (
              <View className='admin-row' key={item.id}>
                <Text className='admin-row-main'>{item.severity || 'issue'} · {item.code || '-'}</Text>
                <Text className='admin-row-meta'>第 {item.rowNo || '-'} 行 · {item.fieldPath || '整行'} · {item.message || '-'}</Text>
              </View>
            ))}
          </View>
          {!issues.length ? <View className='admin-empty'>选择导入任务后可查看问题行。</View> : null}
          {mapping?.fields?.length ? (
            <View className='admin-list'>
              {mapping.fields.slice(0, 10).map(field => (
                <View className='admin-row' key={field.field || field.label}>
                  <Text className='admin-row-main'>{field.label || field.field}{field.required ? ' · 必填' : ''}</Text>
                  <Text className='admin-row-meta'>{field.field || '-'} · {(field.aliases || []).join(' / ') || '无别名'}</Text>
                  <Text className='admin-row-meta'>{field.description || ''}</Text>
                  <Input
                    className='admin-input compact'
                    placeholder='本次导入字段别名，用逗号分隔'
                    value={mappingOverrides[field.field || ''] || ''}
                    onInput={event => setMappingOverrides(prev => ({ ...prev, [field.field || '']: String(event.detail.value || '') }))}
                  />
                </View>
              ))}
            </View>
          ) : null}
          {template?.contentPreview ? <View className='admin-empty'>{template.fileName || '模板'}：{template.contentPreview.slice(0, 240)}</View> : null}
          {previewResult?.items?.length ? (
            <View className='admin-list'>
              {previewResult.items.slice(0, 5).map((item, index) => (
                <View className='admin-row' key={`${item.rowNo || index}-${item.externalId || item.status}`}>
                  <Text className='admin-row-main'>预览第 {item.rowNo || index + 1} 行 · {item.status || '-'}</Text>
                  <Text className='admin-row-meta'>外部 ID {item.externalId || '-'} · 问题 {String(item.issues?.length || 0)}</Text>
                  <Text className='admin-row-meta break-line'>{safeJsonPreview(item.normalized, 260)}</Text>
                </View>
              ))}
            </View>
          ) : null}
        </View>

        <View className='admin-section'>
          <Text className='admin-section-title'>同步通知</Text>
          <View className='admin-list'>
            {notifications.map(item => (
              <View className='admin-row' key={item.id}>
                <Text className='admin-row-main'>{item.title || '内容通知'} · {notificationStatusText(item)}</Text>
                <Text className='admin-row-meta'>{item.message || '-'} · {item.createdAt ? String(item.createdAt).slice(0, 16).replace('T', ' ') : ''}</Text>
                <View className='admin-row-actions'>
                  {item.status === 'unread' ? (
                    <Button className='admin-mini-button primary' loading={busy === `notification:${item.id}:read`} onClick={() => updateNotificationStatus(item, 'read')}>标记已读</Button>
                  ) : null}
                  {item.status !== 'dismissed' && item.status !== 'resolved' ? (
                    <Button className='admin-mini-button' loading={busy === `notification:${item.id}:dismissed`} onClick={() => updateNotificationStatus(item, 'dismissed')}>忽略</Button>
                  ) : null}
                </View>
              </View>
            ))}
          </View>
          {!notifications.length ? <View className='admin-empty'>暂无公共题库同步通知。</View> : null}
        </View>

        <View className='admin-section'>
          <Text className='admin-section-title'>公共题库</Text>
          <View className='admin-list'>
            {publicBanks.map((item, index) => (
              <View className='admin-row' key={item.grantId || item.sourceQuestionBankId || index}>
                <Text className='admin-row-main'>{displayBankName(item)}</Text>
                <Text className='admin-row-meta'>{displayRegionName(item)} · {item.grantScope || '授权'} · {adoptionIdOf(item) ? '已采纳' : '可采纳'} · {item.questionCount || 0} 题</Text>
                <Text className='admin-row-meta'>同步 {item.syncStatus || '-'} · 已复制 {item.copiedQuestionCount || 0} · {item.lastSyncedAt ? String(item.lastSyncedAt).slice(0, 10) : '未同步'}</Text>
                <View className='admin-row-actions'>
                  <Button className='admin-mini-button primary' loading={busy === `adopt:${item.grantId}`} onClick={() => submitAdopt(item)}>采纳</Button>
                  <Button className='admin-mini-button' loading={busy === `sync:${adoptionIdOf(item)}`} onClick={() => submitSync(item)}>同步</Button>
                  <Button className='admin-mini-button' onClick={() => showConflicts(item)}>冲突</Button>
                </View>
              </View>
            ))}
          </View>
          {!publicBanks.length ? <View className='admin-empty'>暂无可见公共题库授权。</View> : null}
        </View>

        <View className='admin-section'>
          <Text className='admin-section-title'>同步冲突</Text>
          {conflicts ? (
            <View className='admin-list'>
              <View className='admin-row'>
                <Text className='admin-row-main'>状态 {String(conflicts.status || conflicts.syncStatus || '-')}</Text>
                <Text className='admin-row-meta'>冲突数 {String(conflicts.conflictCount || 0)} · 最近同步 {String(conflicts.lastSyncedAt || '')}</Text>
                <Text className='admin-row-meta'>采纳平台会覆盖为平台新版；保留本地会记录本次冲突已处理。</Text>
                {conflictItems(conflicts).length ? (
                  <View className='admin-row-actions'>
                    <Button className='admin-mini-button primary' loading={busy === 'resolve-batch:accept_platform'} onClick={() => resolveVisibleConflicts('accept_platform')}>全部采纳平台</Button>
                    <Button className='admin-mini-button' loading={busy === 'resolve-batch:keep_local'} onClick={() => resolveVisibleConflicts('keep_local')}>全部保留本地</Button>
                  </View>
                ) : null}
              </View>
              {conflictItems(conflicts).slice(0, 20).map((item, index) => (
                <View className='admin-row' key={`${item.sourceQuestionId || index}-${item.targetQuestionId || ''}`}>
                  <Text className='admin-row-main'>题目冲突 {String(item.sourceQuestionId || '').slice(0, 8)}</Text>
                  <Text className='admin-row-meta'>目标 {String(item.targetQuestionId || '-').slice(0, 8)} · 平台 {String(item.sourceHash || '').slice(0, 10)} · 本地 {String(item.targetHash || '').slice(0, 10)}</Text>
                  <Text className='admin-row-meta'>上次平台 {String(item.previousSourceHash || '-').slice(0, 10)}</Text>
                  <View className='admin-row-actions'>
                    <Button className='admin-mini-button primary' loading={busy === `resolve:${item.sourceQuestionId}:accept_platform`} onClick={() => resolveConflict(item, 'accept_platform')}>采纳平台</Button>
                    <Button className='admin-mini-button' loading={busy === `resolve:${item.sourceQuestionId}:keep_local`} onClick={() => resolveConflict(item, 'keep_local')}>保留本地</Button>
                  </View>
                </View>
              ))}
              {!conflictItems(conflicts).length ? <View className='admin-empty'>当前没有待处理冲突。</View> : null}
            </View>
          ) : <View className='admin-empty'>同步后或点击“冲突”可查看最近冲突摘要。</View>}
        </View>
        {error ? <Text className='admin-error'>{error}</Text> : null}
      </View>
    </View>
  );
}
