/**
 * DataTable cell renderers
 * @countermeasure/web-components/components/data-table
 */

import { type CellRenderer } from './types'

import { escapeHtml } from '../../utils/sanitize'
export { escapeHtml }

/** Format a byte count into a human-readable string (KB, MB, GB, TB) */
function formatBytes(bytes: number): string {
  if (bytes === 0) {
    return '0 B'
  }

  const units = ['B', 'KB', 'MB', 'GB', 'TB']
  const k = 1024
  const i = Math.floor(Math.log(bytes) / Math.log(k))
  const unitIndex = Math.min(i, units.length - 1)
  const unit = units[unitIndex]

  if (unit === undefined) {
    return `${String(bytes)} B`
  }

  const value = bytes / Math.pow(k, unitIndex)
  return `${value.toFixed(2)} ${unit}`
}

/** Format a date value as relative time (e.g. "2h ago") */
function formatRelativeTime(date: Date): string {
  const now = Date.now()
  const diffMs = now - date.getTime()
  const diffSec = Math.floor(diffMs / 1000)
  const diffMin = Math.floor(diffSec / 60)
  const diffHr = Math.floor(diffMin / 60)
  const diffDay = Math.floor(diffHr / 24)

  if (diffSec < 60) {
    return `${String(diffSec)}s ago`
  }
  if (diffMin < 60) {
    return `${String(diffMin)}m ago`
  }
  if (diffHr < 24) {
    return `${String(diffHr)}h ago`
  }
  return `${String(diffDay)}d ago`
}

/** Convert an unknown value to a string for display */
function valueToString(value: unknown): string {
  if (value === null || value === undefined) {
    return ''
  }
  if (typeof value === 'string') {
    return value
  }
  if (typeof value === 'number' || typeof value === 'boolean') {
    return String(value)
  }
  return JSON.stringify(value)
}

/**
 * Render a cell value based on the specified renderer type.
 * Returns an HTML string.
 */
export function renderCell(
  value: unknown,
  renderer: CellRenderer,
  options?: Record<string, unknown>
): string {
  const str = valueToString(value)

  switch (renderer) {
    case 'text': {
      return escapeHtml(str)
    }

    case 'badge': {
      const escaped = escapeHtml(str)
      const badgeMap = options?.badgeMap
      let badgeClass = 'default'
      if (badgeMap !== null && badgeMap !== undefined && typeof badgeMap === 'object') {
        const map = badgeMap as Record<string, string>
        const mapped = map[str]
        if (mapped !== undefined) {
          badgeClass = mapped
        }
      }
      return `<span class="cmm-badge cmm-badge--${escapeHtml(badgeClass)}">${escaped}</span>`
    }

    case 'link': {
      const escaped = escapeHtml(str)
      if (/^https?:\/\//.test(str)) {
        return `<a href="${escaped}" class="cmm-link" target="_blank" rel="noopener noreferrer">${escaped}</a>`
      }
      return escaped
    }

    case 'code': {
      return `<code class="cmm-code">${escapeHtml(str)}</code>`
    }

    case 'bytes': {
      const num = typeof value === 'number' ? value : parseFloat(str)
      if (Number.isNaN(num)) {
        return escapeHtml(str)
      }
      return escapeHtml(formatBytes(num))
    }

    case 'datetime': {
      const date = value instanceof Date ? value : new Date(str)
      if (Number.isNaN(date.getTime())) {
        return escapeHtml(str)
      }

      const format = options?.format
      if (format === 'iso') {
        return escapeHtml(date.toISOString())
      }
      if (format === 'locale') {
        return escapeHtml(date.toLocaleString())
      }
      // Default: relative
      return escapeHtml(formatRelativeTime(date))
    }

    case 'number': {
      const num = typeof value === 'number' ? value : parseFloat(str)
      if (Number.isNaN(num)) {
        return escapeHtml(str)
      }
      return escapeHtml(num.toLocaleString())
    }

    default: {
      return escapeHtml(str)
    }
  }
}
