import type { KbxBulkSelectionRequest, KbxGridColumn, KbxGridPasteIssue, KbxGridPasteResult, KbxGridType, KbxSelectionState } from '@kbx/contracts'

function normalizeNumber(raw: unknown, integer = false) {
  const text = String(raw ?? '').replace(/,/g, '').trim()
  if (!text) return { value: null, changed: raw !== null && raw !== '' }
  const value = Number(text)
  if (!Number.isFinite(value) || (integer && !Number.isInteger(value))) return { invalid: true as const, value: raw, changed: false }
  return { value, changed: String(value) !== String(raw) }
}

function normalizeDate(raw: unknown) {
  const text = String(raw ?? '').trim()
  if (!text) return { value: '', changed: false }
  const compact = text.replace(/[./-]/g, '')
  if (!/^\d{8}$/.test(compact)) return { invalid: true as const, value: raw, changed: false }
  const y = Number(compact.slice(0, 4)), m = Number(compact.slice(4, 6)), d = Number(compact.slice(6, 8))
  const date = new Date(Date.UTC(y, m - 1, d))
  if (date.getUTCFullYear() !== y || date.getUTCMonth() !== m - 1 || date.getUTCDate() !== d) return { invalid: true as const, value: raw, changed: false }
  const value = `${compact.slice(0,4)}-${compact.slice(4,6)}-${compact.slice(6,8)}`
  return { value, changed: value !== text }
}

function normalizeBoolean(raw: unknown) {
  const text = String(raw ?? '').trim().toLowerCase()
  if (['true','1','y','yes','예','사용','o'].includes(text)) return { value: true, changed: raw !== true }
  if (['false','0','n','no','아니오','미사용','x'].includes(text)) return { value: false, changed: raw !== false }
  return { invalid: true as const, value: raw, changed: false }
}

export function normalizeKbxGridClipboardValue(type: KbxGridType | undefined, raw: unknown) {
  if (type === 'integer') return { ...normalizeNumber(raw, true), code: 'INVALID_NUMBER' as const }
  if (['decimal','quantity','money','percent'].includes(type ?? '')) return { ...normalizeNumber(raw), code: 'INVALID_NUMBER' as const }
  if (type === 'date') return { ...normalizeDate(raw), code: 'INVALID_DATE' as const }
  if (type === 'boolean') return { ...normalizeBoolean(raw), code: 'INVALID_BOOLEAN' as const }
  const value = typeof raw === 'string' ? raw.trim() : raw
  return { value, changed: value !== raw }
}

export function normalizeKbxGridClipboardData<T extends object>(
  data: unknown[][],
  columns: KbxGridColumn<T>[],
  startColumnIndex: number,
): { data: unknown[][]; result: KbxGridPasteResult } {
  const issues: KbxGridPasteIssue[] = []
  let normalizedCount = 0
  const normalized = data.map((row, rowOffset) => row.map((raw, columnOffset) => {
    const column = columns[startColumnIndex + columnOffset]
    if (!column) return raw
    const parsed = normalizeKbxGridClipboardValue(column.type, raw)
    if (parsed.invalid) issues.push({ rowOffset, columnOffset, field: column.field, code: parsed.code!, value: raw })
    if (parsed.changed) normalizedCount++
    return parsed.value
  }))
  const cellCount = normalized.reduce((sum, row) => sum + row.length, 0)
  return { data: normalized, result: { cellCount, normalizedCount, rejectedCount: issues.length, issues } }
}

export function toKbxBulkSelectionRequest<TFilter, TId = string>(selection: KbxSelectionState<TId>, filter: TFilter): KbxBulkSelectionRequest<TFilter, TId> {
  return selection.mode === 'all-filtered'
    ? { mode: 'filter', filter, excludedIds: selection.excludedIds ?? [] }
    : { mode: 'ids', ids: selection.selectedIds }
}
