<script setup lang="ts">
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue';
import type { FormRules } from 'element-plus';
import JSZip from 'jszip';
import '@univerjs/preset-sheets-core/lib/index.css';
import {
  createPerformanceSheet,
  downloadFile,
  fetchPerformanceSheet,
  fetchPerformanceTemplateCurrent,
  sendPerformanceSheet,
  updatePerformanceSheetExcel,
  uploadFile
} from '@/service/api';
import { useForm, useFormRules } from '@/hooks/common/form';
import { createDefaultPeriodMonth, normalizeScoreText } from './performance-shared';

defineOptions({ name: 'PerformanceExcelEditorDrawer' });

interface SubordinateOption {
  label: string;
  value: string;
}

interface Props {
  rowData?: Api.Performance.Sheet.Sheet | null;
  mode: 'view' | 'edit' | 'create';
  subordinateOptions?: SubordinateOption[];
  showApprovalFooter?: boolean;
}

const props = withDefaults(defineProps<Props>(), {
  rowData: null,
  subordinateOptions: () => [],
  showApprovalFooter: false
});

const visible = defineModel<boolean>('visible', { default: false });

const emit = defineEmits<{
  saved: [];
  savedAndSent: [];
  startApproval: [];
}>();

const { formRef, validate } = useForm();
const { createRequiredRule } = useFormRules();

const containerRef = ref<HTMLDivElement>();
const loading = ref(false);
const saving = ref(false);
const sending = ref(false);
const currentSheet = ref<Api.Performance.Sheet.Sheet | null>(null);
const currentTemplate = ref<Api.Performance.Template.Template | null>(null);
const errorMessage = ref('');
const viewportWidth = ref(typeof window === 'undefined' ? 1920 : window.innerWidth);

const createForm = reactive({
  periodMonth: createDefaultPeriodMonth(),
  employeeId: ''
});

const createFormRules = computed<FormRules>(() => ({
  periodMonth: [createRequiredRule('请选择绩效月份')],
  employeeId: [createRequiredRule('请选择下属')]
}));
const ASSESSED_EMPLOYEE_TEXT_PATTERN = /(被考核人\s*[：:]\s*)(.+)/u;

let univerInstance: any = null;
let univerAPI: any = null;
let LuckyExcelModule: any = null;
let createUniverFn: any = null;
let UniverSheetsCorePresetFn: any = null;
let univerLocales: Record<string, unknown> | null = null;
let excelRuntimeLoading: Promise<void> | null = null;
const DEFAULT_SHEET_ZOOM_RATIO = 0.4;

const isCreateMode = computed(() => props.mode === 'create');
const drawerTitle = computed(() => {
  let action = '查看';
  if (isCreateMode.value) action = '新增';
  else if (props.mode === 'edit') action = '编辑';
  const selectedEmployee = props.subordinateOptions.find(opt => opt.value === createForm.employeeId);
  const name = currentSheet.value?.employeeName || props.rowData?.employeeName || selectedEmployee?.label || '';

  return `${action}绩效 Excel${name ? ` - ${name}` : ''}`;
});
const canSave = computed(() => props.mode !== 'view');
const showApprovalFooter = computed(() => props.mode === 'view' && props.showApprovalFooter);
const drawerSize = computed(() => (viewportWidth.value >= 2560 ? '60%' : '88%'));

function syncViewportWidth() {
  viewportWidth.value = window.innerWidth;
}

function handleClose() {
  visible.value = false;
}

function handleStartApproval() {
  emit('startApproval');
}

function disposeUniver() {
  try {
    univerInstance?.dispose?.();
  } catch {
    // ignore dispose errors so closing the drawer still works
  }

  univerInstance = null;
  univerAPI = null;

  if (containerRef.value) {
    containerRef.value.innerHTML = '';
  }
}

async function ensureExcelRuntime() {
  if (LuckyExcelModule && createUniverFn && UniverSheetsCorePresetFn && univerLocales) {
    return;
  }

  if (!excelRuntimeLoading) {
    excelRuntimeLoading = Promise.all([
      import('@zwight/luckyexcel'),
      import('@univerjs/presets'),
      import('@univerjs/preset-sheets-core'),
      import('@univerjs/preset-sheets-core/locales/zh-CN'),
      import('@univerjs/preset-sheets-core/locales/en-US')
    ]).then(([luckyexcelModule, presetsModule, sheetsCoreModule, zhCNLocaleModule, enUSLocaleModule]) => {
      LuckyExcelModule = luckyexcelModule.default || luckyexcelModule;
      createUniverFn = presetsModule.createUniver;
      UniverSheetsCorePresetFn = sheetsCoreModule.UniverSheetsCorePreset;
      univerLocales = {
        'zh-CN': zhCNLocaleModule.default || zhCNLocaleModule,
        'en-US': enUSLocaleModule.default || enUSLocaleModule
      };
    });
  }

  await excelRuntimeLoading;
}

function transformExcelToUniver(file: File) {
  return new Promise<any>((resolve, reject) => {
    LuckyExcelModule.transformExcelToUniver(
      file,
      (snapshot: any) => resolve(snapshot || {}),
      (error: unknown) => reject(error)
    );
  });
}

function transformUniverToExcel(snapshot: any, fileName: string) {
  return new Promise<BlobPart>((resolve, reject) => {
    LuckyExcelModule.transformUniverToExcel({
      snapshot,
      fileName,
      getBuffer: true,
      success: (buffer?: unknown) => {
        if (!buffer) {
          reject(new Error('Excel 导出结果为空'));
          return;
        }

        resolve(buffer as BlobPart);
      },
      error: (error: Error) => reject(error)
    });
  });
}

async function normalizeExcelBuffer(buffer: BlobPart): Promise<ArrayBuffer> {
  if (buffer instanceof ArrayBuffer) {
    return buffer;
  }

  if (ArrayBuffer.isView(buffer)) {
    return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength).slice().buffer;
  }

  if (buffer instanceof Blob) {
    return buffer.arrayBuffer();
  }

  throw new Error('Excel 导出结果格式不受支持');
}

function applySheetZoomRatio(snapshot: any, zoomRatio = DEFAULT_SHEET_ZOOM_RATIO) {
  const data = snapshot || {};

  if (data.sheets && typeof data.sheets === 'object') {
    Object.values(data.sheets).forEach((sheet: any) => {
      if (sheet && typeof sheet === 'object') {
        sheet.zoomRatio = zoomRatio;
      }
    });
  }

  return data;
}

function replaceAssessedEmployeeText(value: string, employeeName: string) {
  if (!ASSESSED_EMPLOYEE_TEXT_PATTERN.test(value)) {
    return value;
  }

  return value.replace(ASSESSED_EMPLOYEE_TEXT_PATTERN, `$1${employeeName}`);
}

function injectAssessedEmployeeName(snapshot: any, employeeName: string) {
  if (!snapshot || !employeeName) return snapshot;

  const visited = new WeakSet<object>();

  const walk = (target: unknown) => {
    if (typeof target === 'string') {
      return replaceAssessedEmployeeText(target, employeeName);
    }

    if (!target || typeof target !== 'object') {
      return target;
    }

    if (visited.has(target as object)) {
      return target;
    }

    visited.add(target as object);

    if (Array.isArray(target)) {
      target.forEach((item, index) => {
        target[index] = walk(item);
      });

      return target;
    }

    Object.entries(target).forEach(([key, value]) => {
      (target as Record<string, unknown>)[key] = walk(value);
    });

    return target;
  };

  return walk(snapshot);
}

function findFirstElementByLocalName(parent: Element | Document, localName: string) {
  return Array.from(parent.childNodes).find(
    node => node.nodeType === Node.ELEMENT_NODE && (node as Element).localName === localName
  ) as Element | undefined;
}

function ensureChildElement(document: XMLDocument, parent: Element, localName: string) {
  const existing = Array.from(parent.childNodes).find(
    node => node.nodeType === Node.ELEMENT_NODE && (node as Element).localName === localName
  ) as Element | undefined;

  if (existing) return existing;

  const namespace = parent.namespaceURI || document.documentElement?.namespaceURI || null;
  const element = namespace ? document.createElementNS(namespace, localName) : document.createElement(localName);
  parent.appendChild(element);

  return element;
}

function createXmlRoot(document: XMLDocument, localName: string, namespace?: string | null) {
  const root = namespace ? document.createElementNS(namespace, localName) : document.createElement(localName);
  document.appendChild(root);

  return root;
}

function applyWorksheetZoomXml(xmlText: string, zoomScale: number) {
  const document = new DOMParser().parseFromString(xmlText, 'application/xml');
  const root =
    document.documentElement && document.documentElement.localName !== 'parsererror'
      ? document.documentElement
      : createXmlRoot(document, 'worksheet', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
  const sheetViews = ensureChildElement(document, root, 'sheetViews');
  const sheetView =
    findFirstElementByLocalName(sheetViews, 'sheetView') || ensureChildElement(document, sheetViews, 'sheetView');

  sheetView.setAttribute('workbookViewId', sheetView.getAttribute('workbookViewId') || '0');
  sheetView.setAttribute('zoomScale', String(zoomScale));
  sheetView.setAttribute('zoomScaleNormal', String(zoomScale));
  sheetView.setAttribute('zoomScalePageLayoutView', String(zoomScale));

  return new XMLSerializer().serializeToString(document);
}

function applyWorkbookZoomXml(xmlText: string, zoomScale: number) {
  const document = new DOMParser().parseFromString(xmlText, 'application/xml');
  const root =
    document.documentElement && document.documentElement.localName !== 'parsererror'
      ? document.documentElement
      : createXmlRoot(document, 'workbook', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
  const bookViews = ensureChildElement(document, root, 'bookViews');
  const workbookView =
    findFirstElementByLocalName(bookViews, 'workbookView') || ensureChildElement(document, bookViews, 'workbookView');

  workbookView.setAttribute('zoomScale', String(zoomScale));
  workbookView.setAttribute('zoomScaleNormal', String(zoomScale));

  return new XMLSerializer().serializeToString(document);
}

async function applyExcelZoomMetadata(buffer: BlobPart, zoomRatio = DEFAULT_SHEET_ZOOM_RATIO) {
  const zoomScale = Math.max(10, Math.min(400, Math.round(zoomRatio * 100)));
  const zip = await JSZip.loadAsync(await normalizeExcelBuffer(buffer));
  const worksheetPaths = Object.keys(zip.files).filter(path => /^xl\/worksheets\/sheet\d+\.xml$/u.test(path));

  await Promise.all(
    worksheetPaths.map(async path => {
      const entry = zip.file(path);
      if (!entry) return;

      const xmlText = await entry.async('string');
      zip.file(path, applyWorksheetZoomXml(xmlText, zoomScale));
    })
  );

  const workbookEntry = zip.file('xl/workbook.xml');
  if (workbookEntry) {
    const workbookXml = await workbookEntry.async('string');
    zip.file('xl/workbook.xml', applyWorkbookZoomXml(workbookXml, zoomScale));
  }

  return zip.generateAsync({ type: 'arraybuffer' });
}

function createWorkbook(snapshot: any) {
  if (!containerRef.value) return;

  disposeUniver();
  const { univer, univerAPI: api } = createUniverFn({
    locale: 'zh-CN',
    locales: univerLocales || undefined,
    presets: [
      UniverSheetsCorePresetFn({
        container: containerRef.value,
        header: true,
        toolbar: props.mode !== 'view',
        formulaBar: props.mode !== 'view'
      })
    ]
  });

  univerInstance = univer;
  univerAPI = api;

  const unitType = api?.Enum?.UniverInstanceType?.UNIVER_SHEET;
  if (!unitType) {
    throw new Error('Univer 工作簿初始化失败');
  }

  // 在 snapshot 数据中预设缩放比例，保证在线查看和导出文件使用同一套缩放值
  const data = applySheetZoomRatio(snapshot);

  univer.createUnit(unitType, data);
}

function getActiveWorkbook() {
  return univerAPI?.getActiveWorkbook?.();
}

function parseCellAddress(address?: string | null) {
  const text = String(address || '')
    .trim()
    .toUpperCase();
  const matched = text.match(/^([A-Z]+)(\d+)$/u);
  if (!matched) return null;

  const [, letters, rowText] = matched;
  let column = 0;
  for (const letter of letters) {
    column = column * 26 + letter.charCodeAt(0) - 64;
  }

  return {
    row: Number(rowText) - 1,
    column: column - 1
  };
}

function readCell(address?: string | null) {
  const position = parseCellAddress(address);
  const workbook = getActiveWorkbook();
  const sheet = workbook?.getActiveSheet?.();
  if (!position || !sheet) return '';

  try {
    const range = sheet.getRange(position.row, position.column);
    const value = range?.getValue?.();

    return normalizeScoreText(value);
  } catch {
    return '';
  }
}

function readScores() {
  const mapping = currentTemplate.value?.scoreCellMapping;

  return {
    actualScoreTotal: readCell(mapping?.actualScoreTotalCell),
    baseScoreTotal: readCell(mapping?.baseScoreTotalCell),
    extraScoreTotal: readCell(mapping?.extraScoreTotalCell)
  };
}

function getCreateEmployeeName() {
  return props.subordinateOptions.find(opt => opt.value === createForm.employeeId)?.label || '';
}

function applyCreateEmployeeName(snapshot: any) {
  if (!isCreateMode.value) return snapshot;

  const employeeName = getCreateEmployeeName();
  if (!employeeName) return snapshot;

  return injectAssessedEmployeeName(snapshot, employeeName);
}

function createInitialFileName() {
  const sheet = currentSheet.value;
  if (sheet) {
    return sheet.fileName || `${sheet.periodMonth}-绩效表_${sheet.employeeName}.xlsx`;
  }

  if (isCreateMode.value && createForm.periodMonth && createForm.employeeId) {
    return `${createForm.periodMonth}-绩效表_${getCreateEmployeeName()}.xlsx`;
  }

  return '绩效表.xlsx';
}

async function loadWorkbook() {
  loading.value = true;
  errorMessage.value = '';
  currentSheet.value = null;
  currentTemplate.value = null;

  try {
    let sourceFileId = '';

    if (isCreateMode.value) {
      const templateResult = await fetchPerformanceTemplateCurrent();
      if (templateResult.error || !templateResult.data) {
        errorMessage.value = '当前没有可用的绩效模板';
        return;
      }

      currentTemplate.value = templateResult.data;
      sourceFileId = templateResult.data.fileId;
    } else {
      if (!props.rowData?.id) return;

      const [sheetResult, templateResult] = await Promise.all([
        fetchPerformanceSheet(props.rowData.id),
        fetchPerformanceTemplateCurrent()
      ]);

      if (sheetResult.error || !sheetResult.data) {
        errorMessage.value = '绩效表详情加载失败';
        return;
      }

      currentSheet.value = sheetResult.data;
      currentTemplate.value = templateResult.error ? null : templateResult.data;
      sourceFileId = currentSheet.value.fileId || currentTemplate.value?.fileId || '';
    }

    if (!sourceFileId) {
      errorMessage.value = '当前绩效表没有可用的 Excel 文件';
      return;
    }

    const fileResult = await downloadFile(sourceFileId);
    if (fileResult.error || !fileResult.data) {
      errorMessage.value = 'Excel 文件下载失败';
      return;
    }

    await ensureExcelRuntime();
    const file = new File([fileResult.data], createInitialFileName(), {
      type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    });
    const snapshot = await transformExcelToUniver(file);
    await nextTick();
    createWorkbook(applyCreateEmployeeName(snapshot));
  } catch (error) {
    errorMessage.value = error instanceof Error ? error.message : 'Excel 解析失败';
  } finally {
    loading.value = false;
  }
}

async function ensureCreatedSheet() {
  if (currentSheet.value) {
    return currentSheet.value;
  }

  if (!createForm.periodMonth || !createForm.employeeId) {
    throw new Error('请先填写绩效月份和下属');
  }

  const createResult = await createPerformanceSheet({
    periodMonth: createForm.periodMonth,
    employeeId: createForm.employeeId
  });

  if (createResult.error || !createResult.data) {
    throw new Error('创建绩效记录失败');
  }

  const sheetResult = await fetchPerformanceSheet(createResult.data);
  if (sheetResult.error || !sheetResult.data) {
    throw new Error('绩效记录已创建，但加载详情失败');
  }

  currentSheet.value = sheetResult.data;
  return sheetResult.data;
}

async function executeSave(): Promise<Api.Performance.Sheet.Sheet | null> {
  const workbook = getActiveWorkbook();
  if (!workbook) return null;

  const scores = readScores();
  if (!scores.actualScoreTotal || !scores.baseScoreTotal || !scores.extraScoreTotal) {
    window.$message?.warning('未能读取完整的三种得分总计，请检查模板单元格映射配置');
    return null;
  }

  // create 模式先校验表单
  if (isCreateMode.value) {
    try {
      await validate();
    } catch {
      return null;
    }
  }

  await ensureExcelRuntime();
  const sheet = await ensureCreatedSheet();
  const snapshot = applySheetZoomRatio(workbook.save());
  const fileName = createInitialFileName();
  const buffer = await transformUniverToExcel(snapshot, fileName);
  const excelBuffer = await applyExcelZoomMetadata(buffer);
  const file = new File([excelBuffer], fileName, {
    type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
  });
  const uploadResult = await uploadFile(file, `performance/sheets/${sheet.periodMonth}`);

  if (uploadResult.error || !uploadResult.data) {
    return null;
  }

  const updateResult = await updatePerformanceSheetExcel(sheet.id, {
    fileId: uploadResult.data.id,
    fileName,
    fileVersion: sheet.fileVersion,
    actualScoreTotal: scores.actualScoreTotal,
    baseScoreTotal: scores.baseScoreTotal,
    extraScoreTotal: scores.extraScoreTotal
  });

  if (updateResult.error) {
    return null;
  }

  return sheet;
}

async function handleSaveDraft() {
  saving.value = true;
  try {
    const sheet = await executeSave();
    if (!sheet) return;

    window.$message?.success(isCreateMode.value ? '绩效表已保存为草稿' : '绩效 Excel 已保存');
    visible.value = false;
    emit('saved');
  } catch (error) {
    window.$message?.error(error instanceof Error ? error.message : '绩效 Excel 保存失败');
  } finally {
    saving.value = false;
  }
}

async function handleSaveAndSend() {
  sending.value = true;
  try {
    const sheet = await executeSave();
    if (!sheet) return;

    const sendResult = await sendPerformanceSheet(sheet.id);
    if (sendResult.error) return;

    window.$message?.success('绩效表已保存并发送');
    visible.value = false;
    emit('savedAndSent');
  } catch (error) {
    window.$message?.error(error instanceof Error ? error.message : '绩效表发送失败');
  } finally {
    sending.value = false;
  }
}

watch(visible, async isVisible => {
  if (!isVisible) {
    disposeUniver();
    currentSheet.value = null;
    currentTemplate.value = null;
    // 重置创建表单
    createForm.periodMonth = createDefaultPeriodMonth();
    createForm.employeeId = '';
    return;
  }

  await nextTick();
  await loadWorkbook();
});

watch(
  () => createForm.employeeId,
  async (employeeId, previousEmployeeId) => {
    if (!visible.value || !isCreateMode.value || !employeeId || employeeId === previousEmployeeId) {
      return;
    }

    const workbook = getActiveWorkbook();
    if (!workbook) return;

    const snapshot = workbook.save();
    await nextTick();
    createWorkbook(applyCreateEmployeeName(snapshot));
  }
);

onBeforeUnmount(() => {
  window.removeEventListener('resize', syncViewportWidth);
  disposeUniver();
});

onMounted(() => {
  syncViewportWidth();
  window.addEventListener('resize', syncViewportWidth);
});
</script>

<template>
  <ElDrawer
    v-model="visible"
    class="performance-excel-editor-drawer"
    :title="drawerTitle"
    body-class="performance-excel-editor-drawer__body"
    :size="drawerSize"
    :close-on-click-modal="false"
    append-to-body
  >
    <!-- 创建模式下的表单区域 -->
    <div v-if="isCreateMode" class="performance-excel-editor__create-form">
      <ElForm ref="formRef" :model="createForm" :rules="createFormRules" label-position="top" inline>
        <ElFormItem label="绩效月份" prop="periodMonth" class="performance-excel-editor__form-item">
          <ElDatePicker
            v-model="createForm.periodMonth"
            type="month"
            value-format="YYYY-MM"
            placeholder="选择绩效月份"
          />
        </ElFormItem>
        <ElFormItem label="下属" prop="employeeId" class="performance-excel-editor__form-item">
          <ElSelect v-model="createForm.employeeId" filterable placeholder="选择下属" style="width: 200px">
            <ElOption v-for="opt in props.subordinateOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
          </ElSelect>
        </ElFormItem>
      </ElForm>
    </div>

    <div v-loading="loading" class="performance-excel-editor">
      <ElAlert
        v-if="errorMessage"
        class="performance-excel-editor__alert"
        type="error"
        :title="errorMessage"
        show-icon
        :closable="false"
      />

      <div ref="containerRef" class="performance-excel-editor__container" />
    </div>

    <template v-if="canSave || showApprovalFooter" #footer>
      <div class="performance-excel-editor__footer">
        <template v-if="canSave">
          <ElButton :loading="saving" :disabled="sending" @click="handleSaveDraft">保存草稿</ElButton>
          <ElButton type="primary" :loading="sending" :disabled="saving" @click="handleSaveAndSend">发送绩效</ElButton>
        </template>
        <template v-else-if="showApprovalFooter">
          <ElButton @click="handleClose">退出审批</ElButton>
          <ElButton type="primary" @click="handleStartApproval">开始审批</ElButton>
        </template>
      </div>
    </template>
  </ElDrawer>
</template>

<style scoped lang="scss">
:global(.performance-excel-editor-drawer__body) {
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding: 20px 24px 0;
}

.performance-excel-editor__create-form {
  flex-shrink: 0;
  margin-bottom: 16px;
  padding: 16px 20px;
  border: 1px solid var(--el-border-color-light);
  border-radius: 8px;
  background: var(--el-fill-color-blank);
}

.performance-excel-editor__form-item {
  margin-bottom: 0;
  margin-right: 24px;
}

.performance-excel-editor__footer {
  display: flex;
  justify-content: flex-end;
  gap: 12px;
  padding: 12px 24px 20px;
  border-top: 1px solid var(--el-border-color-lighter);
  background: var(--el-bg-color);
}

.performance-excel-editor {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-height: 0;
  height: 100%;
}

.performance-excel-editor__alert {
  margin-bottom: 12px;
  flex-shrink: 0;
}

.performance-excel-editor__container {
  flex: 1;
  min-height: 0;
  border: 1px solid var(--el-border-color-light);
  border-radius: 8px;
  overflow: hidden;
  background: var(--el-bg-color);
}
</style>
