import { test } from '@japa/runner'
import { DataDomain } from '../../../src/modeling/DataDomain.js'
import { DomainEntity } from '../../../src/modeling/DomainEntity.js'
import { SemanticType } from '../../../src/modeling/Semantics.js'

test.group('DataDomain.addEntity()', () => {
  test('addEntity adds an entity to the graph', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
    assert.instanceOf(entity, DomainEntity)
    assert.isTrue(dataDomain.graph.hasNode(entity.key))
  }).tags(['@modeling', '@entity', '@add'])

  test('addEntity adds an entity to the graph with a parent', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
    assert.instanceOf(entity, DomainEntity)
    assert.isTrue(dataDomain.graph.hasNode(entity.key))
    assert.equal(dataDomain.graph.parent(entity.key), model.key)
  }).tags(['@modeling', '@entity', '@add'])

  test('addEntity throws an error if parent model does not exist', ({ assert }) => {
    const dataDomain = new DataDomain()
    assert.throws(() => {
      dataDomain.addEntity('non-existent-parent', { key: 'test-entity' })
    }, 'The parent non-existent-parent does not exist')
  }).tags(['@modeling', '@entity', '@add'])

  test('addEntity throws an error if parent is not a model', ({ assert }) => {
    const dataDomain = new DataDomain()
    const namespace = dataDomain.addNamespace({ key: 'test-ns' })
    assert.throws(() => {
      dataDomain.addEntity(namespace.key, { key: 'test-entity' })
    }, `Parent model ${namespace.key} is not a valid model`)
  }).tags(['@modeling', '@entity', '@add'])

  test('addEntity adds an entity with default values', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = dataDomain.addEntity(model.key, {})
    assert.instanceOf(entity, DomainEntity)
    assert.isTrue(dataDomain.graph.hasNode(entity.key))
    assert.equal(dataDomain.graph.parent(entity.key), model.key)
  }).tags(['@modeling', '@entity', '@add'])

  test('addEntity notifies change', async ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    dataDomain.addEntity(model.key, { key: 'test-entity' })
    await assert.dispatches(dataDomain, 'change', { timeout: 20 })
  }).tags(['@modeling', '@entity', '@add'])

  test('addEntity adds an entity to a parent model - checks parent-child relationship', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentModel = dataDomain.addModel({ key: 'parent-model' })
    const entity = dataDomain.addEntity(parentModel.key, { key: 'test-entity' })
    assert.isTrue(dataDomain.graph.hasParent(entity.key, parentModel.key))
  }).tags(['@modeling', '@entity', '@add'])

  test('addEntity adds an entity to a parent model - checks items list', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentModel = dataDomain.addModel({ key: 'parent-model' })
    const entity = dataDomain.addEntity(parentModel.key, { key: 'test-entity' })
    assert.deepInclude([...parentModel!.listEntities()], entity)
  }).tags(['@modeling', '@entity', '@add'])

  test('addEntity throws an error if no parent is provided', ({ assert }) => {
    const dataDomain = new DataDomain()
    assert.throws(() => {
      // @ts-expect-error For testing purposes, we are passing undefined
      dataDomain.addEntity(undefined, { key: 'test-entity' })
    }, `An entity expects a DomainModel parent`)
  }).tags(['@modeling', '@entity', '@add'])

  test('addEntity throws when adding the same entity twice', ({ assert }) => {
    const root = new DataDomain()
    const m1 = root.addModel({ key: 'parent-model' })
    root.addEntity(m1.key, { key: 'test-entity' })
    assert.throws(() => {
      root.addEntity(m1.key, { key: 'test-entity' })
    }, `Entity with key test-entity already exists`)
  }).tags(['@modeling', '@entity', '@add'])
})

test.group('DataDomain.removeEntity()', () => {
  test('removeEntity removes an entity from the graph', ({ assert }) => {
    const dataDomain = new DataDomain()
    const entity = dataDomain.addModel({ key: 'test-model' }).addEntity({ key: 'test-entity' })
    dataDomain.removeEntity(entity.key)
    assert.isFalse(dataDomain.graph.hasNode(entity.key))
  }).tags(['@modeling', '@entity', '@remove'])

  test('removeEntity throws an error if entity does not exist', ({ assert }) => {
    const dataDomain = new DataDomain()
    assert.throws(() => {
      dataDomain.removeEntity('non-existent-entity')
    }, 'Entity non-existent-entity does not exist')
  }).tags(['@modeling', '@entity', '@remove'])

  test('removeEntity notifies change', async ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
    dataDomain.removeEntity(entity.key)
    await assert.dispatches(dataDomain, 'change', { timeout: 20 })
  }).tags(['@modeling', '@entity', '@remove'])

  test('removeEntity removes an entity from a parent model', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentModel = dataDomain.addModel({ key: 'parent-model' })
    const entity = dataDomain.addEntity(parentModel.key, { key: 'test-entity' })
    dataDomain.removeEntity(entity.key)
    assert.isFalse(dataDomain.graph.hasNode(entity.key))
    assert.equal(dataDomain.graph.parent(entity.key), undefined)
  }).tags(['@modeling', '@entity', '@remove'])

  test('removeEntity removes an entity with properties', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
    const property = dataDomain.addProperty(entity.key, { key: 'test-property' })
    dataDomain.removeEntity(entity.key)
    assert.isFalse(dataDomain.graph.hasNode(entity.key))
    assert.isFalse(dataDomain.graph.hasNode(property.key))
  }).tags(['@modeling', '@entity', '@remove'])

  test('removeEntity removes an entity from a parent model - checks parent-child relationship', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentModel = dataDomain.addModel({ key: 'parent-model' })
    const entity = dataDomain.addEntity(parentModel.key, { key: 'test-entity' })
    dataDomain.removeEntity(entity.key)
    assert.isFalse(dataDomain.graph.hasParent(entity.key, parentModel.key))
  }).tags(['@modeling', '@entity', '@remove'])

  test('removeEntity removes an entity from a parent model - checks items list', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentModel = dataDomain.addModel({ key: 'parent-model' })
    const entity = dataDomain.addEntity(parentModel.key, { key: 'test-entity' })
    dataDomain.removeEntity(entity.key)
    assert.notDeepInclude([...parentModel!.listEntities()], entity)
  }).tags(['@modeling', '@entity', '@remove'])

  test('removeEntity throws an error if key is not an entity', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    assert.throws(() => {
      dataDomain.removeEntity(model.key)
    }, `Parent model not found for entity ${model.key}`)
  }).tags(['@modeling', '@entity', '@remove'])
})

test.group('DataDomain.listEntities()', () => {
  test('lists all entities in the graph for a given parent model', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity1 = dataDomain.addEntity(model.key, { key: 'entity1' })
    const entity2 = dataDomain.addEntity(model.key, { key: 'entity2' })
    const entities = [...dataDomain.listEntities(model.key)]
    assert.lengthOf(entities, 2)
    assert.deepInclude(entities, entity1)
    assert.deepInclude(entities, entity2)
  }).tags(['@modeling', '@entity', '@list'])

  test('returns an empty array if there are no entities for a given parent model', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entities = [...dataDomain.listEntities(model.key)]
    assert.lengthOf(entities, 0)
  }).tags(['@modeling', '@entity', '@list'])

  test('returns only entities for a given parent model', ({ assert }) => {
    const root = new DataDomain()
    const m1 = root.addModel({ key: 'm1' })
    const m2 = root.addModel({ key: 'm2' })
    const e1 = root.addEntity(m1.key, { key: 'entity1' })
    root.addEntity(m2.key, { key: 'entity2' })
    root.addNamespace({ key: 'ns1' })
    const entities = [...root.listEntities(m1.key)]
    assert.lengthOf(entities, 1)
    assert.deepInclude(entities, e1)
  }).tags(['@modeling', '@entity', '@list'])

  test('entities in the list are of type DomainEntity', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    dataDomain.addEntity(model.key, { key: 'entity1' })
    const entities = [...dataDomain.listEntities(model.key)]
    assert.instanceOf(entities[0], DomainEntity)
  }).tags(['@modeling', '@entity', '@list'])

  test('lists entities when there are no entities', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entities = [...dataDomain.listEntities(model.key)]
    assert.deepEqual(entities, [])
  }).tags(['@modeling', '@entity', '@list'])

  test('lists entities when there are other domain elements', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity1 = dataDomain.addEntity(model.key, { key: 'entity1' })
    dataDomain.addNamespace({ key: 'ns1' })
    const entities = [...dataDomain.listEntities(model.key)]
    assert.deepEqual(entities, [entity1])
  }).tags(['@modeling', '@entity', '@list'])

  test('returns an empty array if the parent model does not exist', ({ assert }) => {
    const dataDomain = new DataDomain()
    const entities = [...dataDomain.listEntities('non-existent-model')]
    assert.deepEqual(entities, [])
  }).tags(['@modeling', '@entity', '@list'])

  test('returns an empty array if the parent is not a model', ({ assert }) => {
    const dataDomain = new DataDomain()
    const namespace = dataDomain.addNamespace({ key: 'test-ns' })
    const entities = [...dataDomain.listEntities(namespace.key)]
    assert.deepEqual(entities, [])
  }).tags(['@modeling', '@entity', '@list'])

  test('lists entities from a foreign domain', ({ assert }) => {
    const domain1 = new DataDomain()
    const m1 = domain1.addModel({ key: 'model1' })
    const e1 = domain1.addEntity(m1.key, { key: 'entity1' })

    const domain2 = new DataDomain()
    domain2.info.version = '1.0.0'
    const m2 = domain2.addModel({ key: 'model2' })
    domain2.addEntity(m2.key, { key: 'entity2' })
    domain1.registerForeignDomain(domain2)

    const children = [...domain1.listEntities(m1.key)]
    assert.lengthOf(children, 1)
    assert.deepInclude(children, e1)
  }).tags(['@modeling', '@entity', '@list'])
})

test.group('DataDomain.findEntity()', () => {
  test('finds an entity by its key', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
    const foundEntity = dataDomain.findEntity('test-entity')
    assert.deepEqual(foundEntity, entity)
  }).tags(['@modeling', '@entity', '@find'])

  test('returns undefined if entity does not exist', ({ assert }) => {
    const dataDomain = new DataDomain()
    const foundEntity = dataDomain.findEntity('non-existent-entity')
    assert.isUndefined(foundEntity)
  }).tags(['@modeling', '@entity', '@find'])

  test('returns undefined if key is not an entity', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const foundEntity = dataDomain.findEntity(model.key)
    assert.isUndefined(foundEntity)
  }).tags(['@modeling', '@entity', '@find'])

  test('returns undefined if key is undefined', ({ assert }) => {
    const dataDomain = new DataDomain()
    // @ts-expect-error For testing purposes
    const foundEntity = dataDomain.findEntity(undefined)
    assert.isUndefined(foundEntity)
  }).tags(['@modeling', '@entity', '@find'])

  test('returns undefined if key is null', ({ assert }) => {
    const dataDomain = new DataDomain()
    // @ts-expect-error For testing purposes
    const foundEntity = dataDomain.findEntity(null)
    assert.isUndefined(foundEntity)
  }).tags(['@modeling', '@entity', '@find'])

  test('returns undefined if key is an empty string', ({ assert }) => {
    const dataDomain = new DataDomain()
    const foundEntity = dataDomain.findEntity('')
    assert.isUndefined(foundEntity)
  }).tags(['@modeling', '@entity', '@find'])

  test('returns undefined if key is a number', ({ assert }) => {
    const dataDomain = new DataDomain()
    // @ts-expect-error For testing purposes
    const foundEntity = dataDomain.findEntity(123)
    assert.isUndefined(foundEntity)
  }).tags(['@modeling', '@entity', '@find'])

  test('returns undefined if key is a boolean', ({ assert }) => {
    const dataDomain = new DataDomain()
    // @ts-expect-error For testing purposes
    const foundEntity = dataDomain.findEntity(true)
    assert.isUndefined(foundEntity)
  }).tags(['@modeling', '@entity', '@find'])

  test('returns undefined if key is an object', ({ assert }) => {
    const dataDomain = new DataDomain()
    // @ts-expect-error For testing purposes
    const foundEntity = dataDomain.findEntity({})
    assert.isUndefined(foundEntity)
  }).tags(['@modeling', '@entity', '@find'])

  test('returns undefined if key is an array', ({ assert }) => {
    const dataDomain = new DataDomain()
    // @ts-expect-error For testing purposes
    const foundEntity = dataDomain.findEntity([])
    assert.isUndefined(foundEntity)
  }).tags(['@modeling', '@entity', '@find'])

  test('returns undefined if key is a function', ({ assert }) => {
    const dataDomain = new DataDomain()
    // @ts-expect-error For testing purposes
    const foundEntity = dataDomain.findEntity(() => {})
    assert.isUndefined(foundEntity)
  }).tags(['@modeling', '@entity', '@find'])

  test('finds an entity in a foreign domain', ({ assert }) => {
    const domain1 = new DataDomain()
    const m1 = domain1.addModel({ key: 'model1' })
    const e1 = domain1.addEntity(m1.key, { key: 'entity1' })

    const domain2 = new DataDomain()
    const m2 = domain2.addModel({ key: 'model2' })
    domain2.addEntity(m2.key, { key: 'entity2' })
    domain2.info.version = '1.0.0'
    domain1.registerForeignDomain(domain2)

    const foundEntity = domain1.findEntity('entity1')
    assert.deepEqual(foundEntity, e1)
  }).tags(['@modeling', '@entity', '@find'])
})

test.group('DomainEntity.addSemantic()', () => {
  test('adds a new semantic to the entity', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
    const semantic = { id: SemanticType.User }
    entity.addSemantic(semantic)
    assert.deepInclude(entity.semantics, semantic)
  }).tags(['@modeling', '@entity', '@add', '@semantic'])

  test('updates an existing semantic', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
    const semantic1 = { id: SemanticType.User, config: { value: 'old' } }
    const semantic2 = { id: SemanticType.User, config: { value: 'new' } }
    entity.addSemantic(semantic1)
    entity.addSemantic(semantic2)
    assert.lengthOf(entity.semantics, 1)
    assert.deepInclude(entity.semantics, semantic2)
  }).tags(['@modeling', '@entity', '@add', '@semantic'])

  test('throws an error if the semantic is not an entity semantic', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
    const semantic = { id: SemanticType.CreatedTimestamp } // CreatedTimestamp is a Property semantic
    assert.throws(() => {
      entity.addSemantic(semantic)
    }, `Invalid semantic type: ${SemanticType.CreatedTimestamp}. Expected an entity semantic.`)
  }).tags(['@modeling', '@entity', '@add', '@semantic'])

  test('notifies change when adding a new semantic', async ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
    const semantic = { id: SemanticType.User }
    entity.addSemantic(semantic)
    await assert.dispatches(dataDomain, 'change', { timeout: 20 })
  }).tags(['@modeling', '@entity', '@add', '@semantic'])

  test('notifies change when updating an existing semantic', async ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
    const semantic1 = { id: SemanticType.User, config: { value: 'old' } }
    const semantic2 = { id: SemanticType.User, config: { value: 'new' } }
    entity.addSemantic(semantic1)
    // Clear the event queue before the second add
    await new Promise((resolve) => setTimeout(resolve, 0))
    entity.addSemantic(semantic2)
    await assert.dispatches(dataDomain, 'change', { timeout: 20 })
  }).tags(['@modeling', '@entity', '@add', '@semantic'])
})

test.group('DomainEntity.removeSemantic()', () => {
  test('removes an existing semantic from the entity', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
    const semantic = { id: SemanticType.User }
    entity.addSemantic(semantic)
    assert.deepInclude(entity.semantics, semantic)
    entity.removeSemantic(SemanticType.User)
    assert.notDeepInclude(entity.semantics, semantic)
    assert.lengthOf(entity.semantics, 0)
  }).tags(['@modeling', '@entity', '@remove', '@semantic'])

  test('does nothing if the semantic to remove does not exist', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
    const semantic = { id: SemanticType.User }
    entity.addSemantic(semantic)
    const initialSemantics = [...entity.semantics]
    entity.removeSemantic('non-existent-semantic-id' as SemanticType)
    assert.deepEqual(entity.semantics, initialSemantics)
  }).tags(['@modeling', '@entity', '@remove', '@semantic'])

  test('notifies change when a semantic is removed', async ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
    const semantic = { id: SemanticType.User }
    entity.addSemantic(semantic)
    // Clear the event queue before removal
    await new Promise((resolve) => setTimeout(resolve, 0))
    entity.removeSemantic(SemanticType.User)
    await assert.dispatches(dataDomain, 'change', { timeout: 20 })
  }).tags(['@modeling', '@entity', '@remove', '@semantic'])

  test('does not notify change if the semantic to remove does not exist', async ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
    // Clear the event queue before removal
    await new Promise((resolve) => setTimeout(resolve, 0))
    entity.removeSemantic('non-existent-semantic-id' as SemanticType)
    await assert.notDispatches(dataDomain, 'change', { timeout: 20 })
  }).tags(['@modeling', '@entity', '@remove', '@semantic'])
})

test.group('DomainEntity.hasSemantic()', () => {
  test('returns true if the entity has the specified semantic', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
    const semantic = { id: SemanticType.User }
    entity.addSemantic(semantic)
    assert.isTrue(entity.hasSemantic(SemanticType.User))
  }).tags(['@modeling', '@entity', '@has', '@semantic'])

  test('returns false if the entity does not have the specified semantic', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
    assert.isFalse(entity.hasSemantic(SemanticType.User))
  }).tags(['@modeling', '@entity', '@has', '@semantic'])

  test('returns false for an empty semantics array', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
    assert.isFalse(entity.hasSemantic(SemanticType.User))
  }).tags(['@modeling', '@entity', '@has', '@semantic'])
})

test.group('DataDomain.moveEntity()', () => {
  test('moves an entity from one model to another', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model1 = dataDomain.addModel({ key: 'model1' })
    const model2 = dataDomain.addModel({ key: 'model2' })
    const entity = model1.addEntity({ key: 'entity1' })
    dataDomain.moveEntity(entity.key, model1.key, model2.key)
    assert.equal(dataDomain.graph.parent(entity.key), model2.key)
    assert.deepEqual(model1.fields, [])
    assert.deepEqual(model2.fields, [{ key: entity.key, type: 'entity' }])
  }).tags(['@modeling', '@entity', '@move'])

  test('throws an error if the entity does not exist', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model1 = dataDomain.addModel({ key: 'model1' })
    const model2 = dataDomain.addModel({ key: 'model2' })
    assert.throws(() => {
      dataDomain.moveEntity('non-existent-entity', model1.key, model2.key)
    }, 'Entity non-existent-entity does not exist')
  }).tags(['@modeling', '@entity', '@move'])

  test('throws an error if the source model does not exist', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model2 = dataDomain.addModel({ key: 'model2' })
    const entity = dataDomain.addEntity('model2', { key: 'entity1' })
    assert.throws(() => {
      dataDomain.moveEntity(entity.key, 'non-existent-model', model2.key)
    }, 'Source model non-existent-model does not exist')
  }).tags(['@modeling', '@entity', '@move'])

  test('throws an error if the target model does not exist', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model1 = dataDomain.addModel({ key: 'model1' })
    const entity = model1.addEntity({ key: 'entity1' })
    assert.throws(() => {
      dataDomain.moveEntity(entity.key, model1.key, 'non-existent-model')
    }, 'Target model non-existent-model does not exist')
  }).tags(['@modeling', '@entity', '@move'])

  test('throws an error if the entity is not in the same domain', ({ assert }) => {
    const dataDomain1 = new DataDomain()
    const model1 = dataDomain1.addModel({ key: 'model1' })
    const entity = model1.addEntity({ key: 'entity1' })

    const dataDomain2 = new DataDomain()
    const model2 = dataDomain2.addModel({ key: 'model2' })
    dataDomain1.info.version = '1.0.0'
    dataDomain2.registerForeignDomain(dataDomain1)

    assert.throws(() => {
      dataDomain2.moveEntity(`${dataDomain1.key}:${entity.key}`, `${dataDomain1.key}:${model1.key}`, model2.key)
    }, 'Cannot move an entity from a foreign domain')
  }).tags(['@modeling', '@entity', '@move'])

  test('throws an error if the target model is not in the same domain', ({ assert }) => {
    const dataDomain1 = new DataDomain()
    const model1 = dataDomain1.addModel({ key: 'model1' })
    const entity = model1.addEntity({ key: 'entity1' })

    const dataDomain2 = new DataDomain()
    const model2 = dataDomain2.addModel({ key: 'model2' })
    dataDomain2.info.version = '1.0.0'
    dataDomain1.registerForeignDomain(dataDomain2)

    assert.throws(() => {
      dataDomain1.moveEntity(entity.key, model1.key, `${dataDomain2.key}:${model2.key}`)
    }, 'Cannot move an entity to a foreign domain')
  }).tags(['@modeling', '@entity', '@move'])

  test('throws an error if the source and target models are the same', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model1 = dataDomain.addModel({ key: 'model1' })
    const entity = model1.addEntity({ key: 'entity1' })
    assert.throws(() => {
      dataDomain.moveEntity(entity.key, model1.key, model1.key)
    }, 'Cannot move an entity to the same model')
  }).tags(['@modeling', '@entity', '@move'])

  test('throws an error if the target is not a model', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model1 = dataDomain.addModel({ key: 'model1' })
    const namespace = dataDomain.addNamespace({ key: 'namespace1' })
    const entity = model1.addEntity({ key: 'entity1' })
    assert.throws(() => {
      dataDomain.moveEntity(entity.key, model1.key, namespace.key)
    }, 'Entity or models not found in the graph')
  }).tags(['@modeling', '@entity', '@move'])

  test('notifies change', async ({ assert }) => {
    const dataDomain = new DataDomain()
    const model1 = dataDomain.addModel({ key: 'model1' })
    const model2 = dataDomain.addModel({ key: 'model2' })
    const entity = model1.addEntity({ key: 'entity1' })
    dataDomain.moveEntity(entity.key, model1.key, model2.key)
    await assert.dispatches(dataDomain, 'change', { timeout: 20 })
  }).tags(['@modeling', '@entity', '@move'])

  test('moves an entity with properties', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model1 = dataDomain.addModel({ key: 'model1' })
    const model2 = dataDomain.addModel({ key: 'model2' })
    const entity = model1.addEntity({ key: 'entity1' })
    entity.addProperty({ key: 'test-property', type: 'string' })
    dataDomain.moveEntity(entity.key, model1.key, model2.key)
    assert.equal(dataDomain.graph.parent(entity.key), model2.key)
    assert.isTrue(dataDomain.graph.hasNode('test-property'))
  }).tags(['@modeling', '@entity', '@move'])

  test('moves an entity with associations', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model1 = dataDomain.addModel({ key: 'model1' })
    const model2 = dataDomain.addModel({ key: 'model2' })
    const entity = model1.addEntity({ key: 'entity1' })
    const assoc = entity.addAssociation()
    dataDomain.moveEntity(entity.key, model1.key, model2.key)
    assert.equal(dataDomain.graph.parent(entity.key), model2.key)
    assert.isTrue(dataDomain.graph.hasNode(assoc.key))
  }).tags(['@modeling', '@entity', '@move'])
})
