/**
 * RecordDetailDrawer pure helpers (U3 / R2).
 *
 * Pure functions — no Vue, no store, no I/O. Safe to unit-test in isolation.
 *
 * Responsibilities:
 * - formatFieldValue: text/number/date/select/multiselect/formula/lookup → display string
 * - getAttachmentFileList / getImageThumbnailList: normalize attachment/image
 *   field values (which may be IAttachmentMeta[] or stale legacy shapes) into
 *   a uniform {name, url} / {url, alt} list for the drawer.
 * - getRecordTitle: first text-type field value, fallback "未命名记录".
 * - isFieldEditable: user-owned fields are editable; agent-owned + formula +
 *   lookup are read-only (formula/lookup are computed, not user data).
 * - getDrawerWidth: '100vw' on mobile, else 480px (≤10 fields) / 640px (>10).
 *
 * ponytail: width returns a CSS var() reference, not a hardcoded px value, so
 * the design token in bitable-tokens.css stays the single source of truth.
 * Ceiling: the field-count threshold (10) is hardcoded per KTD4 spec; if the
 * token name or threshold changes, update both here and the spec.
 */

import type { FieldType, IAttachmentMeta, IBitableField, IBitableRecord } from '@/api/bitable'

const DRAWER_FIELD_COUNT_THRESHOLD = 10

export interface IAttachmentFileView {
  name: string
  url: string
  size?: number
}

export interface IImageView {
  url: string
  alt?: string
}

function isEmptyValue(v: unknown): boolean {
  return v == null || v === '' || (Array.isArray(v) && v.length === 0)
}

/**
 * Format a field value for read-only display in the drawer.
 *
 * - text/number/formula/lookup: coerce to string, empty → '—'
 * - date: pass through (ISO string); the drawer may format further, but we
 *   return the raw stored value so callers can apply a consistent formatter.
 *   ponytail: no dayjs dependency here — the drawer renders the ISO string
 *   directly to avoid pulling a date lib into a pure helper. Upgrade path:
 *   if a shared date formatter exists, call it from the drawer component.
 * - select/multiselect: return label joined by ' / '; empty → '—'
 * - attachment/image: return `${n} 个文件` summary (the drawer renders the
 *   file list separately via getAttachmentFileList / getImageThumbnailList)
 */
export function formatFieldValue(
  value: unknown,
  fieldType: FieldType,
  options?: { labelOf?: (v: string) => string },
): string {
  if (isEmptyValue(value)) return '—'

  switch (fieldType) {
    case 'text':
    case 'number':
    case 'formula':
    case 'lookup':
      return String(value)
    case 'date':
      return typeof value === 'string' ? value : String(value)
    case 'select': {
      const v = value as string
      return options?.labelOf ? options.labelOf(v) : v
    }
    case 'multiselect': {
      const arr = value as string[]
      const labels = arr.map((v) => (options?.labelOf ? options.labelOf(v) : v))
      return labels.join(' / ')
    }
    case 'attachment':
    case 'image': {
      const arr = value as IAttachmentMeta[]
      return `${arr.length} 个文件`
    }
    default:
      return String(value)
  }
}

/**
 * Normalize an attachment field value into a uniform file view list.
 *
 * Accepts IAttachmentMeta[] (the canonical shape). Defensively tolerates
 * legacy / malformed entries (missing url → skipped, missing filename →
 * fallback to stored_name or '未命名文件').
 */
export function getAttachmentFileList(value: unknown): IAttachmentFileView[] {
  if (!Array.isArray(value)) return []
  const out: IAttachmentFileView[] = []
  for (const raw of value) {
    if (!raw || typeof raw !== 'object') continue
    const m = raw as Partial<IAttachmentMeta>
    if (!m.url) continue
    out.push({
      name: m.filename || m.stored_name || '未命名文件',
      url: m.url,
      size: typeof m.size === 'number' ? m.size : undefined,
    })
  }
  return out
}

/**
 * Normalize an image field value into a uniform thumbnail view list.
 *
 * Reuses getAttachmentFileList logic; image thumbnails use the same
 * IAttachmentMeta shape (url + filename). `alt` falls back to filename.
 */
export function getImageThumbnailList(value: unknown): IImageView[] {
  return getAttachmentFileList(value).map((f) => ({
    url: f.url,
    alt: f.name,
  }))
}

/**
 * Return the record's title — the value of the first text-type field, or
 * "未命名记录" if none exists / value is empty.
 */
export function getRecordTitle(record: IBitableRecord, fields: IBitableField[]): string {
  const firstText = fields.find((f) => f.field_type === 'text')
  if (!firstText) return '未命名记录'
  const v = record.values[firstText.id]
  if (isEmptyValue(v)) return '未命名记录'
  return String(v)
}

/**
 * A field is editable in the drawer iff:
 * - owner === 'user' (agent-owned columns are managed by the backend / agents)
 * - field_type is NOT formula (computed) or lookup (derived from another table)
 *
 * attachment/image are NOT inline-editable from the drawer in P0 (file upload
 * flow is out of scope for U3 — ponytail: ceiling noted, upgrade path = add
 * an upload control in a follow-up U-ID).
 */
export function isFieldEditable(field: IBitableField): boolean {
  if (field.owner !== 'user') return false
  if (field.field_type === 'formula' || field.field_type === 'lookup') return false
  if (field.field_type === 'attachment' || field.field_type === 'image') return false
  return true
}

/**
 * Compute the drawer width.
 *
 * @param fieldCount number of fields rendered in the body
 * @param isMobile   from useResponsiveBreakpoint — full-screen overlay when true
 * @returns a CSS value string. On desktop/tablet, returns a var() reference
 *          to --bitable-drawer-width (≤10 fields) or --bitable-drawer-width-wide
 *          (>10 fields). On mobile, returns '100vw'.
 */
export function getDrawerWidth(fieldCount: number, isMobile: boolean): string {
  if (isMobile) return '100vw'
  return fieldCount > DRAWER_FIELD_COUNT_THRESHOLD
    ? 'var(--bitable-drawer-width-wide)'
    : 'var(--bitable-drawer-width)'
}

// ponytail self-check: width threshold + var() reference. The two var() names
// must match bitable-tokens.css exactly. Trivial string logic, no test file.
