import { test } from '@japa/runner'
import { DataDomain, PropertyValidation } from '../../../../src/index.js'

test.group('PropertyValidation', (group) => {
  let domain: DataDomain
  let validation: PropertyValidation

  group.each.setup(() => {
    domain = new DataDomain()
    validation = new PropertyValidation(domain)
  })

  test('validate() should return an error when the property does not exist', ({ assert }) => {
    const results = validation.validate('nonExistentProperty')
    assert.lengthOf(results, 1)
    assert.equal(results[0].rule, 'exists')
    assert.equal(results[0].severity, 'error')
  })

  test('validate() should call other validation methods', ({ assert, sinon }) => {
    const model = domain.addModel({ key: 'model' })
    const entity = model.addEntity({ key: 'entity', info: { name: 'Entity' } })
    const property = entity.addProperty({ key: 'property', type: 'string', info: { name: 'property' } })
    const validateNameSpy = sinon.spy(validation, 'validateName')

    validation.validate(property)

    assert.calledWith(validateNameSpy, property)
    sinon.restore()
  })

  test('validateName() should return an error when the property has no name', ({ assert }) => {
    const model = domain.addModel({ key: 'model' })
    const entity = model.addEntity({ key: 'entity', info: { name: 'Entity' } })
    const property = entity.addProperty({ key: 'property', type: 'string' })
    property.info.name = undefined
    const results = validation.validateName(property)
    assert.lengthOf(results, 1)
    assert.equal(results[0].rule, 'required')
    assert.equal(results[0].severity, 'error')
  })

  test('validateName() should return an error when the property name starts with a number', ({ assert }) => {
    const model = domain.addModel({ key: 'model' })
    const entity = model.addEntity({ key: 'entity', info: { name: 'Entity' } })
    const property = entity.addProperty({ key: '1property', type: 'string', info: { name: '1property' } })
    const results = validation.validateName(property)
    assert.lengthOf(results, 1)
    assert.equal(results[0].rule, 'format')
    assert.equal(results[0].severity, 'error')
  })

  test('validateName() should return an error when the property name contains invalid characters', ({ assert }) => {
    const model = domain.addModel({ key: 'model' })
    const entity = model.addEntity({ key: 'entity', info: { name: 'Entity' } })
    const property = entity.addProperty({
      key: 'invalid-property!',
      type: 'string',
      info: { name: 'invalid-property!' },
    })
    const results = validation.validateName(property)
    assert.lengthOf(results, 1)
    assert.equal(results[0].rule, 'format')
    assert.equal(results[0].severity, 'error')
  })

  test('validateName() should return an error when the property name is too short', ({ assert }) => {
    const model = domain.addModel({ key: 'model' })
    const entity = model.addEntity({ key: 'entity', info: { name: 'Entity' } })
    const property = entity.addProperty({ key: 'a', type: 'string', info: { name: 'a' } })
    const results = validation.validateName(property)
    assert.lengthOf(results, 1)
    assert.equal(results[0].rule, 'length')
    assert.equal(results[0].severity, 'error')
  })

  test('validateName() should return an error when the property name is too long', ({ assert }) => {
    const model = domain.addModel({ key: 'model' })
    const entity = model.addEntity({ key: 'entity', info: { name: 'Entity' } })
    const property = entity.addProperty({ key: 'a'.repeat(60), type: 'string', info: { name: 'a'.repeat(60) } })
    const results = validation.validateName(property)
    assert.lengthOf(results, 1)
    assert.equal(results[0].rule, 'length')
    assert.equal(results[0].severity, 'error')
  })

  test('validateName() should return no errors when the property name is valid', ({ assert }) => {
    const model = domain.addModel({ key: 'model' })
    const entity = model.addEntity({ key: 'entity', info: { name: 'Entity' } })
    const property = entity.addProperty({
      key: 'valid_property123',
      type: 'string',
      info: { name: 'valid_property123' },
    })
    const results = validation.validateName(property)
    assert.lengthOf(results, 0)
  })

  test('validateName() should return no errors when the property name starts with underscore', ({ assert }) => {
    const model = domain.addModel({ key: 'model' })
    const entity = model.addEntity({ key: 'entity', info: { name: 'Entity' } })
    const property = entity.addProperty({ key: '_property', type: 'string', info: { name: '_property' } })
    const results = validation.validateName(property)
    assert.lengthOf(results, 0)
  })

  test('validate() should return an error when the target property is not found (string)', ({ assert }) => {
    const model = domain.addModel({ key: 'model' })
    const entity = model.addEntity({ key: 'entity', info: { name: 'Entity' } })
    entity.addProperty({ key: 'existingProperty', type: 'string', info: { name: 'existing_property' } })
    const results = validation.validate('nonExistingProperty')
    assert.lengthOf(results, 1)
    assert.equal(results[0].rule, 'exists')
    assert.equal(results[0].severity, 'error')
    assert.equal(results[0].key, 'nonExistingProperty')
  })

  test('validate() should return validation errors for an invalid property name', ({ assert }) => {
    const model = domain.addModel({ key: 'model' })
    const entity = model.addEntity({ key: 'entity', info: { name: 'Entity' } })
    const property = entity.addProperty({ key: 'invalid-name', type: 'string', info: { name: 'invalid-name' } })
    const results = validation.validate(property)
    assert.lengthOf(results, 1)
    assert.equal(results[0].rule, 'format')
    assert.equal(results[0].severity, 'error')
  })

  test('validate() should return no errors for a valid property name', ({ assert }) => {
    const model = domain.addModel({ key: 'model' })
    const entity = model.addEntity({ key: 'entity', info: { name: 'Entity' } })
    const property = entity.addProperty({ key: 'valid_name', type: 'string', info: { name: 'valid_name' } })
    const results = validation.validate(property)
    assert.lengthOf(results, 0)
  })
})
