/**
 * User-visible absolute timestamps — always Asia/Shanghai (北京时间).
 * API / DB store UTC; parse with parseApiDateTime before formatting.
 */

export const BEIJING_TZ = 'Asia/Shanghai'

/** Parse API/MySQL datetime (naive UTC or ISO Z) into a Date instant. */
export function parseApiDateTime(raw: string | null | undefined): Date | null {
  if (raw == null) return null
  const s = String(raw).trim()
  if (!s) return null

  let normalized = s
  if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/.test(s)) {
    normalized = `${s.replace(' ', 'T')}Z`
  } else if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/.test(s)) {
    normalized = `${s}Z`
  } else if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+$/.test(s)) {
    normalized = `${s}Z`
  }

  const d = new Date(normalized)
  return Number.isNaN(d.getTime()) ? null : d
}

export interface BeijingWallParts {
  minute: number
  hour: number
  day: number
  month: number
  /** Cron dow: 0=Sunday … 6=Saturday */
  dow: number
}

const BEIJING_WEEKDAY: Record<string, number> = {
  Sun: 0,
  Mon: 1,
  Tue: 2,
  Wed: 3,
  Thu: 4,
  Fri: 5,
  Sat: 6,
}

/** Wall-clock components in Asia/Shanghai for cron / schedule forms. */
export function beijingWallParts(d: Date): BeijingWallParts {
  const p = beijingParts(d)
  const weekday = new Intl.DateTimeFormat('en-US', {
    timeZone: BEIJING_TZ,
    weekday: 'short',
  }).format(d)
  return {
    minute: parseInt(p.minute!, 10),
    hour: parseInt(p.hour!, 10),
    day: parseInt(p.day!, 10),
    month: parseInt(p.month!, 10),
    dow: BEIJING_WEEKDAY[weekday] ?? 0,
  }
}

function beijingParts(d: Date): Record<string, string> {
  const parts = new Intl.DateTimeFormat('en-US', {
    timeZone: BEIJING_TZ,
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
    hour12: false,
  }).formatToParts(d)
  return Object.fromEntries(
    parts.filter((p) => p.type !== 'literal').map((p) => [p.type, p.value]),
  )
}

function formatParts(d: Date, mode: 'datetime' | 'date' | 'time'): string {
  const p = beijingParts(d)
  if (mode === 'date') return `${p.year}-${p.month}-${p.day}`
  if (mode === 'time') return `${p.hour}:${p.minute}:${p.second}`
  return `${p.year}-${p.month}-${p.day} ${p.hour}:${p.minute}:${p.second}`
}

function resolveDate(raw: string | Date | null | undefined): Date | null {
  if (raw instanceof Date) return Number.isNaN(raw.getTime()) ? null : raw
  return parseApiDateTime(raw)
}

/** YYYY-MM-DD HH:mm:ss in Beijing. */
export function formatDateTimeBeijing(raw: string | Date | null | undefined): string {
  const d = resolveDate(raw)
  if (!d) {
    if (typeof raw === 'string' && raw.trim()) {
      return raw.replace('T', ' ').slice(0, 19)
    }
    return '—'
  }
  return formatParts(d, 'datetime')
}

/** YYYY-MM-DD in Beijing. */
export function formatDateBeijing(raw: string | Date | null | undefined): string {
  const d = resolveDate(raw)
  if (!d) return '—'
  return formatParts(d, 'date')
}

/** HH:mm:ss in Beijing. */
export function formatTimeBeijing(raw: string | Date | null | undefined): string {
  const d = resolveDate(raw)
  if (!d) return '—'
  return formatParts(d, 'time')
}

/** Today's date in Beijing (for filenames, etc.). */
export function beijingTodayYmd(): string {
  return formatDateBeijing(new Date())
}
