import { test } from '@japa/runner'
import { ThingKind } from '../../../src/models/kinds.js'
import { Thing, IThing } from '../../../src/models/Thing.js'

test.group('Models > Thing > Thing.fromName()', () => {
  test('sets the kind', ({ assert }) => {
    const result = Thing.fromName('a name')
    assert.equal(result.kind, ThingKind)
  })

  test('sets the name', ({ assert }) => {
    const result = Thing.fromName('a name')
    assert.equal(result.name, 'a name')
  })

  test('ignores other properties', ({ assert }) => {
    const result = Thing.fromName('a name')
    assert.isUndefined(result.description)
    assert.isUndefined(result.version)
  })
})

test.group('Models > Thing > constructor()', () => {
  test('creates an empty Thing', ({ assert }) => {
    const result = new Thing()
    assert.equal(result.kind, ThingKind)
    assert.isUndefined(result.name)
    assert.isUndefined(result.description)
    assert.isUndefined(result.version)
  })

  test('creates a Thing from the schema values', ({ assert }) => {
    const schema: IThing = {
      kind: ThingKind,
      name: 'a name',
      displayName: 'a displayName',
      description: 'a desc',
      version: 'a ver',
    }
    const result = new Thing(schema)
    assert.equal(result.kind, ThingKind)
    assert.equal(result.name, 'a name')
    assert.equal(result.displayName, 'a displayName')
    assert.equal(result.description, 'a desc')
    assert.equal(result.version, 'a ver')
  })

  test('creates a Thing from the JSON schema string', ({ assert }) => {
    const schema: IThing = {
      kind: ThingKind,
      name: 'a name',
      displayName: 'a displayName',
      description: 'a desc',
      version: 'a ver',
    }
    const result = new Thing(JSON.stringify(schema))
    assert.equal(result.kind, ThingKind)
    assert.equal(result.name, 'a name')
    assert.equal(result.displayName, 'a displayName')
    assert.equal(result.description, 'a desc')
    assert.equal(result.version, 'a ver')
  })

  test('throws when invalid schema', ({ assert }) => {
    assert.throws(() => {
      new Thing(
        JSON.stringify({
          name: 'a name',
        })
      )
    })
  })
})

test.group('Models > Thing > toJSON()', (group) => {
  let thing: Thing
  group.each.setup(() => {
    thing = new Thing()
  })

  test('serializes the kind', ({ assert }) => {
    const result = thing.toJSON()
    assert.equal(result.kind, ThingKind)
  })

  test('serializes the name', ({ assert }) => {
    thing.name = 'a name'
    const result = thing.toJSON()
    assert.equal(result.name, 'a name')
  })

  test('does not serialize name when missing', ({ assert }) => {
    const result = thing.toJSON()
    assert.isUndefined(result.name)
  })

  test('serializes the displayName', ({ assert }) => {
    thing.displayName = 'a displayName'
    const result = thing.toJSON()
    assert.equal(result.displayName, 'a displayName')
  })

  test('does not serialize displayName when missing', ({ assert }) => {
    const result = thing.toJSON()
    assert.isUndefined(result.displayName)
  })

  test('serializes the description', ({ assert }) => {
    thing.description = 'a description'
    const result = thing.toJSON()
    assert.equal(result.description, 'a description')
  })

  test('does not serialize description when missing', ({ assert }) => {
    const result = thing.toJSON()
    assert.isUndefined(result.description)
  })

  test('serializes the version', ({ assert }) => {
    thing.version = 'a version'
    const result = thing.toJSON()
    assert.equal(result.version, 'a version')
  })

  test('does not serialize version when missing', ({ assert }) => {
    const result = thing.toJSON()
    assert.isUndefined(result.version)
  })
})

test.group('Models > Thing > getLabel()', () => {
  test('returns the default value', ({ assert }) => {
    const t = new Thing()
    assert.equal(t.getLabel(), 'Unnamed object')
  })

  test('returns the passed default value', ({ assert }) => {
    const t = new Thing()
    assert.equal(t.getLabel('xyz'), 'xyz')
  })

  test('returns the name value', ({ assert }) => {
    const t = new Thing()
    t.name = 'a thing'
    assert.equal(t.getLabel(), 'a thing')
  })

  test('returns the displayName when set', ({ assert }) => {
    const t = new Thing()
    t.name = 'a thing'
    t.displayName = 'better thing'
    assert.equal(t.getLabel(), 'better thing')
  })
})
