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

test.group('DataDomain.addNamespace()', () => {
  test('addNamespace adds a namespace to the graph', ({ assert }) => {
    const dataDomain = new DataDomain()
    const namespace = dataDomain.addNamespace({ key: 'test-ns' })
    assert.instanceOf(namespace, DomainNamespace)
    assert.isTrue(dataDomain.graph.hasNode(namespace.key))
  }).tags(['@modeling', '@namespace', '@add'])

  test('addNamespace adds a namespace to the graph with a parent', ({ assert }) => {
    const dataDomain = new DataDomain()
    const pNs = dataDomain.addNamespace({ key: 'parent-ns' })
    const cNs = dataDomain.addNamespace({ key: 'child-ns' }, pNs.key)
    assert.instanceOf(cNs, DomainNamespace)
    assert.isTrue(dataDomain.graph.hasNode(cNs.key))
    assert.equal(dataDomain.graph.parent(cNs.key), pNs.key)
  }).tags(['@modeling', '@namespace', '@add'])

  test('addNamespace throws an error if parent namespace does not exist', ({ assert }) => {
    const dataDomain = new DataDomain()
    assert.throws(() => {
      dataDomain.addNamespace({ key: 'child-ns' }, 'non-existent-parent')
    }, 'Parent namespace non-existent-parent does not exist')
  }).tags(['@modeling', '@namespace', '@add'])

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

  test('addNamespace adds a namespace to the root of the graph', ({ assert }) => {
    const dataDomain = new DataDomain()
    const namespace = dataDomain.addNamespace({ key: 'test-ns' })
    assert.instanceOf(namespace, DomainNamespace)
    assert.isTrue(dataDomain.graph.hasNode(namespace.key))
    assert.isUndefined(dataDomain.graph.parent(namespace.key))
  }).tags(['@modeling', '@namespace', '@add'])

  test('addNamespace adds a namespace to a parent namespace', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentNamespace = dataDomain.addNamespace({ key: 'parent-ns' })
    const childNamespace = dataDomain.addNamespace({ key: 'child-ns' }, parentNamespace.key)
    assert.instanceOf(childNamespace, DomainNamespace)
    assert.isTrue(dataDomain.graph.hasNode(childNamespace.key))
    assert.equal(dataDomain.graph.parent(childNamespace.key), parentNamespace.key)
  }).tags(['@modeling', '@namespace', '@add'])

  test('addNamespace adds a namespace with default values', ({ assert }) => {
    const dataDomain = new DataDomain()
    const namespace = dataDomain.addNamespace({})
    assert.instanceOf(namespace, DomainNamespace)
    assert.isTrue(dataDomain.graph.hasNode(namespace.key))
    assert.isUndefined(dataDomain.graph.parent(namespace.key))
  }).tags(['@modeling', '@namespace', '@add'])

  test('addNamespace adds to the fields list', ({ assert }) => {
    const dataDomain = new DataDomain()
    const namespace = dataDomain.addNamespace({})
    assert.lengthOf(dataDomain.fields, 1)
    const [field] = dataDomain.fields
    assert.equal(field.key, namespace.key)
    assert.equal(field.type, 'namespace')
  }).tags(['@modeling', '@namespace', '@add'])
})

test.group('DataDomain.removeNamespace()', () => {
  test('removes a namespace from the graph', ({ assert }) => {
    const root = new DataDomain()
    const namespace = root.addNamespace({ key: 'test-ns' })
    root.removeNamespace(namespace.key)
    assert.isFalse(root.graph.hasNode(namespace.key))
  }).tags(['@modeling', '@namespace', '@remove'])

  test('removes a nested namespace from the graph', ({ assert }) => {
    const root = new DataDomain()
    const n1 = root.addNamespace({ key: 'parent-ns' })
    const n2 = root.addNamespace({ key: 'child-ns' }, n1.key)
    root.removeNamespace(n2.key)
    assert.isFalse(root.graph.hasNode(n2.key))
    assert.isTrue(root.graph.hasNode(n1.key))
  }).tags(['@modeling', '@namespace', '@remove'])

  test('removeNamespace throws an error if namespace does not exist', ({ assert }) => {
    const root = new DataDomain()
    assert.throws(() => {
      root.removeNamespace('non-existent-ns')
    }, 'Namespace non-existent-ns does not exist')
  }).tags(['@modeling', '@namespace', '@remove'])

  test('removeNamespace notifies change', async ({ assert }) => {
    const root = new DataDomain()
    const n1 = root.addNamespace({ key: 'test-ns' })
    root.removeNamespace(n1.key)
    await assert.dispatches(root, 'change', { timeout: 20 })
  }).tags(['@modeling', '@namespace', '@remove'])

  test('removeNamespace removes a namespace from the root of the graph', ({ assert }) => {
    const root = new DataDomain()
    const n1 = root.addNamespace({ key: 'test-ns' })
    root.removeNamespace(n1.key)
    assert.isFalse(root.graph.hasNode(n1.key))
    assert.isUndefined(root.listGraphNamespaces().next().value)
  }).tags(['@modeling', '@namespace', '@remove'])

  test('removeNamespace removes a namespace from a parent namespace', ({ assert }) => {
    const root = new DataDomain()
    const n1 = root.addNamespace({ key: 'parent-ns' })
    const n2 = root.addNamespace({ key: 'child-ns' }, n1.key)
    root.removeNamespace(n2.key)
    assert.isFalse(root.graph.hasNode(n2.key))
    assert.equal(root.graph.parent(n2.key), undefined)
  }).tags(['@modeling', '@namespace', '@remove'])

  test('removeNamespace removes a namespace with children', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentNamespace = dataDomain.addNamespace({ key: 'parent-ns' })
    const childNamespace = dataDomain.addNamespace({ key: 'child-ns' }, parentNamespace.key)
    const grandChildNamespace = dataDomain.addNamespace({ key: 'grandchild-ns' }, childNamespace.key)
    dataDomain.removeNamespace(parentNamespace.key)
    assert.isFalse(dataDomain.graph.hasNode(parentNamespace.key))
    assert.isFalse(dataDomain.graph.hasNode(childNamespace.key))
    assert.isFalse(dataDomain.graph.hasNode(grandChildNamespace.key))
  }).tags(['@modeling', '@namespace', '@remove'])

  test('removeNamespace removes a namespace with models', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentNamespace = dataDomain.addNamespace({ key: 'parent-ns' })
    const model = dataDomain.addModel({ key: 'test-model' }, parentNamespace.key)
    dataDomain.removeNamespace(parentNamespace.key)
    assert.isFalse(dataDomain.graph.hasNode(parentNamespace.key))
    assert.isFalse(dataDomain.graph.hasNode(model.key))
  }).tags(['@modeling', '@namespace', '@remove'])

  test('removeNamespace removes the whole domain structure', ({ assert }) => {
    const d1 = new DataDomain()
    const ns1 = d1.addNamespace({ key: 'ns1' })
    const ns2 = ns1.addNamespace({ key: 'ns2' })
    const m1 = ns2.addModel({ key: 'model1' })
    const e1 = m1.addEntity({ key: 'entity1' })
    const e2 = m1.addEntity({ key: 'entity2' })
    const p1 = e1.addProperty({ key: 'property1' })
    const a1 = e1.addAssociation({ key: e2.key })
    d1.removeNamespace(ns1.key)
    assert.isFalse(d1.graph.hasNode(ns1.key))
    assert.isFalse(d1.graph.hasNode(ns2.key))
    assert.isFalse(d1.graph.hasNode(m1.key))
    assert.isFalse(d1.graph.hasNode(e1.key))
    assert.isFalse(d1.graph.hasNode(e2.key))
    assert.isFalse(d1.graph.hasNode(p1.key))
    assert.isFalse(d1.graph.hasNode(a1.key))
    assert.deepEqual([...d1.graph.edges()], [])
    assert.deepEqual([...d1.graph.nodes()], [])
  }).tags(['@modeling', '@namespace', '@remove'])

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

  test('removeNamespace removes from fields', ({ assert }) => {
    const root = new DataDomain()
    const n1 = root.addNamespace()
    root.removeNamespace(n1.key)
    assert.deepEqual(root.fields, [])
  }).tags(['@modeling', '@namespace', '@remove'])
})

test.group('DataDomain.listNamespaces()', () => {
  test('lists all namespaces in the data domain', ({ assert }) => {
    const dataDomain = new DataDomain()
    const namespace1 = dataDomain.addNamespace()
    const namespace2 = dataDomain.addNamespace()
    const namespaces = [...dataDomain.listNamespaces()]
    assert.lengthOf(namespaces, 2)
    assert.deepInclude(namespaces, namespace1)
    assert.deepInclude(namespaces, namespace2)
  }).tags(['@modeling', '@namespace', '@list'])

  test('returns an empty array if there are no namespaces', ({ assert }) => {
    const dataDomain = new DataDomain()
    const namespaces = [...dataDomain.listNamespaces()]
    assert.lengthOf(namespaces, 0)
  }).tags(['@modeling', '@namespace', '@list'])

  test('returns only namespaces', ({ assert }) => {
    const dataDomain = new DataDomain()
    const namespace1 = dataDomain.addNamespace()
    dataDomain.addModel()
    const namespaces = [...dataDomain.listNamespaces()]
    assert.lengthOf(namespaces, 1)
    assert.deepInclude(namespaces, namespace1)
  }).tags(['@modeling', '@namespace', '@list'])

  test('namespaces in the list are of type DomainNamespace', ({ assert }) => {
    const dataDomain = new DataDomain()
    dataDomain.addNamespace()
    const namespaces = [...dataDomain.listNamespaces()]
    assert.instanceOf(namespaces[0], DomainNamespace)
  }).tags(['@modeling', '@namespace', '@list'])

  test('lists namespaces in the order they were added', ({ assert }) => {
    const dataDomain = new DataDomain()
    const namespace1 = dataDomain.addNamespace()
    const namespace2 = dataDomain.addNamespace()
    const namespaces = [...dataDomain.listNamespaces()]
    assert.deepEqual(namespaces, [namespace1, namespace2])
  }).tags(['@modeling', '@namespace', '@list'])

  test('lists namespaces when there are no namespaces', ({ assert }) => {
    const dataDomain = new DataDomain()
    const namespaces = [...dataDomain.listNamespaces()]
    assert.deepEqual(namespaces, [])
  }).tags(['@modeling', '@namespace', '@list'])

  test('lists namespaces when there are other domain elements', ({ assert }) => {
    const dataDomain = new DataDomain()
    const namespace1 = dataDomain.addNamespace()
    dataDomain.addModel()
    const namespaces = [...dataDomain.listNamespaces()]
    assert.deepEqual(namespaces, [namespace1])
  }).tags(['@modeling', '@namespace', '@list'])
})

test.group('DataDomain.hasNamespaces()', () => {
  test('returns true if there are child namespaces', ({ assert }) => {
    const dataDomain = new DataDomain()
    dataDomain.addNamespace()
    assert.isTrue(dataDomain.hasNamespaces())
  }).tags(['@modeling', '@namespace', '@has'])

  test('returns false if there are no child namespaces', ({ assert }) => {
    const dataDomain = new DataDomain()
    assert.isFalse(dataDomain.hasNamespaces())
  }).tags(['@modeling', '@namespace', '@has'])

  test('returns false if there are only models', ({ assert }) => {
    const dataDomain = new DataDomain()
    dataDomain.addModel()
    assert.isFalse(dataDomain.hasNamespaces())
  }).tags(['@modeling', '@namespace', '@has'])

  test('returns true if there are both models and namespaces', ({ assert }) => {
    const dataDomain = new DataDomain()
    dataDomain.addNamespace()
    dataDomain.addModel()
    assert.isTrue(dataDomain.hasNamespaces())
  }).tags(['@modeling', '@namespace', '@has'])

  test('returns false if the data domain has no fields', ({ assert }) => {
    const dataDomain = new DataDomain()
    dataDomain.fields = []
    assert.isFalse(dataDomain.hasNamespaces())
  }).tags(['@modeling', '@namespace', '@has'])
})

test.group('DataDomain.listGraphNamespaces()', () => {
  test('lists all namespaces in the graph', ({ assert }) => {
    const root = new DataDomain()
    const n1 = root.addNamespace({ key: 'ns1' })
    const n2 = root.addNamespace({ key: 'ns2' })
    const namespaces = [...root.listGraphNamespaces()]
    assert.lengthOf(namespaces, 2)
    assert.deepInclude(namespaces, n1)
    assert.deepInclude(namespaces, n2)
  }).tags(['@modeling', '@namespace', '@list'])

  test('lists all namespaces in the graph with a parent', ({ assert }) => {
    const root = new DataDomain()
    const n1 = root.addNamespace({ key: 'parent-ns' })
    const n2 = root.addNamespace({ key: 'ns1' }, n1.key)
    root.addNamespace({ key: 'ns2' })
    const namespaces = [...root.listGraphNamespaces(n1.key)]
    assert.lengthOf(namespaces, 1)
    assert.deepInclude(namespaces, n2)
  }).tags(['@modeling', '@namespace', '@list'])

  test('returns an empty array if there are no namespaces', ({ assert }) => {
    const root = new DataDomain()
    const namespaces = [...root.listGraphNamespaces()]
    assert.lengthOf(namespaces, 0)
  }).tags(['@modeling', '@namespace', '@list'])

  test('returns an empty array if the parent does not exist', ({ assert }) => {
    const root = new DataDomain()
    const namespaces = [...root.listGraphNamespaces('non-existent-parent')]
    assert.lengthOf(namespaces, 0)
  }).tags(['@modeling', '@namespace', '@list'])

  test('returns only namespaces', ({ assert }) => {
    const root = new DataDomain()
    const n1 = root.addNamespace({ key: 'ns1' })
    root.addModel({ key: 'model1' })
    const namespaces = [...root.listGraphNamespaces()]
    assert.lengthOf(namespaces, 1)
    assert.deepInclude(namespaces, n1)
  }).tags(['@modeling', '@namespace', '@list'])

  test('returns only namespaces for the given parent', ({ assert }) => {
    const root = new DataDomain()
    const n1 = root.addNamespace({ key: 'parent-ns' })
    const n2 = root.addNamespace({ key: 'ns1' }, n1.key)
    root.addNamespace({ key: 'ns2' })
    root.addModel({ key: 'model1' }, n1.key)
    root.addModel({ key: 'model2' })
    const namespaces = [...root.listGraphNamespaces(n1.key)]
    assert.lengthOf(namespaces, 1)
    assert.deepInclude(namespaces, n2)
  }).tags(['@modeling', '@namespace', '@list'])

  test('namespaces in the list are of type DomainNamespace', ({ assert }) => {
    const dataDomain = new DataDomain()
    dataDomain.addNamespace({ key: 'ns1' })
    const namespaces = [...dataDomain.listGraphNamespaces()]
    assert.instanceOf(namespaces[0], DomainNamespace)
  }).tags(['@modeling', '@namespace', '@list'])

  test('lists namespaces from the root when no parent is provided', ({ assert }) => {
    const root = new DataDomain()
    const n1 = root.addNamespace({ key: 'ns1' })
    const n2 = root.addNamespace({ key: 'ns2' })
    const n3 = root.addNamespace({ key: 'parent-ns' })
    root.addNamespace({ key: 'ns3' }, n3.key)
    const namespaces = [...root.listGraphNamespaces()]
    assert.lengthOf(namespaces, 3)
    assert.deepInclude(namespaces, n1)
    assert.deepInclude(namespaces, n2)
    assert.deepInclude(namespaces, n3)
  }).tags(['@modeling', '@namespace', '@list'])

  test('lists namespaces from a parent namespace when provided', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentNamespace = dataDomain.addNamespace({ key: 'parent-ns' })
    const namespace1 = dataDomain.addNamespace({ key: 'ns1' }, parentNamespace.key)
    dataDomain.addNamespace({ key: 'ns2' })

    const namespaces = [...dataDomain.listGraphNamespaces(parentNamespace.key)]
    assert.lengthOf(namespaces, 1)
    assert.deepInclude(namespaces, namespace1)
  }).tags(['@modeling', '@namespace', '@list'])

  test('lists namespaces from a parent namespace when provided and the parent has no namespaces', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentNamespace = dataDomain.addNamespace({ key: 'parent-ns' })

    const namespaces = [...dataDomain.listGraphNamespaces(parentNamespace.key)]
    assert.lengthOf(namespaces, 0)
  }).tags(['@modeling', '@namespace', '@list'])

  test('lists namespaces from a parent namespace when provided and the parent is not a namespace', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'model-1' })

    const namespaces = [...dataDomain.listGraphNamespaces(model.key)]
    assert.lengthOf(namespaces, 0)
  }).tags(['@modeling', '@namespace', '@list'])

  test('lists namespaces from a parent namespace when provided and the parent is not in the graph', ({ assert }) => {
    const dataDomain = new DataDomain()

    const namespaces = [...dataDomain.listGraphNamespaces('non-existent-parent')]
    assert.lengthOf(namespaces, 0)
  }).tags(['@modeling', '@namespace', '@list'])

  test('lists all children for the root, excluding foreign namespaces', ({ assert }) => {
    const domain1 = new DataDomain()
    const ns1 = domain1.addNamespace({ key: 'ns1' })

    const domain2 = new DataDomain()
    domain2.addNamespace({ key: 'ns2' })
    domain2.info.version = '1.0.0'
    domain1.registerForeignDomain(domain2)

    const children = [...domain1.listGraphNamespaces()]
    assert.lengthOf(children, 1)
    assert.deepInclude(children, ns1)
  }).tags(['@modeling', '@namespace', '@list'])

  test('lists all children for a namespace, excluding foreign namespaces', ({ assert }) => {
    const domain1 = new DataDomain()
    const ns1 = domain1.addNamespace({ key: 'ns1' })
    const ns2 = ns1.addNamespace({ key: 'ns2' })

    const domain2 = new DataDomain()
    domain2.addNamespace({ key: 'ns3' })
    domain2.info.version = '1.0.0'
    domain1.registerForeignDomain(domain2)

    const children = [...domain1.listGraphNamespaces(ns1.key)]
    assert.lengthOf(children, 1)
    assert.deepInclude(children, ns2)
  }).tags(['@modeling', '@namespace', '@list'])
})

test.group('DataDomain.findNamespace()', () => {
  test('finds a namespace by its key', ({ assert }) => {
    const dataDomain = new DataDomain()
    const namespace = dataDomain.addNamespace({ key: 'test-ns' })
    const foundNamespace = dataDomain.findNamespace('test-ns')
    assert.deepEqual(foundNamespace, namespace)
  }).tags(['@modeling', '@namespace', '@find'])

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

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

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

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

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

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

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

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

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

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

test.group('DataDomain.moveNamespace()', () => {
  test('moves a namespace from the root to a parent namespace', ({ assert }) => {
    const dataDomain = new DataDomain()
    const namespace = dataDomain.addNamespace({ key: 'test-ns' })
    const parentNamespace = dataDomain.addNamespace({ key: 'parent-ns' })
    dataDomain.moveNamespace(namespace.key, parentNamespace.key)
    assert.equal(dataDomain.graph.parent(namespace.key), parentNamespace.key)
    const children = [...parentNamespace.listNamespaces()]
    assert.deepEqual(children, [namespace])
  }).tags(['@modeling', '@namespace', '@move'])

  test('moves a namespace from a parent namespace to the root', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentNamespace = dataDomain.addNamespace({ key: 'parent-ns' })
    const namespace = dataDomain.addNamespace({ key: 'test-ns' }, parentNamespace.key)
    dataDomain.moveNamespace(namespace.key)
    assert.equal(dataDomain.graph.parent(namespace.key), undefined)
    assert.deepEqual([...parentNamespace.listNamespaces()], [])
  }).tags(['@modeling', '@namespace', '@move'])

  test('moves a namespace from one parent namespace to another', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentNamespace1 = dataDomain.addNamespace({ key: 'parent-ns1' })
    const parentNamespace2 = dataDomain.addNamespace({ key: 'parent-ns2' })
    const namespace = dataDomain.addNamespace({ key: 'test-ns' }, parentNamespace1.key)
    dataDomain.moveNamespace(namespace.key, parentNamespace2.key)
    assert.equal(dataDomain.graph.parent(namespace.key), parentNamespace2.key)
    assert.deepEqual([...parentNamespace1.listNamespaces()], [])
    assert.deepEqual([...parentNamespace2.listNamespaces()], [namespace])
  }).tags(['@modeling', '@namespace', '@move'])

  test('throws an error if namespace does not exist', ({ assert }) => {
    const dataDomain = new DataDomain()
    assert.throws(() => {
      dataDomain.moveNamespace('non-existent-ns', 'parent-ns')
    }, 'Namespace non-existent-ns does not exist')
  }).tags(['@modeling', '@namespace', '@move'])

  test('throws an error if parent namespace does not exist', ({ assert }) => {
    const dataDomain = new DataDomain()
    const namespace = dataDomain.addNamespace({ key: 'test-ns' })
    assert.throws(() => {
      dataDomain.moveNamespace(namespace.key, 'non-existent-parent')
    }, 'Parent namespace non-existent-parent does not exist')
  }).tags(['@modeling', '@namespace', '@move'])

  test('throws an error if namespace is moved to itself', ({ assert }) => {
    const dataDomain = new DataDomain()
    const namespace = dataDomain.addNamespace({ key: 'test-ns' })
    assert.throws(() => {
      dataDomain.moveNamespace(namespace.key, namespace.key)
    }, 'Cannot move a namespace to itself')
  }).tags(['@modeling', '@namespace', '@move'])

  test('throws an error if namespace is moved to its own child', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentNamespace = dataDomain.addNamespace({ key: 'parent-ns' })
    const namespace = dataDomain.addNamespace({ key: 'test-ns' }, parentNamespace.key)
    assert.throws(() => {
      dataDomain.moveNamespace(parentNamespace.key, namespace.key)
    }, 'Cannot move a namespace to its own child')
  }).tags(['@modeling', '@namespace', '@move'])

  test('throws an error if namespace is moved to a foreign domain', ({ assert }) => {
    const dataDomain1 = new DataDomain()
    const dataDomain2 = new DataDomain()

    const namespace = dataDomain1.addNamespace({ key: 'test-ns' })
    const foreignNamespace = dataDomain2.addNamespace({ key: 'foreign-ns' })

    dataDomain2.info.version = '1.0.0'
    dataDomain1.registerForeignDomain(dataDomain2)

    assert.throws(() => {
      dataDomain1.moveNamespace(namespace.key, `${dataDomain2.key}:${foreignNamespace.key}`)
    }, 'Cannot move a namespace to a foreign domain')
  }).tags(['@modeling', '@namespace', '@move'])

  test('throws an error if namespace is moved from a foreign domain', ({ assert }) => {
    const dataDomain1 = new DataDomain()
    const dataDomain2 = new DataDomain()

    const namespace = dataDomain1.addNamespace({ key: 'test-ns' })
    const foreignNamespace = dataDomain2.addNamespace({ key: 'foreign-ns' })

    dataDomain2.info.version = '1.0.0'
    dataDomain1.registerForeignDomain(dataDomain2)

    assert.throws(() => {
      dataDomain1.moveNamespace(`${dataDomain2.key}:${foreignNamespace.key}`, namespace.key)
    }, 'Cannot move a namespace from a foreign domain')
  }).tags(['@modeling', '@namespace', '@move'])

  test('moveNamespace notifies change', async ({ assert }) => {
    const dataDomain = new DataDomain()
    const namespace = dataDomain.addNamespace({ key: 'test-ns' })
    const parentNamespace = dataDomain.addNamespace({ key: 'parent-ns' })
    dataDomain.moveNamespace(namespace.key, parentNamespace.key)
    await assert.dispatches(dataDomain, 'change', { timeout: 20 })
  }).tags(['@modeling', '@namespace', '@move'])

  test('moves a namespace from the root to a parent namespace - checks parent-child relationship', ({ assert }) => {
    const dataDomain = new DataDomain()
    const namespace = dataDomain.addNamespace({ key: 'test-ns' })
    const parentNamespace = dataDomain.addNamespace({ key: 'parent-ns' })
    dataDomain.moveNamespace(namespace.key, parentNamespace.key)
    assert.isTrue(dataDomain.graph.hasParent(namespace.key, parentNamespace.key))
  }).tags(['@modeling', '@namespace', '@move'])

  test('moves a namespace from a parent namespace to the root - checks parent-child relationship', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentNamespace = dataDomain.addNamespace({ key: 'parent-ns' })
    const namespace = dataDomain.addNamespace({ key: 'test-ns' }, parentNamespace.key)
    dataDomain.moveNamespace(namespace.key)
    assert.isFalse(dataDomain.graph.hasParent(namespace.key, parentNamespace.key))
  }).tags(['@modeling', '@namespace', '@move'])

  test('moves a namespace from one parent namespace to another - checks parent-child relationship', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentNamespace1 = dataDomain.addNamespace({ key: 'parent-ns1' })
    const parentNamespace2 = dataDomain.addNamespace({ key: 'parent-ns2' })
    const namespace = dataDomain.addNamespace({ key: 'test-ns' }, parentNamespace1.key)
    dataDomain.moveNamespace(namespace.key, parentNamespace2.key)
    assert.isFalse(dataDomain.graph.hasParent(namespace.key, parentNamespace1.key))
    assert.isTrue(dataDomain.graph.hasParent(namespace.key, parentNamespace2.key))
  }).tags(['@modeling', '@namespace', '@move'])

  test('moves a namespace from the root to a parent namespace - checks items list', ({ assert }) => {
    const dataDomain = new DataDomain()
    const namespace = dataDomain.addNamespace({ key: 'test-ns' })
    const parentNamespace = dataDomain.addNamespace({ key: 'parent-ns' })
    dataDomain.moveNamespace(namespace.key, parentNamespace.key)
    const parentNs = dataDomain.findNamespace(parentNamespace.key)
    assert.deepInclude([...parentNs!.listNamespaces()], namespace)
  }).tags(['@modeling', '@namespace', '@move'])

  test('moves a namespace from a parent namespace to the root - checks items list', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentNamespace = dataDomain.addNamespace({ key: 'parent-ns' })
    const namespace = dataDomain.addNamespace({ key: 'test-ns' }, parentNamespace.key)
    dataDomain.moveNamespace(namespace.key)
    assert.deepInclude([...dataDomain.listGraphNamespaces()], namespace)
  }).tags(['@modeling', '@namespace', '@move'])

  test('moves a namespace from one parent namespace to another - checks items list', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentNamespace1 = dataDomain.addNamespace({ key: 'parent-ns1' })
    const parentNamespace2 = dataDomain.addNamespace({ key: 'parent-ns2' })
    const namespace = dataDomain.addNamespace({ key: 'test-ns' }, parentNamespace1.key)
    dataDomain.moveNamespace(namespace.key, parentNamespace2.key)
    const parentNs1 = dataDomain.findNamespace(parentNamespace1.key)
    const parentNs2 = dataDomain.findNamespace(parentNamespace2.key)
    assert.notDeepInclude([...parentNs1!.listNamespaces()], namespace)
    assert.deepInclude([...parentNs2!.listNamespaces()], namespace)
  }).tags(['@modeling', '@namespace', '@move'])

  test('moves a namespace from the root to a parent namespace - updates fields array', ({ assert }) => {
    const dataDomain = new DataDomain()
    const namespace = dataDomain.addNamespace({ key: 'test-ns' })
    const parentNamespace = dataDomain.addNamespace({ key: 'parent-ns' })
    dataDomain.moveNamespace(namespace.key, parentNamespace.key)
    assert.notDeepInclude(dataDomain.fields, { key: namespace.key, type: 'namespace' })
  }).tags(['@modeling', '@namespace', '@move'])

  test('moves a namespace from a parent namespace to the root - updates fields array', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentNamespace = dataDomain.addNamespace({ key: 'parent-ns' })
    const namespace = dataDomain.addNamespace({ key: 'test-ns' }, parentNamespace.key)
    dataDomain.moveNamespace(namespace.key)
    assert.deepInclude(dataDomain.fields, { key: namespace.key, type: 'namespace' })
  }).tags(['@modeling', '@namespace', '@move'])

  test('moves a namespace from one parent namespace to another - updates fields array', ({ assert }) => {
    const dataDomain = new DataDomain()
    const parentNamespace1 = dataDomain.addNamespace({ key: 'parent-ns1' })
    const parentNamespace2 = dataDomain.addNamespace({ key: 'parent-ns2' })
    const namespace = dataDomain.addNamespace({ key: 'test-ns' }, parentNamespace1.key)
    dataDomain.moveNamespace(namespace.key, parentNamespace2.key)
    assert.notDeepInclude(dataDomain.fields, { key: namespace.key, type: 'namespace' })
  }).tags(['@modeling', '@namespace', '@move'])

  test('moves a namespace from one parent namespace to another - updates fields array when moving to root', ({
    assert,
  }) => {
    const dataDomain = new DataDomain()
    const parentNamespace1 = dataDomain.addNamespace({ key: 'parent-ns1' })
    const namespace = dataDomain.addNamespace({ key: 'test-ns' }, parentNamespace1.key)
    dataDomain.moveNamespace(namespace.key)
    assert.deepInclude(dataDomain.fields, { key: namespace.key, type: 'namespace' })
  }).tags(['@modeling', '@namespace', '@move'])
})
