import ExcelJS from 'exceljs';

import { MEDICAL_RECORD_FIELD_OPTIONS } from '../config/constants';

const MAX_ROWS = 2000;

export type ParsedMedicalRecordEntryRow = {
  field_code: string;
  title: string;
  content: string;
  remark: string;
  sort: number;
  status: number;
};

export type ParseMedicalRecordEntryResult = {
  items: ParsedMedicalRecordEntryRow[];
  invalidCount: number;
  rowCount: number;
  /** 首条无效原因，便于前端提示（如填了中文名未识别等） */
  firstInvalidReason?: string;
};

export type ParseMedicalRecordEntryOptions = {
  /**
   * 按字段拆分菜单导入时传入：字段编码列可留空，自动落到当前字段
   * 为什么需要：运营在「主诉词条」页导入时常不填字段编码，旧逻辑会整行判无效
   */
  defaultFieldCode?: string;
};

function normalizeCell(value: ExcelJS.CellValue): string {
  if (value === null || value === undefined) {
    return '';
  }
  if (typeof value === 'object') {
    // 富文本：Excel/WPS 局部加粗后常见 { richText: [...] }
    if ('richText' in value && Array.isArray((value as any).richText)) {
      return (value as any).richText
        .map((p: { text?: string }) => p?.text ?? '')
        .join('')
        .trim();
    }
    // 超链接 / 带 text 的复合值
    if ('text' in value) {
      return String((value as any).text ?? '').trim();
    }
    // 公式结果
    if ('result' in value) {
      return normalizeCell((value as any).result as ExcelJS.CellValue);
    }
  }
  if (typeof value === 'number') {
    return Number.isInteger(value) ? String(value) : String(value).trim();
  }
  if (typeof value === 'boolean') {
    return value ? '1' : '0';
  }
  return String(value).trim();
}

function normalizeHeader(value: string): string {
  return value.replace(/\s+/g, '').replace(/\u3000/g, '');
}

const FIELD_ALIASES = ['字段编码', 'field_code', '字段'];
const TITLE_ALIASES = ['词条名称', '名称', 'title'];
const CONTENT_ALIASES = ['正文', '内容', 'content'];
const REMARK_ALIASES = ['临床意义', '备注', 'remark'];
const SORT_ALIASES = ['排序', 'sort'];
const STATUS_ALIASES = ['状态', 'status'];

const VALID_FIELDS = new Set(MEDICAL_RECORD_FIELD_OPTIONS.map((o) => o.value));

/** 中文名 / 英文编码 → 标准 field_code（运营常按「字段编码说明」里的中文名填写） */
const FIELD_LABEL_TO_CODE = new Map<string, string>(
  MEDICAL_RECORD_FIELD_OPTIONS.flatMap((o) => [
    [normalizeHeader(o.value), o.value],
    [normalizeHeader(o.label), o.value],
  ]),
);

/**
 * 把单元格里的字段编码或中文名解析成标准 code；无法识别则返回空串
 */
function resolveFieldCode(raw: string): string {
  const key = normalizeHeader(raw);
  if (!key) return '';
  return FIELD_LABEL_TO_CODE.get(key) || (VALID_FIELDS.has(raw) ? raw : '');
}

function buildHeaderIndexMap(headerRow: ExcelJS.Row): Record<string, number> {
  const map: Record<string, number> = {};
  headerRow.eachCell({ includeEmpty: true }, (cell, colNumber) => {
    const key = normalizeHeader(normalizeCell(cell.value));
    if (key) {
      map[key] = colNumber;
    }
  });
  return map;
}

function pickColumn(
  headerMap: Record<string, number>,
  aliases: string[],
): number | undefined {
  for (const alias of aliases) {
    const col = headerMap[normalizeHeader(alias)];
    if (col) return col;
  }
  return undefined;
}

function getCellText(row: ExcelJS.Row, col?: number): string {
  if (!col) return '';
  return normalizeCell(row.getCell(col).value);
}

function getCellInt(row: ExcelJS.Row, col?: number, fallback = 0): number {
  if (!col) return fallback;
  const raw = normalizeCell(row.getCell(col).value);
  if (raw === '') return fallback;
  const n = Number(raw);
  return Number.isFinite(n) ? Math.trunc(n) : fallback;
}

function rowIsEmpty(row: ExcelJS.Row): boolean {
  let hasValue = false;
  row.eachCell({ includeEmpty: false }, (cell) => {
    if (normalizeCell(cell.value) !== '') {
      hasValue = true;
    }
  });
  return !hasValue;
}

/**
 * 优先取「词条导入」sheet；避免用户改完后激活了「字段编码说明」导致读错表
 */
function pickImportSheet(workbook: ExcelJS.Workbook): ExcelJS.Worksheet | undefined {
  const byName = workbook.worksheets.find((s) => s.name === '词条导入');
  return byName || workbook.worksheets[0];
}

/**
 * 解析词条导入 Excel（首行为表头）
 * 字段编码列支持英文 code 或中文名；按字段页可传 defaultFieldCode 允许留空
 */
export async function parseMedicalRecordEntryExcelBuffer(
  buffer: ArrayBuffer,
  options: ParseMedicalRecordEntryOptions = {},
): Promise<ParseMedicalRecordEntryResult> {
  const workbook = new ExcelJS.Workbook();
  await workbook.xlsx.load(buffer);
  const sheet = pickImportSheet(workbook);
  if (!sheet) {
    return { items: [], invalidCount: 0, rowCount: 0 };
  }
  const defaultFieldCode = resolveFieldCode(options.defaultFieldCode || '');
  const headerRow = sheet.getRow(1);
  const headerMap = buildHeaderIndexMap(headerRow);
  const fieldCol = pickColumn(headerMap, FIELD_ALIASES);
  const titleCol = pickColumn(headerMap, TITLE_ALIASES);
  const contentCol = pickColumn(headerMap, CONTENT_ALIASES);
  const remarkCol = pickColumn(headerMap, REMARK_ALIASES);
  const sortCol = pickColumn(headerMap, SORT_ALIASES);
  const statusCol = pickColumn(headerMap, STATUS_ALIASES);
  // 无默认字段时必须有「字段编码」列；有默认字段时仅要求「词条名称」
  if (!titleCol) {
    throw new Error('模板表头缺少「词条名称」列，请重新下载模板');
  }
  if (!fieldCol && !defaultFieldCode) {
    throw new Error('模板表头缺少「字段编码」列，请重新下载模板');
  }
  const items: ParsedMedicalRecordEntryRow[] = [];
  let invalidCount = 0;
  let rowCount = 0;
  let firstInvalidReason: string | undefined;
  const maxRow = Math.min(sheet.rowCount || 0, MAX_ROWS + 1);
  for (let rowNumber = 2; rowNumber <= maxRow; rowNumber++) {
    const row = sheet.getRow(rowNumber);
    if (rowIsEmpty(row)) continue;
    rowCount++;
    if (items.length >= MAX_ROWS) break;
    const rawField = getCellText(row, fieldCol);
    const title = getCellText(row, titleCol);
    let field_code = resolveFieldCode(rawField);
    if (!field_code && defaultFieldCode) {
      field_code = defaultFieldCode;
    }
    if (!title) {
      invalidCount++;
      if (!firstInvalidReason) {
        firstInvalidReason = `第${rowNumber}行：词条名称为空`;
      }
      continue;
    }
    if (!field_code) {
      invalidCount++;
      if (!firstInvalidReason) {
        firstInvalidReason = rawField
          ? `第${rowNumber}行：字段「${rawField}」无法识别（请填英文编码如 chief_complaint，或中文名如「主诉」）`
          : `第${rowNumber}行：字段编码为空（请对照「字段编码说明」填写，或从对应字段菜单页导入）`;
      }
      continue;
    }
    const statusRaw = getCellInt(row, statusCol, 1);
    items.push({
      field_code,
      title,
      content: getCellText(row, contentCol) || title,
      remark: getCellText(row, remarkCol),
      sort: getCellInt(row, sortCol, 0),
      status: statusRaw === 0 ? 0 : 1,
    });
  }
  return { items, invalidCount, rowCount, firstInvalidReason };
}
