import dayjs from 'dayjs'
import { calculateAge } from '@/utils/dateUtil'

/**
 * 患者信息展示的公共格式化工具。
 *
 * 背景（需求1）：患者信息需在**每个界面**展示 18 项：
 *   流水号、排队序号、姓名、性别、出生日期、年龄、项目、病区、病床号、
 *   登记时间、状态、患者电话、开单日期、距今天数、来源（门急住体检）、
 *   门诊号、医嘱号、开单医师
 *
 * 其中 **年龄** 与 **距今天数** 数据库无对应列，必须实时计算。
 * 原先该算法内联在 RoutinePanel.vue 中，多个界面若各自复制会造成口径不一致
 * （例如一个按 doctorTime 算、另一个按 createTime 算），故抽到此处统一。
 *
 * 注意：
 *   · 后端返回的生日/日期字段多为字符串（"2026-09-29" / "2026-09-29 10:00:00"），
 *     故这里统一用 dayjs 兼容解析，避免 Date 构造在不同浏览器下的差异。
 *   · 拿不到值时统一返回 ''（而不是 "NaN岁" / "NaN天"）。
 */

/**
 * 年龄：由生日实时计算，返回形如 "65岁"；无生日/生日非法时返回 ''
 *
 * 注意：库中 `pat_birthday` 可能是 `LocalDate`（"2000-01-01"）或
 * 时间戳/ISO 字符串；也可能存在脏值（如 "0000-00-00"）。
 * dayjs 对脏值返回 Invalid，`diff` 会得到 NaN，故这里先显式校验。
 */
export function formatAge(birthday?: string | number | Date | null): string {
  if (birthday === undefined || birthday === null || birthday === '') return ''
  const d = dayjs(birthday as any)
  if (!d.isValid()) return ''
  const age = calculateAge(birthday as unknown as Date)
  if (!Number.isFinite(age) || age < 0 || age > 150) return ''
  return `${age}岁`
}

/**
 * 距今天数：按**开单日期**（doctorTime）计算，返回形如 "3天"；无值时返回 ''
 *
 * 说明：需求1 的"距今天数"是给医生判断"这张单子压了多久"，
 *       因此基准必须是**开单日期**，而不是创建时间/登记时间。
 */
export function formatDaysFromOrder(orderTime?: string | number | Date | null): string {
  if (orderTime === undefined || orderTime === null || orderTime === '') return ''
  const d = dayjs(orderTime)
  if (!d.isValid()) return ''
  const days = dayjs().startOf('day').diff(d.startOf('day'), 'day')
  if (!Number.isFinite(days)) return ''
  return `${days}天`
}

/**
 * 距今天数（数值版，便于排序/条件样式）
 */
export function daysFromOrder(orderTime?: string | number | Date | null): number | null {
  if (orderTime === undefined || orderTime === null || orderTime === '') return null
  const d = dayjs(orderTime)
  if (!d.isValid()) return null
  return dayjs().startOf('day').diff(d.startOf('day'), 'day')
}

/**
 * 日期时间格式化（空值安全），默认 "YYYY-MM-DD HH:mm:ss"
 */
export function formatDateTime(
  value?: string | number | Date | null,
  format = 'YYYY-MM-DD HH:mm:ss'
): string {
  if (value === undefined || value === null || value === '') return ''
  const d = dayjs(value)
  return d.isValid() ? d.format(format) : ''
}

/**
 * 日期格式化（空值安全），默认 "YYYY-MM-DD"
 */
export function formatDateOnly(
  value?: string | number | Date | null,
  format = 'YYYY-MM-DD'
): string {
  return formatDateTime(value, format)
}

/**
 * 空值安全展示：null/undefined/'' 统一显示为 "-"
 */
export function displayOr(value?: string | number | null, placeholder = '-'): string {
  if (value === undefined || value === null || value === '') return placeholder
  return String(value)
}

/** 就诊卡号的解析结果：值 + 实际来源标注 */
export interface CardNoResult {
  /** 展示用值（无任何候选时为 '-'） */
  value: string
  /** 值的真实来源；有真卡号时为空串（无需标注） */
  from: '' | '门诊号' | '身份证' | '住院号'
}

/**
 * 判断一个值是否"像身份证号"（18 位数字末位可为 X，或 15 位纯数字）。
 *
 * 为什么需要按**值的形态**判断，而不是按字段名：
 *   实测 `appointment.outpatient_no` 混合存放两类内容 ——
 *     18 位身份证格式 103604 行（65.2%）
 *     12 位真门诊号    54204 行（34.1%，形如 882609282283）
 *   若一律按字段名标注为「门诊号」，那 65% 的身份证号依然被错标。
 */
export function looksLikeIdCard(v?: string | null): boolean {
  if (!v) return false
  const s = String(v).trim()
  return /^[0-9]{17}[0-9Xx]$/.test(s) || /^[0-9]{15}$/.test(s)
}

/**
 * 解析「门诊号」列的展示值与来源。
 *
 * 【背景】实测 HIS 在 S0201ECG 报文的 `patientPerson.id.item[1]`
 * （root `2.16.156.10011.1.15`，官方文档注释即「医保卡号」）
 * **恒为空**（222/222 条），因此 `medical_card` 全库为 NULL —— 详见《部署清单》5.1。
 *
 * 【列名变更（经确认）】由于该列**实际展示的是 `outpatient_no`**
 * （实测 159,116 行：65% 形如身份证、35% 为门诊号；`medical_card` 无一非空），
 * 页面标签已由「就诊卡号」改为「**门诊号**」，避免医护人员误读。
 *
 * 回退优先级与标注规则：
 *   1. `medicalCard`               → 无标注（HIS 补齐卡号后会走此分支，标注自动消失）
 *   2. `outpatientNo` 非身份证形态  → 标注「门诊号」
 *   3. `outpatientNo` 是身份证形态  → 标注「身份证」
 *   4. `patIdentityId`             → 标注「身份证」
 *   5. `hospitalNo`（住院号）       → 标注「住院号」
 *   6. 都没有                      → '-'
 *
 * > 标注的作用：`outpatient_no` 是**混合列**，里面既有真门诊号也有身份证号，
 * > 故按**值形态**（而非字段名）标注，便于医护人员区分。
 *
 * @param row 患者/预约/队列/租赁数据对象（兼容平铺字段与 patDetails）
 */
export function resolveCardNo(row: any): CardNoResult {
  if (row === undefined || row === null) return { value: '-', from: '' }
  const pd = row.patDetails || {}

  const medical = firstNonEmpty(row.medicalCard, pd.medicalCard)
  if (medical !== undefined) return { value: medical, from: '' }

  const outpatient = firstNonEmpty(row.outpatientNo, pd.outpatientNo)
  if (outpatient !== undefined) {
    // 该列实测混合了身份证号，按形态标注而非按字段名
    return { value: outpatient, from: looksLikeIdCard(outpatient) ? '身份证' : '门诊号' }
  }

  const identity = firstNonEmpty(row.patIdentityId, pd.patIdentityId)
  if (identity !== undefined) return { value: identity, from: '身份证' }

  const hospital = firstNonEmpty(row.hospitalNo, pd.hospitalNo)
  if (hospital !== undefined) return { value: hospital, from: '住院号' }

  return { value: '-', from: '' }
}

/** 取第一个非空（null/undefined/'' 均视为空）值，全空时返回 undefined */
export function firstNonEmpty(...vals: any[]): string | undefined {
  for (const v of vals) {
    if (v !== undefined && v !== null && v !== '') return String(v)
  }
  return undefined
}
