import { test } from '@japa/runner'
import { ProjectSchema, IProjectSchema, Kind as ProjectSchemaKind } from '../../../src/models/ProjectSchema.js'
import { Property, IProperty, Kind as PropertyKind } from '../../../src/models/Property.js'

test.group('ProjectSchema#fromName()', () => {
  test('creates the schema from the name', ({ assert }) => {
    const result = ProjectSchema.fromName('test')
    assert.equal(result.kind, ProjectSchemaKind)
    assert.equal(result.name, 'test')
    assert.typeOf(result.key, 'string')
    assert.isUndefined(result.properties)
    assert.isUndefined(result.content)
    assert.isUndefined(result.mime)
  })
})

test.group('ProjectSchema#fromContent()', () => {
  test('creates the schema from the content', ({ assert }) => {
    const result = ProjectSchema.fromContent('test name', 'test-content', 'application/json')
    assert.equal(result.kind, ProjectSchemaKind)
    assert.equal(result.name, 'test name')
    assert.typeOf(result.key, 'string')
    assert.isUndefined(result.properties)
    assert.equal(result.content, 'test-content')
    assert.equal(result.mime, 'application/json')
  })
})

test.group('ProjectSchema.constructor()', () => {
  test('creates a default schema', ({ assert }) => {
    const result = new ProjectSchema()
    assert.equal(result.kind, ProjectSchemaKind)
    assert.equal(result.name, '')
    assert.typeOf(result.key, 'string')
    assert.isUndefined(result.properties)
    assert.isUndefined(result.content)
    assert.isUndefined(result.mime)
  })

  test('creates an instance from a schema', ({ assert }) => {
    const info: IProjectSchema = {
      kind: ProjectSchemaKind,
      name: 'test-info',
      content: 'test-content',
      mime: 'application/json',
      key: '123',
      properties: [Property.Boolean('p1').toJSON()],
    }
    const result = new ProjectSchema(info)
    assert.equal(result.kind, ProjectSchemaKind)
    assert.equal(result.name, 'test-info')
    assert.equal(result.key, '123')
    assert.equal(result.content, 'test-content')
    assert.equal(result.mime, 'application/json')
    assert.typeOf(result.properties, 'array')
    assert.lengthOf(result.properties!, 1)
    assert.equal(result.properties![0].name, 'p1')
    assert.equal(result.properties![0].type, 'boolean')
  })

  test('creates an instance from a JSON string', ({ assert }) => {
    const info: IProjectSchema = {
      kind: ProjectSchemaKind,
      name: 'test-info',
      key: '123',
    }
    const result = new ProjectSchema(JSON.stringify(info))
    assert.equal(result.kind, ProjectSchemaKind)
    assert.equal(result.name, 'test-info')
    assert.equal(result.key, '123')
  })

  test('creates the key when missing', ({ assert }) => {
    const info: IProjectSchema = {
      kind: ProjectSchemaKind,
      name: 'test-info',
      key: '123',
    }
    // @ts-expect-error Used in testing
    delete info.key
    const result = new ProjectSchema(info)
    assert.typeOf(result.key, 'string')
  })
})

test.group('ProjectSchema.toJSON()', () => {
  const base: IProjectSchema = {
    kind: ProjectSchemaKind,
    name: 'test-info',
    content: 'test-content',
    mime: 'application/json',
    key: '123',
    properties: [Property.Boolean('p1').toJSON()],
  }

  test('serializes the kind', ({ assert }) => {
    const instance = new ProjectSchema({ ...base })
    const result = instance.toJSON()
    assert.equal(result.kind, ProjectSchemaKind)
  })

  test('serializes the key', ({ assert }) => {
    const instance = new ProjectSchema({ ...base })
    const result = instance.toJSON()
    assert.equal(result.key, '123')
  })

  test('creates the key when missing', ({ assert }) => {
    const instance = new ProjectSchema({ ...base })
    // @ts-expect-error Used in testing
    instance.key = undefined
    const result = instance.toJSON()
    assert.typeOf(result.key, 'string')
  })

  test('serializes the name', ({ assert }) => {
    const instance = new ProjectSchema({ ...base })
    const result = instance.toJSON()
    assert.equal(result.name, 'test-info')
  })

  test('serializes the default name when missing', ({ assert }) => {
    const instance = new ProjectSchema({ ...base })
    // @ts-expect-error Used in testing
    instance.name = undefined
    const result = instance.toJSON()
    assert.equal(result.name, '')
  })

  test('serializes the content', ({ assert }) => {
    const instance = new ProjectSchema({ ...base })
    const result = instance.toJSON()
    assert.equal(result.content, 'test-content')
  })

  test('serializes the mime', ({ assert }) => {
    const instance = new ProjectSchema({ ...base })
    const result = instance.toJSON()
    assert.equal(result.mime, 'application/json')
  })

  test('serializes the properties', ({ assert }) => {
    const instance = new ProjectSchema({ ...base })
    const result = instance.toJSON()
    assert.typeOf(result.properties, 'array')
    assert.lengthOf(result.properties!, 1)
  })

  test('does not serialize the content when missing', ({ assert }) => {
    const instance = new ProjectSchema({ ...base, content: undefined })
    const result = instance.toJSON()
    assert.isUndefined(result.content)
  })

  test('does not serialize the mime when missing', ({ assert }) => {
    const instance = new ProjectSchema({ ...base, mime: undefined })
    const result = instance.toJSON()
    assert.isUndefined(result.mime)
  })

  test('does not serialize the properties when missing', ({ assert }) => {
    const instance = new ProjectSchema({ ...base, properties: undefined })
    const result = instance.toJSON()
    assert.isUndefined(result.properties)
  })
})

test.group('ProjectSchema.addProperty()', () => {
  test('adds property by name and type when no properties', ({ assert }) => {
    const instance = new ProjectSchema()
    instance.addProperty('a', 'boolean')
    assert.typeOf(instance.properties, 'array')
    assert.lengthOf(instance.properties!, 1)
    const [prop] = instance.properties!
    assert.equal(prop.name, 'a')
    assert.equal(prop.type, 'boolean')
  })

  test('adds property by name and type when existing properties', ({ assert }) => {
    const instance = new ProjectSchema()
    instance.addProperty('a', 'boolean')
    instance.addProperty('b', 'string')
    assert.lengthOf(instance.properties!, 2)
    const [, prop] = instance.properties!
    assert.equal(prop.name, 'b')
    assert.equal(prop.type, 'string')
  })

  test('returns the created property', ({ assert }) => {
    const instance = new ProjectSchema()
    const result = instance.addProperty('a', 'boolean')
    const [prop] = instance.properties!
    assert.deepEqual(prop, result)
  })

  test('throws when the type is not defined', ({ assert }) => {
    const instance = new ProjectSchema()
    assert.throws(() => {
      // @ts-expect-error Used in testing
      instance.addProperty('a')
    })
  })

  test('adds a property from the schema', ({ assert }) => {
    const instance = new ProjectSchema()
    const schema: IProperty = {
      kind: PropertyKind,
      name: 'a',
      type: 'string',
      value: '',
    }
    instance.addProperty(schema)
    assert.typeOf(instance.properties, 'array')
    assert.lengthOf(instance.properties!, 1)
    const [prop] = instance.properties!
    assert.equal(prop.name, 'a')
    assert.equal(prop.type, 'string')
  })

  test('adds the kind property when missing', ({ assert }) => {
    const instance = new ProjectSchema()
    const schema: IProperty = {
      kind: PropertyKind,
      name: 'a',
      type: 'string',
      value: '',
    }
    // @ts-expect-error Used in testing
    delete schema.kind
    instance.addProperty(schema)
    assert.typeOf(instance.properties, 'array')
    assert.lengthOf(instance.properties!, 1)
    const [prop] = instance.properties!
    assert.equal(prop.kind, PropertyKind)
  })
})
