/**
 * 日志内容处理：base64 压缩解压 + ANSI 转 HTML
 */

/** 尝试将后端可能返回的压缩/base64 内容还原为明文 */
export function decodeLogContent(raw: string): string {
  if (!raw) return ''
  // 若包含 ANSI 或明显是明文，直接返回
  if (raw.includes('\u001b[') || raw.includes('\n') || raw.length < 40) return raw

  // 纯 base64 形态尝试解码
  const compact = raw.replace(/\s+/g, '')
  if (!/^[A-Za-z0-9+/=]+$/.test(compact) || compact.length % 4 !== 0) return raw

  try {
    const binary = atob(compact)
    // 尝试 inflate（部分后端用 gzip/deflate 再 base64）
    if (typeof DecompressionStream !== 'undefined') {
      // 同步路径：先按 UTF-8 解码；若失败再当明文 base64 文本
      try {
        const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0))
        // 非 gzip 魔数则直接当 UTF-8
        if (bytes[0] === 0x1f && bytes[1] === 0x8b) {
          // 异步不适合这里，回退文本
        }
        return new TextDecoder().decode(bytes)
      } catch {
        return binary
      }
    }
    return decodeURIComponent(escape(binary))
  } catch {
    return raw
  }
}

/** 转义 HTML */
function escapeHtml(s: string) {
  return s
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
}

const ANSI_COLORS: Record<string, string> = {
  '30': '#000',
  '31': '#ef4444',
  '32': '#22c55e',
  '33': '#eab308',
  '34': '#3b82f6',
  '35': '#a855f7',
  '36': '#06b6d4',
  '37': '#e5e7eb',
  '90': '#6b7280',
  '91': '#f87171',
  '92': '#4ade80',
  '93': '#facc15',
  '94': '#60a5fa',
  '95': '#c084fc',
  '96': '#22d3ee',
  '97': '#f9fafb',
}

/** ANSI → HTML（轻量实现，覆盖常用前景色/粗体/重置） */
export function ansiToHtml(input: string): string {
  const text = decodeLogContent(input)
  if (!text.includes('\u001b[') && !text.includes('\x1b[')) {
    return escapeHtml(text)
  }

  let html = ''
  let open = false
  let style = ''
  const re = /\x1b\[([0-9;]*)m/g
  let last = 0
  let m: RegExpExecArray | null
  while ((m = re.exec(text))) {
    const chunk = text.slice(last, m.index)
    if (chunk) html += open ? `<span style="${style}">${escapeHtml(chunk)}</span>` : escapeHtml(chunk)
    last = m.index + m[0].length
    const codes = (m[1] || '0').split(';').filter(Boolean)
    if (codes.length === 0 || codes.includes('0')) {
      open = false
      style = ''
      continue
    }
    const parts: string[] = []
    for (const c of codes) {
      if (c === '1') parts.push('font-weight:700')
      else if (c === '2') parts.push('opacity:.75')
      else if (c === '4') parts.push('text-decoration:underline')
      else if (ANSI_COLORS[c]) parts.push(`color:${ANSI_COLORS[c]}`)
    }
    if (parts.length) {
      open = true
      style = parts.join(';')
    }
  }
  const rest = text.slice(last)
  if (rest) html += open ? `<span style="${style}">${escapeHtml(rest)}</span>` : escapeHtml(rest)
  return html
}
