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

test.group('DataDomain.registerForeignDomain()', () => {
  test('registers a foreign domain', ({ assert }) => {
    const dataDomain = new DataDomain()
    const foreignDomain = new DataDomain()
    foreignDomain.info.version = '1.0.0'
    dataDomain.registerForeignDomain(foreignDomain)
    assert.equal(dataDomain.dependencies.size, 1)
    assert.equal(dataDomain.dependencies.get(foreignDomain.key), foreignDomain)
    assert.equal(dataDomain.dependencyList.length, 1)
    assert.deepEqual(dataDomain.dependencyList[0], { key: foreignDomain.key, version: '1.0.0' })
  }).tags(['@modeling', '@datadomain', '@foreign', '@register'])

  test('throws an error when registering a domain with the same key', ({ assert }) => {
    const dataDomain = new DataDomain()
    const foreignDomain1 = new DataDomain()
    foreignDomain1.info.version = '1.0.0'
    dataDomain.registerForeignDomain(foreignDomain1)
    assert.throws(
      () => dataDomain.registerForeignDomain(foreignDomain1),
      `Foreign domain with key ${foreignDomain1.key} already registered`
    )
  }).tags(['@modeling', '@datadomain', '@foreign', '@register'])

  test('throws an error when registering a domain without a version', ({ assert }) => {
    const dataDomain = new DataDomain()
    const foreignDomain = new DataDomain()
    assert.throws(
      () => dataDomain.registerForeignDomain(foreignDomain),
      `Foreign domain ${foreignDomain.key} does not have a version set`
    )
  }).tags(['@modeling', '@datadomain', '@foreign', '@register'])

  test('copies nodes from the foreign domain to the local graph', ({ assert }) => {
    const dataDomain = new DataDomain()
    const foreignDomain = new DataDomain()
    const model = foreignDomain.addModel({ key: 'test-model' })
    const entity = foreignDomain.addEntity(model.key, { key: 'test-entity' })
    const property = foreignDomain.addProperty(entity.key, { key: 'test-property' })
    foreignDomain.info.version = '1.0.0'
    dataDomain.registerForeignDomain(foreignDomain)

    assert.isTrue(dataDomain.graph.hasNode(`${foreignDomain.key}:${model.key}`))
    assert.isTrue(dataDomain.graph.hasNode(`${foreignDomain.key}:${entity.key}`))
    assert.isTrue(dataDomain.graph.hasNode(`${foreignDomain.key}:${property.key}`))
    assert.deepEqual(dataDomain.graph.node(`${foreignDomain.key}:${model.key}`), model)
    assert.deepEqual(dataDomain.graph.node(`${foreignDomain.key}:${entity.key}`), entity)
    assert.deepEqual(dataDomain.graph.node(`${foreignDomain.key}:${property.key}`), property)
  }).tags(['@modeling', '@datadomain', '@foreign', '@register'])

  test('copies edges from the foreign domain to the local graph', ({ assert }) => {
    const dataDomain = new DataDomain()
    const foreignDomain = new DataDomain()
    const model = foreignDomain.addModel({ key: 'test-model' })
    const entity = foreignDomain.addEntity(model.key, { key: 'test-entity' })
    const property = foreignDomain.addProperty(entity.key, { key: 'test-property' })
    foreignDomain.info.version = '1.0.0'
    dataDomain.registerForeignDomain(foreignDomain)

    assert.isTrue(
      dataDomain.graph.hasEdge(`${foreignDomain.key}:${entity.key}`, `${foreignDomain.key}:${property.key}`)
    )
    assert.deepEqual(
      dataDomain.graph.edge(`${foreignDomain.key}:${entity.key}`, `${foreignDomain.key}:${property.key}`),
      { type: 'property', domain: foreignDomain.key, foreign: true }
    )
  }).tags(['@modeling', '@datadomain', '@foreign', '@register'])

  test('copies parent-child relationships from the foreign domain to the local graph', ({ assert }) => {
    const dataDomain = new DataDomain()
    const foreignDomain = new DataDomain()
    const model = foreignDomain.addModel({ key: 'test-model' })
    const entity = foreignDomain.addEntity(model.key, { key: 'test-entity' })
    const childEntity = foreignDomain.addEntity(model.key, { key: 'child-entity' })
    childEntity.addParent(entity.key)
    foreignDomain.info.version = '1.0.0'
    dataDomain.registerForeignDomain(foreignDomain)

    assert.isTrue(
      dataDomain.graph.hasEdge(`${foreignDomain.key}:${childEntity.key}`, `${foreignDomain.key}:${entity.key}`)
    )
    assert.isTrue(dataDomain.graph.hasParent(`${foreignDomain.key}:${entity.key}`, `${foreignDomain.key}:${model.key}`))
  }).tags(['@modeling', '@datadomain', '@foreign', '@register'])

  test('copies multiple parent-child relationships from the foreign domain to the local graph', ({ assert }) => {
    const dataDomain = new DataDomain()
    const foreignDomain = new DataDomain()
    const model = foreignDomain.addModel({ key: 'test-model' })
    const entity1 = foreignDomain.addEntity(model.key, { key: 'entity1' })
    const entity2 = foreignDomain.addEntity(model.key, { key: 'entity2' })
    const childEntity = foreignDomain.addEntity(model.key, { key: 'child-entity' })
    childEntity.addParent(entity1.key)
    childEntity.addParent(entity2.key)
    foreignDomain.info.version = '1.0.0'
    dataDomain.registerForeignDomain(foreignDomain)

    assert.isTrue(
      dataDomain.graph.hasEdge(`${foreignDomain.key}:${childEntity.key}`, `${foreignDomain.key}:${entity1.key}`)
    )
    assert.isTrue(
      dataDomain.graph.hasEdge(`${foreignDomain.key}:${childEntity.key}`, `${foreignDomain.key}:${entity2.key}`)
    )
    assert.isTrue(
      dataDomain.graph.hasParent(`${foreignDomain.key}:${entity1.key}`, `${foreignDomain.key}:${model.key}`)
    )
    assert.isTrue(
      dataDomain.graph.hasParent(`${foreignDomain.key}:${entity2.key}`, `${foreignDomain.key}:${model.key}`)
    )
    // assert.isTrue(dataDomain.graph.hasParent(`${foreignDomain.key}:${model.key}`))
  }).tags(['@modeling', '@datadomain', '@foreign', '@register'])

  test('copies association edges from the foreign domain to the local graph', ({ assert }) => {
    const dataDomain = new DataDomain()
    const foreignDomain = new DataDomain()
    const model = foreignDomain.addModel({ key: 'test-model' })
    const entity1 = foreignDomain.addEntity(model.key, { key: 'entity1' })
    const entity2 = foreignDomain.addEntity(model.key, { key: 'entity2' })
    const association = entity1.addAssociation({ key: entity2.key })
    foreignDomain.info.version = '1.0.0'
    dataDomain.registerForeignDomain(foreignDomain)

    assert.isTrue(
      dataDomain.graph.hasEdge(`${foreignDomain.key}:${entity1.key}`, `${foreignDomain.key}:${association.key}`)
    )
    assert.isTrue(
      dataDomain.graph.hasEdge(`${foreignDomain.key}:${association.key}`, `${foreignDomain.key}:${entity2.key}`)
    )
  }).tags(['@modeling', '@datadomain', '@foreign', '@register'])

  test('copies foreign domain entities to the root of the graph', ({ assert }) => {
    const dataDomain = new DataDomain()
    const foreignDomain = new DataDomain()
    const model = foreignDomain.addModel({ key: 'test-model' })
    const entity = foreignDomain.addEntity(model.key, { key: 'test-entity' })
    foreignDomain.info.version = '1.0.0'
    dataDomain.registerForeignDomain(foreignDomain)

    assert.isTrue(dataDomain.graph.hasNode(`${foreignDomain.key}:${entity.key}`))
    assert.isTrue(dataDomain.graph.hasParent(`${foreignDomain.key}:${entity.key}`, `${foreignDomain.key}:${model.key}`))
  }).tags(['@modeling', '@datadomain', '@foreign', '@register'])
})

test.group('DataDomain.findNamespace()', () => {
  test('finds a foreign namespace by its key and domain key', ({ assert }) => {
    const domain = new DataDomain()
    const fd = new DataDomain()
    const ns = fd.addNamespace({ key: 'test-namespace' })
    fd.info.version = '1.0.0'
    domain.registerForeignDomain(fd)
    const result = domain.findNamespace('test-namespace', fd.key)
    assert.deepEqual(result, ns)
  }).tags(['@modeling', '@datadomain', '@foreign', '@find', '@namespace'])

  test('returns undefined when namespace not found', ({ assert }) => {
    const domain = new DataDomain()
    const fd = new DataDomain()
    fd.info.version = '1.0.0'
    domain.registerForeignDomain(fd)
    const result = domain.findNamespace('other-namespace', fd.key)
    assert.isUndefined(result)
  }).tags(['@modeling', '@datadomain', '@foreign', '@find', '@namespace'])

  test("throws when namespace's namespace not found", ({ assert }) => {
    const domain = new DataDomain()
    domain.addNamespace({ key: 'test-namespace' })
    assert.throws(() => {
      domain.findNamespace('test-namespace', 'non-existent-domain')
    }, `Foreign domain non-existent-domain not found`)
  }).tags(['@modeling', '@datadomain', '@foreign', '@find', '@namespace'])
})

test.group('DataDomain.findModel()', () => {
  test('finds a foreign model by its key and domain key', ({ assert }) => {
    const domain = new DataDomain()
    const fd = new DataDomain()
    const ns = fd.addNamespace({ key: 'test-namespace' })
    const model = ns.addModel({ key: 'test-model' })
    fd.info.version = '1.0.0'
    domain.registerForeignDomain(fd)
    const result = domain.findModel('test-model', fd.key)
    assert.deepEqual(result, model)
  }).tags(['@modeling', '@datadomain', '@foreign', '@find', '@model'])

  test('returns undefined when model not found', ({ assert }) => {
    const domain = new DataDomain()
    const fd = new DataDomain()
    fd.info.version = '1.0.0'
    domain.registerForeignDomain(fd)
    const result = domain.findModel('other-model', fd.key)
    assert.isUndefined(result)
  }).tags(['@modeling', '@datadomain', '@foreign', '@find', '@model'])

  test("throws when model's namespace not found", ({ assert }) => {
    const domain = new DataDomain()
    domain.addNamespace({ key: 'test-namespace' })
    assert.throws(() => {
      domain.findModel('test-model', 'non-existent-domain')
    }, `Foreign domain non-existent-domain not found`)
  }).tags(['@modeling', '@datadomain', '@foreign', '@find', '@model'])
})

test.group('DataDomain.findEntity()', () => {
  test('finds a foreign entity by its key and domain key', ({ assert }) => {
    const dataDomain = new DataDomain()
    const foreignDomain = new DataDomain()
    const model = foreignDomain.addModel({ key: 'test-model' })
    const entity = foreignDomain.addEntity(model.key, { key: 'test-entity' })
    foreignDomain.info.version = '1.0.0'
    dataDomain.registerForeignDomain(foreignDomain)
    const foundEntity = dataDomain.findEntity('test-entity', foreignDomain.key)
    assert.deepEqual(foundEntity, entity)
  }).tags(['@modeling', '@datadomain', '@foreign', '@find', '@entity'])

  test('throws when the foreign domain does not exist', ({ assert }) => {
    const dataDomain = new DataDomain()
    assert.throws(
      () => dataDomain.findEntity('test-entity', 'non-existent-domain'),
      `Foreign domain non-existent-domain not found`
    )
  }).tags(['@modeling', '@datadomain', '@foreign', '@find', '@entity'])

  test('returns undefined if the entity does not exist in the foreign domain', ({ assert }) => {
    const dataDomain = new DataDomain()
    const foreignDomain = new DataDomain()
    foreignDomain.info.version = '1.0.0'
    dataDomain.registerForeignDomain(foreignDomain)
    const foundEntity = dataDomain.findEntity('non-existent-entity', foreignDomain.key)
    assert.isUndefined(foundEntity)
  }).tags(['@modeling', '@datadomain', '@foreign', '@find', '@entity'])

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

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

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

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

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

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

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

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

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

  test('returns undefined if domainKey is undefined', ({ assert }) => {
    const dataDomain = new DataDomain()
    const foreignDomain = new DataDomain()
    foreignDomain.info.version = '1.0.0'
    const model = foreignDomain.addModel({ key: 'test-model' })
    const entity = foreignDomain.addEntity(model.key, { key: 'test-entity' })
    dataDomain.registerForeignDomain(foreignDomain)
    const foundEntity = dataDomain.findEntity(entity.key, undefined)
    assert.isUndefined(foundEntity)
  }).tags(['@modeling', '@datadomain', '@foreign', '@find', '@entity'])

  test('returns undefined if domainKey is null', ({ assert }) => {
    const dataDomain = new DataDomain()
    const foreignDomain = new DataDomain()
    foreignDomain.info.version = '1.0.0'
    const model = foreignDomain.addModel({ key: 'test-model' })
    const entity = foreignDomain.addEntity(model.key, { key: 'test-entity' })
    dataDomain.registerForeignDomain(foreignDomain)
    // @ts-expect-error For testing purposes
    const foundEntity = dataDomain.findEntity(entity.key, null)
    assert.isUndefined(foundEntity)
  }).tags(['@modeling', '@datadomain', '@foreign', '@find', '@entity'])

  test('returns undefined if domainKey is an empty string', ({ assert }) => {
    const dataDomain = new DataDomain()
    const foreignDomain = new DataDomain()
    foreignDomain.info.version = '1.0.0'
    const model = foreignDomain.addModel({ key: 'test-model' })
    const entity = foreignDomain.addEntity(model.key, { key: 'test-entity' })
    dataDomain.registerForeignDomain(foreignDomain)
    const foundEntity = dataDomain.findEntity(entity.key, '')
    assert.isUndefined(foundEntity)
  }).tags(['@modeling', '@datadomain', '@foreign', '@find', '@entity'])

  test('throws when domainKey is a number', ({ assert }) => {
    const dataDomain = new DataDomain()
    const foreignDomain = new DataDomain()
    foreignDomain.info.version = '1.0.0'
    const model = foreignDomain.addModel({ key: 'test-model' })
    const entity = foreignDomain.addEntity(model.key, { key: 'test-entity' })
    dataDomain.registerForeignDomain(foreignDomain)
    assert.throws(
      // @ts-expect-error For testing purposes
      () => dataDomain.findEntity(entity.key, 123),
      `Invalid namespace key. Expected a string, got number`
    )
  }).tags(['@modeling', '@datadomain', '@foreign', '@find', '@entity'])

  test('throws when domainKey is a boolean', ({ assert }) => {
    const dataDomain = new DataDomain()
    const foreignDomain = new DataDomain()
    foreignDomain.info.version = '1.0.0'
    const model = foreignDomain.addModel({ key: 'test-model' })
    const entity = foreignDomain.addEntity(model.key, { key: 'test-entity' })
    dataDomain.registerForeignDomain(foreignDomain)
    assert.throws(
      // @ts-expect-error For testing purposes
      () => dataDomain.findEntity(entity.key, true),
      `Invalid namespace key. Expected a string, got boolean`
    )
  }).tags(['@modeling', '@datadomain', '@foreign', '@find', '@entity'])

  test('throws when domainKey is an object', ({ assert }) => {
    const dataDomain = new DataDomain()
    const foreignDomain = new DataDomain()
    foreignDomain.info.version = '1.0.0'
    const model = foreignDomain.addModel({ key: 'test-model' })
    const entity = foreignDomain.addEntity(model.key, { key: 'test-entity' })
    dataDomain.registerForeignDomain(foreignDomain)
    assert.throws(
      // @ts-expect-error For testing purposes
      () => dataDomain.findEntity(entity.key, {}),
      `Invalid namespace key. Expected a string, got object`
    )
  }).tags(['@modeling', '@datadomain', '@foreign', '@find', '@entity'])

  test('throws when domainKey is an array', ({ assert }) => {
    const dataDomain = new DataDomain()
    const foreignDomain = new DataDomain()
    foreignDomain.info.version = '1.0.0'
    const model = foreignDomain.addModel({ key: 'test-model' })
    const entity = foreignDomain.addEntity(model.key, { key: 'test-entity' })
    dataDomain.registerForeignDomain(foreignDomain)
    assert.throws(
      // @ts-expect-error For testing purposes
      () => dataDomain.findEntity(entity.key, []),
      `Invalid namespace key. Expected a string, got object`
    )
  }).tags(['@modeling', '@datadomain', '@foreign', '@find', '@entity'])

  test('throws when domainKey is a function', ({ assert }) => {
    const dataDomain = new DataDomain()
    const foreignDomain = new DataDomain()
    foreignDomain.info.version = '1.0.0'
    const model = foreignDomain.addModel({ key: 'test-model' })
    const entity = foreignDomain.addEntity(model.key, { key: 'test-entity' })
    dataDomain.registerForeignDomain(foreignDomain)
    assert.throws(
      // @ts-expect-error For testing purposes
      () => dataDomain.findEntity(entity.key, () => {}),
      `Invalid namespace key. Expected a string, got function`
    )
  }).tags(['@modeling', '@datadomain', '@foreign', '@find', '@entity'])
})

test.group('DataDomain.listForeignNamespaces()', () => {
  test('lists all namespaces from a registered foreign domain', ({ assert }) => {
    const localDomain = new DataDomain()
    const foreignDomain = new DataDomain({ key: 'foreignKey' })
    foreignDomain.info.version = '1.0.0'

    const ns1Foreign = foreignDomain.addNamespace({ key: 'ns1' })
    const ns2Foreign = foreignDomain.addNamespace({ key: 'ns2' })

    localDomain.registerForeignDomain(foreignDomain)

    const namespaces = [...localDomain.listForeignNamespaces(foreignDomain.key)]
    assert.lengthOf(namespaces, 2)
    assert.deepInclude(namespaces, ns1Foreign)
    assert.deepInclude(namespaces, ns2Foreign)
  }).tags(['@modeling', '@datadomain', '@foreign', '@list', '@namespace'])

  test('returns an empty generator if the foreign domain has no namespaces', ({ assert }) => {
    const localDomain = new DataDomain()
    const foreignDomain = new DataDomain({ key: 'foreignKey' })
    foreignDomain.info.version = '1.0.0'
    localDomain.registerForeignDomain(foreignDomain)

    const namespaces = [...localDomain.listForeignNamespaces(foreignDomain.key)]
    assert.lengthOf(namespaces, 0)
  }).tags(['@modeling', '@datadomain', '@foreign', '@list', '@namespace'])

  test('returns an empty generator if foreign namespaces are not in the local graph', ({ assert }) => {
    const localDomain = new DataDomain()
    const foreignDomain = new DataDomain({ key: 'foreignKey' })
    foreignDomain.info.version = '1.0.0'
    foreignDomain.addNamespace({ key: 'ns1' })
    localDomain.registerForeignDomain(foreignDomain)

    // Simulate that the foreign namespaces are not in the local graph
    localDomain.graph.removeNode(`${foreignDomain.key}:ns1`)

    const namespacesAfterRemoval = [...localDomain.listForeignNamespaces(foreignDomain.key)]
    assert.lengthOf(namespacesAfterRemoval, 0)
  }).tags(['@modeling', '@datadomain', '@foreign', '@list', '@namespace'])

  test('throws an error if the foreign domain key is not registered', ({ assert }) => {
    const localDomain = new DataDomain()
    assert.throws(
      () => [...localDomain.listForeignNamespaces('nonExistentKey')],
      'Foreign domain nonExistentKey not found'
    )
  }).tags(['@modeling', '@datadomain', '@foreign', '@list', '@namespace'])
})

test.group('DataDomain.listForeignModels()', () => {
  test('lists all models from a registered foreign domain', ({ assert }) => {
    const localDomain = new DataDomain()
    const foreignDomain = new DataDomain({ key: 'foreignKey' })
    foreignDomain.info.version = '1.0.0'

    const model1Foreign = foreignDomain.addModel({ key: 'model1' })
    const model2Foreign = foreignDomain.addModel({ key: 'model2' })

    localDomain.registerForeignDomain(foreignDomain)

    // Ensure the foreign nodes are in the local graph (simulating mergeGraph)
    localDomain.graph.setNode(`${foreignDomain.key}:${model1Foreign.key}`, model1Foreign)
    localDomain.graph.setNode(`${foreignDomain.key}:${model2Foreign.key}`, model2Foreign)

    const models = [...localDomain.listForeignModels(foreignDomain.key)]
    assert.lengthOf(models, 2)
    assert.deepInclude(models, model1Foreign)
    assert.deepInclude(models, model2Foreign)
  }).tags(['@modeling', '@datadomain', '@foreign', '@list', '@model'])

  test('returns an empty generator if the foreign domain has no models', ({ assert }) => {
    const localDomain = new DataDomain()
    const foreignDomain = new DataDomain({ key: 'foreignKey' })
    foreignDomain.info.version = '1.0.0'
    localDomain.registerForeignDomain(foreignDomain)

    const models = [...localDomain.listForeignModels(foreignDomain.key)]
    assert.lengthOf(models, 0)
  }).tags(['@modeling', '@datadomain', '@foreign', '@list', '@model'])

  test('returns an empty generator if foreign models are not in the local graph', ({ assert }) => {
    const localDomain = new DataDomain()
    const foreignDomain = new DataDomain({ key: 'foreignKey' })
    foreignDomain.info.version = '1.0.0'
    foreignDomain.addModel({ key: 'model1' })
    localDomain.registerForeignDomain(foreignDomain)

    // Simulate that the foreign models are not in the local graph
    localDomain.graph.removeNode(`${foreignDomain.key}:model1`)

    const modelsAfterRemoval = [...localDomain.listForeignModels(foreignDomain.key)]
    assert.lengthOf(modelsAfterRemoval, 0)
  }).tags(['@modeling', '@datadomain', '@foreign', '@list', '@model'])

  test('throws an error if the foreign domain key is not registered', ({ assert }) => {
    const localDomain = new DataDomain()
    assert.throws(() => [...localDomain.listForeignModels('nonExistentKey')], 'Foreign domain nonExistentKey not found')
  }).tags(['@modeling', '@datadomain', '@foreign', '@list', '@model'])
})

test.group('DataDomain.listForeignEntities()', () => {
  test('lists all entities from a registered foreign domain', ({ assert }) => {
    const localDomain = new DataDomain()
    const foreignDomain = new DataDomain({ key: 'foreignKey' })
    foreignDomain.info.version = '1.0.0'

    const modelForeign = foreignDomain.addModel({ key: 'modelF' })
    const entity1Foreign = foreignDomain.addEntity(modelForeign.key, { key: 'entity1' })
    const entity2Foreign = foreignDomain.addEntity(modelForeign.key, { key: 'entity2' })

    localDomain.registerForeignDomain(foreignDomain)

    // Ensure the foreign nodes are in the local graph (simulating mergeGraph)
    localDomain.graph.setNode(`${foreignDomain.key}:${entity1Foreign.key}`, entity1Foreign)
    localDomain.graph.setNode(`${foreignDomain.key}:${entity2Foreign.key}`, entity2Foreign)

    const entities = [...localDomain.listForeignEntities(foreignDomain.key)]
    assert.lengthOf(entities, 2)
    assert.deepInclude(entities, entity1Foreign)
    assert.deepInclude(entities, entity2Foreign)
  }).tags(['@modeling', '@datadomain', '@foreign', '@list', '@entity'])

  test('returns an empty generator if the foreign domain has no entities', ({ assert }) => {
    const localDomain = new DataDomain()
    const foreignDomain = new DataDomain({ key: 'foreignKey' })
    foreignDomain.info.version = '1.0.0'
    localDomain.registerForeignDomain(foreignDomain)

    const entities = [...localDomain.listForeignEntities(foreignDomain.key)]
    assert.lengthOf(entities, 0)
  }).tags(['@modeling', '@datadomain', '@foreign', '@list', '@entity'])

  test('returns an empty generator if foreign entities are not in the local graph', ({ assert }) => {
    const localDomain = new DataDomain()
    const foreignDomain = new DataDomain({ key: 'foreignKey' })
    foreignDomain.info.version = '1.0.0'
    const modelForeign = foreignDomain.addModel({ key: 'modelF' })
    foreignDomain.addEntity(modelForeign.key, { key: 'entity1' })
    localDomain.registerForeignDomain(foreignDomain)

    // Simulate that the foreign entities are not in the local graph
    localDomain.graph.removeNode(`${foreignDomain.key}:entity1`)

    const entitiesAfterRemoval = [...localDomain.listForeignEntities(foreignDomain.key)]
    assert.lengthOf(entitiesAfterRemoval, 0)
  }).tags(['@modeling', '@datadomain', '@foreign', '@list', '@entity'])

  test('throws an error if the foreign domain key is not registered', ({ assert }) => {
    const localDomain = new DataDomain()
    assert.throws(
      () => [...localDomain.listForeignEntities('nonExistentKey')],
      'Foreign domain nonExistentKey not found'
    )
  }).tags(['@modeling', '@datadomain', '@foreign', '@list', '@entity'])

  test('lists entities from a foreign domain even if local domain has entities', ({ assert }) => {
    const localDomain = new DataDomain()
    const modelLocal = localDomain.addModel({ key: 'modelL' })
    localDomain.addEntity(modelLocal.key, { key: 'entityL' })

    const foreignDomain = new DataDomain({ key: 'foreignKey' })
    foreignDomain.info.version = '1.0.0'
    const modelForeign = foreignDomain.addModel({ key: 'modelF' })
    const entityForeign = foreignDomain.addEntity(modelForeign.key, { key: 'entityF' })
    localDomain.registerForeignDomain(foreignDomain)

    const entities = [...localDomain.listForeignEntities(foreignDomain.key)]
    assert.lengthOf(entities, 1)
    assert.deepInclude(entities, entityForeign)
    assert.notDeepInclude(entities, localDomain.findEntity('entityL') as DomainEntity)
  }).tags(['@modeling', '@datadomain', '@foreign', '@list', '@entity'])
})

test.group('DataDomain.findAssociation()', () => {
  test('finds a foreign association by its key and domain key', ({ assert }) => {
    const domain = new DataDomain()
    const fd = new DataDomain()
    const m1 = fd.addModel()
    const e1 = m1.addEntity()
    const e2 = m1.addEntity()
    const a1 = e1.addAssociation({ key: e2.key })
    fd.info.version = '1.0.0'
    domain.registerForeignDomain(fd)
    const result = domain.findAssociation(a1.key, fd.key)
    assert.deepEqual(result, a1)
  }).tags(['@modeling', '@datadomain', '@foreign', '@find', '@association'])

  test('returns undefined when association not found', ({ assert }) => {
    const domain = new DataDomain()
    const fd = new DataDomain()
    fd.info.version = '1.0.0'
    domain.registerForeignDomain(fd)
    const result = domain.findAssociation('other-association', fd.key)
    assert.isUndefined(result)
  }).tags(['@modeling', '@datadomain', '@foreign', '@find', '@association'])

  test("throws when association's namespace not found", ({ assert }) => {
    const domain = new DataDomain()
    domain.addNamespace({ key: 'test-namespace' })
    assert.throws(() => {
      domain.findAssociation('test-association', 'non-existent-domain')
    }, `Foreign domain non-existent-domain not found`)
  }).tags(['@modeling', '@datadomain', '@foreign', '@find', '@association'])
})

test.group('DataDomain.findProperty()', () => {
  test('finds a foreign property by its key and domain key', ({ assert }) => {
    const domain = new DataDomain()
    const fd = new DataDomain()
    const m1 = fd.addModel()
    const e1 = m1.addEntity()
    const p1 = e1.addProperty()
    fd.info.version = '1.0.0'
    domain.registerForeignDomain(fd)
    const result = domain.findProperty(p1.key, fd.key)
    assert.deepEqual(result, p1)
  }).tags(['@modeling', '@datadomain', '@foreign', '@find', '@property'])

  test('returns undefined when property not found', ({ assert }) => {
    const domain = new DataDomain()
    const fd = new DataDomain()
    fd.info.version = '1.0.0'
    domain.registerForeignDomain(fd)
    const result = domain.findProperty('other-property', fd.key)
    assert.isUndefined(result)
  }).tags(['@modeling', '@datadomain', '@foreign', '@find', '@property'])

  test("throws when property's namespace not found", ({ assert }) => {
    const domain = new DataDomain()
    domain.addNamespace({ key: 'test-namespace' })
    assert.throws(() => {
      domain.findProperty('test-property', 'non-existent-domain')
    }, `Foreign domain non-existent-domain not found`)
  }).tags(['@modeling', '@datadomain', '@foreign', '@find', '@property'])
})

test.group('DataDomain foreign elements namespace property', () => {
  test('DomainNamespace retains its original namespace key after foreign registration', ({ assert }) => {
    const localDomain = new DataDomain({ key: 'localKey' })
    const foreignDomain = new DataDomain({ key: 'foreignKey' })
    foreignDomain.addNamespace({ key: 'nsInForeign' })
    foreignDomain.info.version = '1.0.0'
    localDomain.registerForeignDomain(foreignDomain)

    const retrievedNs = localDomain.findNamespace('nsInForeign', 'foreignKey')
    assert.ok(retrievedNs, 'Foreign namespace should be found in local domain')
    assert.equal(retrievedNs!.namespace, 'foreignKey', 'Namespace property should point to foreign domain key')
    assert.equal(retrievedNs!.domain.key, 'foreignKey', 'Domain property should point to local domain key')
  }).tags(['@modeling', '@datadomain', '@foreign', '@namespace_property', '@check'])

  test('DomainModel retains its original namespace key after foreign registration', ({ assert }) => {
    const localDomain = new DataDomain({ key: 'localKey' })
    const foreignDomain = new DataDomain({ key: 'foreignKey' })
    foreignDomain.info.version = '1.0.0'

    foreignDomain.addModel({ key: 'modelInForeign' })
    localDomain.registerForeignDomain(foreignDomain)

    const retrievedModel = localDomain.findModel('modelInForeign', 'foreignKey')
    assert.ok(retrievedModel, 'Foreign model should be found in local domain')
    assert.equal(retrievedModel!.namespace, 'foreignKey', 'Namespace property should point to foreign domain key')
    assert.equal(retrievedModel!.domain.key, 'foreignKey', 'Domain property should point to local domain key')
  }).tags(['@modeling', '@datadomain', '@foreign', '@namespace_property', '@check'])

  test('DomainEntity retains its original namespace key after foreign registration', ({ assert }) => {
    const localDomain = new DataDomain({ key: 'localKey' })
    const foreignDomain = new DataDomain({ key: 'foreignKey' })
    foreignDomain.info.version = '1.0.0'

    const parentForeignModel = foreignDomain.addModel({ key: 'parentModelInForeign' })
    foreignDomain.addEntity(parentForeignModel.key, { key: 'entityInForeign' })
    localDomain.registerForeignDomain(foreignDomain)

    const retrievedEntity = localDomain.findEntity('entityInForeign', 'foreignKey')
    assert.ok(retrievedEntity, 'Foreign entity should be found in local domain')
    assert.equal(retrievedEntity!.namespace, 'foreignKey', 'Namespace property should point to foreign domain key')
    assert.equal(retrievedEntity!.domain.key, 'foreignKey', 'Domain property should point to local domain key')
  }).tags(['@modeling', '@datadomain', '@foreign', '@namespace_property', '@check'])

  test('DomainProperty retains its original namespace key after foreign registration', ({ assert }) => {
    const localDomain = new DataDomain({ key: 'localKey' })
    const foreignDomain = new DataDomain({ key: 'foreignKey' })
    foreignDomain.info.version = '1.0.0'

    const parentForeignModel = foreignDomain.addModel({ key: 'parentModelInForeign' })
    const parentForeignEntity = foreignDomain.addEntity(parentForeignModel.key, { key: 'parentEntityInForeign' })
    foreignDomain.addProperty(parentForeignEntity.key, { key: 'propertyInForeign' })
    localDomain.registerForeignDomain(foreignDomain)

    const retrievedProperty = localDomain.findProperty('propertyInForeign', 'foreignKey')
    assert.ok(retrievedProperty, 'Foreign property should be found in local domain')
    assert.equal(retrievedProperty!.namespace, 'foreignKey', 'Namespace property should point to foreign domain key')
    assert.equal(retrievedProperty!.domain.key, 'foreignKey', 'Domain property should point to local domain key')
  }).tags(['@modeling', '@datadomain', '@foreign', '@namespace_property', '@check'])

  test('DomainElement.namespace is the key of the domain it was created in', ({ assert }) => {
    const domain = new DataDomain({ key: 'originalDomainKey' })
    const model = domain.addModel({ key: 'myModel' })
    const entity = model.addEntity({ key: 'myEntity' })

    assert.equal(model.namespace, 'originalDomainKey')
    assert.equal(entity.namespace, 'originalDomainKey')
  }).tags(['@modeling', '@datadomain', '@foreign', '@namespace_property', '@check'])
})
