import { describe, expect, test } from 'vitest'
import { objectToHtml } from './object-to-html'
import { FORMATTERS } from './object-to-html.contants'
import type { Value, FormatterType } from './types'
import { formatterTypes } from './const'

describe('FORMATTERS', () => {
  test('should format currency values', () => {
    const formatter = FORMATTERS[formatterTypes.currency]
    const result = formatter.format(1234.56, 'en-US')
    expect(typeof result).toBe('string')
    expect(result.length).toBeGreaterThan(0)
  })

  test('should format number values', () => {
    const formatter = FORMATTERS[formatterTypes.number]
    const result = formatter.format(9876.54, 'en-US')
    expect(typeof result).toBe('string')
    expect(result.length).toBeGreaterThan(0)
  })

  test('should format default values with toLocaleString', () => {
    const formatter = FORMATTERS[formatterTypes.default]
    const mockValue = 10
    const result = formatter.format(mockValue, 'en-US')
    expect(result).toBe('10')
  })
})

describe('object-to-html', () => {
  test('should contain all values', () => {
    const properties = {
      name: 'I am a feature',
      price: 100,
      description: 'I am a description',
      quantity: 10,
      total: 200,
    }

    const html = objectToHtml({
      properties,
    })

    Object.values(properties).forEach((value) => {
      expect(html).toContain(value)
    })
  })

  test('should contain column names', () => {
    const properties = {
      name: 'I am a feature',
      price: 100,
      description: 'I am a description',
      quantity: 10,
      total: 200,
    }

    const html = objectToHtml({
      properties,
      showColumnName: true,
    })

    Object.keys(properties).forEach((value) => {
      expect(html).toContain(value)
    })
  })

  test('should contain only included columns', () => {
    const properties = {
      name: 'I am a feature',
      price: 100,
      description: 'I am a description',
      quantity: 10,
      total: 200,
    }

    const includeColumns = ['name', 'price', 'description']
    const html = objectToHtml({
      properties,
      includeColumns, // default value is '*'
      showColumnName: true,
    })

    includeColumns.forEach((value) => {
      expect(html).toContain(value)
    })
  })

  test('should contain the title', () => {
    const properties = {}
    const title = 'Title'

    expect(
      objectToHtml({
        title,
        properties,
      }),
    ).toContain(title)
  })

  test('should format numbers', () => {
    const properties = {
      total: 123456789,
      partial: 1234,
    }

    const html = objectToHtml({
      properties,
    })

    expect(html).toContain('123,456,789')
    expect(html).toContain('1,234')
  })

  test('should allow using currency formatter', () => {
    const properties = {
      price: 1234,
    }
    const html = objectToHtml({
      properties,
      formatters: {
        price: 'CURRENCY',
      },
    })
    expect(html).toContain('$1.23K')
  })

  test('should allow using custom formatters', () => {
    const properties = {
      id: 1,
      name: 'I am a feature',
      price: 100,
      description: 'I am a description',
      quantity: 10,
    }

    const html = objectToHtml({
      properties,
      formatters: {
        price: 'CURRENCY',
        id: (value: Value) => {
          return `ID: ${value}`
        },
        description: (value: Value) => {
          return `<p>${value}</p>`
        },
      },
    })

    expect(html).toContain('ID: 1')
    expect(html).toContain('$100')
    expect(html).toContain('<p>I am a description</p>')
  })

  test('should allow using custom getColumnName function', () => {
    const properties = {
      id: 1,
      name: 'I am a feature',
      price: 100,
      description: 'I am a description',
      quantity: 10,
    }

    const html = objectToHtml({
      properties,
      getColumnName: (name: string) => {
        return `Column: ${name} - `
      },
    })

    expect(html).toContain('Column: id - 1')
  })

  test('should allow using custom getFieldHtml function', () => {
    const properties = {
      id: 1,
      name: 'I am a feature',
      price: 100,
      description: 'I am a description',
      quantity: 10,
    }

    const html = objectToHtml({
      properties,
      title: 'Title',
      getFieldHtml: (column, value) => {
        return `<p>${column}${value}</p>`
      },
    })

    expect(html).toContain('<p><strong>id</strong>: 1')
  })

  test('should allow using custom getTitle function', () => {
    const properties = {
      id: 1,
      name: 'I am a feature',
      price: 100,
      description: 'I am a description',
      quantity: 10,
    }

    const html = objectToHtml({
      properties,
      title: 'Title',
      getTitle: (title: string) => {
        return `Title: ${title} - `
      },
    })

    expect(html).toContain('Title: Title - ')
  })

  test('should allow using custom getValue function', () => {
    const properties = {
      id: 1,
      name: 'I am a feature',
      price: 100,
      description: 'I am a description',
      quantity: 10,
    }

    const html = objectToHtml({
      properties,
      title: 'Title',
      getValue: (value, locale, formatter) => {
        const format = (
          (formatter && FORMATTERS[formatter as FormatterType]) ||
          FORMATTERS[formatterTypes.default]
        ).format
        return `Value: ${format(value, locale)}`
      },
    })

    expect(html).toContain('<strong>price</strong>: Value: 100</br>')
  })

  test('should handle formatter errors gracefully with catch block', () => {
    const properties = {
      id: 1,
      value: 'test',
    }

    // Create a formatter that throws an error
    const badFormatter = () => {
      throw new Error('Formatter error')
    }

    const html = objectToHtml({
      properties,
      formatters: {
        value: badFormatter as never,
      },
    })

    // When formatter throws, it should return the original value
    expect(html).toContain('test')
  })
})
