import { formatterTypes } from './const'
import { FORMATTERS } from './object-to-html.contants'
import type { FieldFormatter, ObjectToHtmlOptions, Value } from './types'

/**
 * Returns a string with the HTML content for an object.
 *
 * @param {ObjectToHtmlOptions} - The options of the formatter.
 *
 * @return {string}
 *
 * @defaultValues
 *
 * | Prop | Value |
 * | --- | --- |
 * | `formatters` | `{}` |
 * | `includeColumns` | `Object.keys(properties)` |
 * | `locale` | `en-US` |
 * | `showColumnName` | `true` |
 * | `title` | `''` |
 * | `getColumnName` | `Function` |
 * | `getFieldHtml` | `Function` |
 * | `getTitle` | `Function` |
 * | `getValue` | `Function` |
 *
 * @example
 * const properties = {
 *  name: 'I am a feature',
 *  price: 100,
 * }
 *
 * const html = objectToHtml({
 *   properties,
 *   formatters: {
 *    price: 'CURRENCY',
 *   },
 *   locale: 'es-ES',
 * })
 */
export function objectToHtml<T extends Record<string, Value>>({
  properties,
  formatters = {},
  includeColumns = Object.keys(properties),
  locale = 'en-US',
  showColumnName = true,
  title,
  getColumnName = _getColumnName,
  getFieldHtml = _getFieldHtml,
  getTitle = _getTitle,
  getValue = _getValue,
}: ObjectToHtmlOptions<T>): string {
  const header = title ? getTitle(title) : ''

  const propNames = includeColumns

  const body = propNames.reduce((acc, cur) => {
    const formatter = formatters[cur]!
    const value = properties[cur]!

    const valueTransformed = getValue(value, locale, formatter)
    const columnName = showColumnName ? getColumnName(cur) : ''

    return acc + getFieldHtml(columnName, valueTransformed)
  }, '')

  return header + body
}

function _getTitle(title: string) {
  return `<h3 style="margin: 0"><strong>${title}</strong></h3></br>`
}

function _getFieldHtml(columnName: string, value: Value) {
  const propHtml = `${columnName}${value}</br>`

  return propHtml
}

function _getValue(value: Value, locale: string, formatter?: FieldFormatter) {
  try {
    const format = getFormatterFunction(formatter)
    return format(value, locale)
  } catch {
    return value as string
  }
}

function getFormatterFunction(formatter?: FieldFormatter) {
  if (typeof formatter === 'function') {
    return formatter
  }

  const fn =
    (formatter && FORMATTERS[formatter]) ?? FORMATTERS[formatterTypes.default]

  return fn.format
}

function _getColumnName(propName: string) {
  return `<strong>${propName}</strong>: `
}
