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

test.group('DomainModel.addEntity()', () => {
  test('adds an entity to the model', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = model.addEntity({ key: 'test-entity' })
    assert.instanceOf(entity, DomainEntity)
    assert.equal(entity.kind, DomainEntityKind)
    assert.equal(entity.key, 'test-entity')
    assert.deepInclude(entity.info, { name: 'new_entity' })
    assert.isTrue(dataDomain.graph.hasNode(entity.key))
    assert.equal(dataDomain.graph.parent(entity.key), model.key)
  })

  test('adds an entity with default values', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = model.addEntity()
    assert.instanceOf(entity, DomainEntity)
    assert.equal(entity.kind, DomainEntityKind)
    assert.typeOf(entity.key, 'string')
    assert.isNotEmpty(entity.key)
    assert.deepInclude(entity.info, { name: 'new_entity' })
    assert.isTrue(dataDomain.graph.hasNode(entity.key))
    assert.equal(dataDomain.graph.parent(entity.key), model.key)
  })

  test('throws an error if an entity with the same key already exists', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    model.addEntity({ key: 'test-entity' })
    assert.throws(() => {
      model.addEntity({ key: 'test-entity' })
    }, 'Entity with key test-entity already exists')
  })

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

  test('adds an entity to the fields array', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = model.addEntity({ key: 'test-entity' })
    assert.deepEqual(model.fields, [{ key: entity.key, type: 'entity' }])
  })

  test('adds an entity to the root graph', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = model.addEntity({ key: 'test-entity' })
    assert.isTrue(dataDomain.graph.hasNode(entity.key))
  })

  test('adds an entity with a parent relationship', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = model.addEntity({ key: 'test-entity' })
    assert.equal(dataDomain.graph.parent(entity.key), model.key)
  })
})

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

  test('throws an error if the entity does not exist', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    assert.throws(() => {
      model.removeEntity('non-existent-entity')
    }, "Trying to remove the non-existent-entity entity, but it doesn't exist")
  })

  test('throws an error if the key is not an entity', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    assert.throws(() => {
      model.removeEntity(model.key)
    }, `Trying to remove the ${model.key} entity, but it is not an entity`)
  })

  test('throws an error if the entity belongs to another parent', ({ assert }) => {
    const root = new DataDomain()
    const m1 = root.addModel()
    const m2 = root.addModel()
    const e1 = m1.addEntity()
    assert.throws(() => {
      m2.removeEntity(e1.key)
    }, `Trying to remove the ${e1.key} entity, but it is not a child of this model`)
  })

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

  test('removes the entity from the fields array', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = model.addEntity({ key: 'test-entity' })
    model.removeEntity(entity.key)
    assert.deepEqual(model.fields, [])
  })

  test('removes the entity from the root graph', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = model.addEntity({ key: 'test-entity' })
    model.removeEntity(entity.key)
    assert.isFalse(dataDomain.graph.hasNode(entity.key))
  })

  test('removes the parent relationship', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = model.addEntity({ key: 'test-entity' })
    model.removeEntity(entity.key)
    assert.isUndefined(dataDomain.graph.parent(entity.key))
    assert.deepEqual([...dataDomain.graph.children(model.key)], [])
  })

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

  test('removes an entity with associations', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity = model.addEntity({ key: 'test-entity' })
    const association = entity.addAssociation()
    model.removeEntity(entity.key)
    assert.isFalse(dataDomain.graph.hasNode(entity.key))
    assert.isFalse(dataDomain.graph.hasNode(association.key))
  })
})

test.group('DomainModel.attachEntity()', () => {
  test('attaches an entity to the model', ({ assert }) => {
    const root = new DataDomain()
    const m1 = root.addModel()
    const m2 = root.addModel()
    const e1 = m1.addEntity()
    m2.attachEntity(e1.key)
    assert.isTrue(root.graph.hasNode(e1.key))
    assert.equal(root.graph.parent(e1.key), m2.key)
    assert.deepEqual(m2.fields, [{ key: e1.key, type: 'entity' }])
  })

  test('throws an error if the entity does not exist', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    assert.throws(() => {
      model.attachEntity('non-existent-entity')
    }, "Trying to attach the non-existent-entity entity, but it doesn't exist")
  })

  test('throws an error if the key is not an entity', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    assert.throws(() => {
      model.attachEntity(model.key)
    }, `Trying to attach the ${model.key} entity, but it is not a entity`)
  })

  test('throws an error if the entity is already a child of this model', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    assert.throws(() => {
      model.attachEntity(entity.key)
    }, `Trying to attach the ${entity.key} entity, but it is already a child of this model`)
  })

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

    const dataDomain2 = new DataDomain()
    const model2 = dataDomain2.addModel()
    const entity = model2.addEntity()
    dataDomain2.info.version = '1.0.0'
    dataDomain1.registerForeignDomain(dataDomain2)

    assert.throws(() => {
      model1.attachEntity(`${dataDomain2.key}:${entity.key}`)
    }, `Trying to attach the ${dataDomain2.key}:${entity.key} entity, but it is not in the same domain`)
  })

  test('attaches an entity to the fields array', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model1 = dataDomain.addModel({ key: 'test-model1' })
    const model2 = dataDomain.addModel({ key: 'test-model2' })
    const entity = model1.addEntity({ key: 'test-entity' })
    model2.attachEntity(entity.key)
    assert.deepEqual(model2.fields, [{ key: entity.key, type: 'entity' }])
  })

  test('attaches an entity to the root graph', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model1 = dataDomain.addModel({ key: 'test-model1' })
    const model2 = dataDomain.addModel({ key: 'test-model2' })
    const entity = model1.addEntity({ key: 'test-entity' })
    model2.attachEntity(entity.key)
    assert.isTrue(dataDomain.graph.hasNode(entity.key))
  })

  test('attaches an entity with a parent relationship', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model1 = dataDomain.addModel({ key: 'test-model1' })
    const model2 = dataDomain.addModel({ key: 'test-model2' })
    const entity = model1.addEntity({ key: 'test-entity' })
    model2.attachEntity(entity.key)
    assert.equal(dataDomain.graph.parent(entity.key), model2.key)
  })

  test('attaches an entity and removes it from the previous parent', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model1 = dataDomain.addModel()
    const model2 = dataDomain.addModel()
    const entity = model1.addEntity()
    model2.attachEntity(entity.key)
    assert.deepEqual(model1.fields, [])
  })
})

test.group('DomainModel.detachEntity()', () => {
  test('detaches an entity from the model', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    model.detachEntity(entity.key)
    assert.isFalse(dataDomain.graph.hasParent(entity.key, model.key))
    assert.deepEqual(model.fields, [])
  })

  test('throws an error if the entity does not exist', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    assert.throws(() => {
      model.detachEntity('non-existent-entity')
    }, "Trying to detach the non-existent-entity entity, but it doesn't exist")
  })

  test('throws an error if the key is not an entity', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    model.fields.push({ key: model.key, type: 'entity' })
    assert.throws(() => {
      model.detachEntity(model.key)
    }, `Trying to detach the ${model.key} entity, but it is not an entity`)
  })

  test('throws an error if the entity is not a child of this model', ({ assert }) => {
    const root = new DataDomain()
    const m1 = root.addModel()
    const m2 = root.addModel()
    const e1 = m1.addEntity()
    m2.fields.push({ key: e1.key, type: 'entity' })
    assert.throws(() => {
      m2.detachEntity(e1.key)
    }, `Trying to detach the ${e1.key} entity, but it is not a child of this model`)
  })

  test('detaches an entity from the fields array', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    model.detachEntity(entity.key)
    assert.deepEqual(model.fields, [])
  })

  test('adds an entity from the root graph', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    model.detachEntity(entity.key)
    assert.isTrue(dataDomain.graph.hasNode(entity.key))
  })

  test('detaches an entity and sets its parent to the root graph', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel()
    const entity = model.addEntity()
    model.detachEntity(entity.key)
    assert.equal(dataDomain.graph.parent(entity.key), undefined)
  })
})

test.group('DomainModel.listEntities()', () => {
  test('lists all entities in the model', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity1 = model.addEntity({ key: 'entity1' })
    const entity2 = model.addEntity({ key: 'entity2' })
    const entities = [...model.listEntities()]
    assert.lengthOf(entities, 2)
    assert.deepInclude(entities, entity1)
    assert.deepInclude(entities, entity2)
  })

  test('returns an empty array if there are no entities', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entities = [...model.listEntities()]
    assert.lengthOf(entities, 0)
  })

  test('returns only entities', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity1 = model.addEntity({ key: 'entity1' })
    dataDomain.addNamespace({ key: 'ns1' })
    const entities = [...model.listEntities()]
    assert.lengthOf(entities, 1)
    assert.deepInclude(entities, entity1)
  })

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

  test('lists entities in the order they were added', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    const entity1 = model.addEntity({ key: 'entity1' })
    const entity2 = model.addEntity({ key: 'entity2' })
    const entities = [...model.listEntities()]
    assert.deepEqual(entities, [entity1, entity2])
  })

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

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

test.group('DomainModel.hasEntities()', () => {
  test('returns true if there are child entities', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    model.addEntity({ key: 'test-entity' })
    assert.isTrue(model.hasEntities())
  })

  test('returns false if there are no child entities', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    assert.isFalse(model.hasEntities())
  })

  test('returns false if there are only other domain elements', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    dataDomain.addNamespace({ key: 'test-namespace' })
    assert.isFalse(model.hasEntities())
  })

  test('returns true if there are both entities and other domain elements', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    model.addEntity({ key: 'test-entity' })
    dataDomain.addNamespace({ key: 'test-namespace' })
    assert.isTrue(model.hasEntities())
  })

  test('returns false if the model has no fields', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model = dataDomain.addModel({ key: 'test-model' })
    model.fields = []
    assert.isFalse(model.hasEntities())
  })
})

test.group('DomainModel.attachEntity() - Race Condition Tests', () => {
  test('simulate multiple entities moved to same target simultaneously', ({ assert }) => {
    const dataDomain = new DataDomain()
    const sourceModel1 = dataDomain.addModel({ key: 'source1' })
    const sourceModel2 = dataDomain.addModel({ key: 'source2' })
    const targetModel = dataDomain.addModel({ key: 'target' })

    const entity1 = sourceModel1.addEntity({ key: 'entity1' })
    const entity2 = sourceModel2.addEntity({ key: 'entity2' })

    // Move multiple entities to the same target
    targetModel.attachEntity(entity1.key)
    targetModel.attachEntity(entity2.key)

    // Verify no duplication in fields array
    const entityFields = targetModel.fields.filter((f) => f.type === 'entity')
    const entityKeys = entityFields.map((f) => f.key)
    const uniqueKeys = [...new Set(entityKeys)]
    assert.equal(entityFields.length, uniqueKeys.length, 'No duplicate entities in fields array')
    assert.lengthOf(entityFields, 2, 'Should have exactly 2 entities')
    assert.include(entityKeys, entity1.key, 'Should contain entity1')
    assert.include(entityKeys, entity2.key, 'Should contain entity2')

    // Verify graph consistency
    assert.equal(dataDomain.graph.parent(entity1.key), targetModel.key, 'Entity1 parent should be target model')
    assert.equal(dataDomain.graph.parent(entity2.key), targetModel.key, 'Entity2 parent should be target model')

    // Verify entities removed from source models
    assert.lengthOf(sourceModel1.fields, 0, 'Source1 should have no entities')
    assert.lengthOf(sourceModel2.fields, 0, 'Source2 should have no entities')
  })

  test('simulate entity moved between models multiple times rapidly', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model1 = dataDomain.addModel({ key: 'model1' })
    const model2 = dataDomain.addModel({ key: 'model2' })
    const model3 = dataDomain.addModel({ key: 'model3' })

    const entity = model1.addEntity({ key: 'entity' })

    // Simulate rapid moves
    model2.attachEntity(entity.key)
    model3.attachEntity(entity.key)
    model1.attachEntity(entity.key)
    model2.attachEntity(entity.key)

    // Final state verification
    assert.lengthOf(model2.fields, 1, 'Model2 should have 1 entity')
    assert.lengthOf(model1.fields, 0, 'Model1 should have no entities')
    assert.lengthOf(model3.fields, 0, 'Model3 should have no entities')
    assert.equal(dataDomain.graph.parent(entity.key), model2.key, 'Entity parent should be model2')
  })

  test('simulate concurrent attachment of same entity to different targets', ({ assert }) => {
    const dataDomain = new DataDomain()
    const sourceModel = dataDomain.addModel({ key: 'source' })
    const targetModel1 = dataDomain.addModel({ key: 'target1' })
    const targetModel2 = dataDomain.addModel({ key: 'target2' })

    const entity = sourceModel.addEntity({ key: 'entity' })

    // First move should succeed
    targetModel1.attachEntity(entity.key)
    assert.equal(dataDomain.graph.parent(entity.key), targetModel1.key, 'Entity should be in target1')
    assert.lengthOf(targetModel1.fields, 1, 'Target1 should have the entity')
    assert.lengthOf(sourceModel.fields, 0, 'Source should be empty')

    // Second move should move from target1 to target2
    targetModel2.attachEntity(entity.key)
    assert.equal(dataDomain.graph.parent(entity.key), targetModel2.key, 'Entity should be in target2')
    assert.lengthOf(targetModel2.fields, 1, 'Target2 should have the entity')
    assert.lengthOf(targetModel1.fields, 0, 'Target1 should be empty')
    assert.lengthOf(sourceModel.fields, 0, 'Source should still be empty')
  })

  test('verify entity is not duplicated when moved from model with multiple entities', ({ assert }) => {
    const dataDomain = new DataDomain()
    const sourceModel = dataDomain.addModel({ key: 'source' })
    const targetModel = dataDomain.addModel({ key: 'target' })

    const entity1 = sourceModel.addEntity({ key: 'entity1' })
    const entity2 = sourceModel.addEntity({ key: 'entity2' })
    const entity3 = sourceModel.addEntity({ key: 'entity3' })

    // Move middle entity
    targetModel.attachEntity(entity2.key)

    // Verify source model has 2 entities remaining
    assert.lengthOf(sourceModel.fields, 2, 'Source should have 2 entities remaining')
    const sourceKeys = sourceModel.fields.map((f) => f.key)
    assert.include(sourceKeys, entity1.key, 'Should contain entity1')
    assert.include(sourceKeys, entity3.key, 'Should contain entity3')
    assert.notInclude(sourceKeys, entity2.key, 'Should not contain moved entity2')

    // Verify target model has 1 entity
    assert.lengthOf(targetModel.fields, 1, 'Target should have 1 entity')
    assert.equal(targetModel.fields[0].key, entity2.key, 'Target should have entity2')

    // Verify graph consistency
    assert.equal(dataDomain.graph.parent(entity1.key), sourceModel.key, 'Entity1 parent should be source')
    assert.equal(dataDomain.graph.parent(entity2.key), targetModel.key, 'Entity2 parent should be target')
    assert.equal(dataDomain.graph.parent(entity3.key), sourceModel.key, 'Entity3 parent should be source')
  })

  test('verify field consistency when moving entities with properties', ({ assert }) => {
    const dataDomain = new DataDomain()
    const sourceModel = dataDomain.addModel({ key: 'source' })
    const targetModel = dataDomain.addModel({ key: 'target' })

    const entity = sourceModel.addEntity({ key: 'entity' })
    entity.addProperty({ key: 'prop1', type: 'string' })
    entity.addProperty({ key: 'prop2', type: 'number' })

    // Verify initial state
    assert.lengthOf(entity.fields, 2, 'Entity should have 2 properties')

    // Move entity
    targetModel.attachEntity(entity.key)

    // Verify entity fields are preserved
    assert.lengthOf(entity.fields, 2, 'Entity should still have 2 properties after move')
    assert.equal(dataDomain.graph.parent(entity.key), targetModel.key, 'Entity parent should be target')

    // Verify model fields
    assert.lengthOf(sourceModel.fields, 0, 'Source model should be empty')
    assert.lengthOf(targetModel.fields, 1, 'Target model should have 1 entity')
  })

  test('stress test - multiple entities moved in sequence to verify no field array corruption', ({ assert }) => {
    const dataDomain = new DataDomain()
    const sourceModels = []
    const targetModel = dataDomain.addModel({ key: 'target' })
    const entities = []

    // Create 10 source models with 1 entity each
    for (let i = 0; i < 10; i++) {
      const sourceModel = dataDomain.addModel({ key: `source${i}` })
      sourceModels.push(sourceModel)
      const entity = sourceModel.addEntity({ key: `entity${i}` })
      entities.push(entity)
    }

    // Move all entities to target model
    for (const entity of entities) {
      targetModel.attachEntity(entity.key)
    }

    // Verify target model state
    assert.lengthOf(targetModel.fields, 10, 'Target should have 10 entities')
    const targetKeys = targetModel.fields.map((f) => f.key).sort()
    const expectedKeys = entities.map((e) => e.key).sort()
    assert.deepEqual(targetKeys, expectedKeys, 'Target should have all expected entities')

    // Verify no duplicates
    const uniqueKeys = [...new Set(targetKeys)]
    assert.equal(targetKeys.length, uniqueKeys.length, 'No duplicate keys in target')

    // Verify source models are empty
    for (const sourceModel of sourceModels) {
      assert.lengthOf(sourceModel.fields, 0, `Source model ${sourceModel.key} should be empty`)
    }

    // Verify graph consistency
    for (const entity of entities) {
      assert.equal(dataDomain.graph.parent(entity.key), targetModel.key, `Entity ${entity.key} parent should be target`)
    }
  })

  test('verify detach-attach sequence atomicity', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model1 = dataDomain.addModel({ key: 'model1' })
    const model2 = dataDomain.addModel({ key: 'model2' })

    const entity = model1.addEntity({ key: 'entity' })

    // Spy on the detachEntity method to verify it's called
    let detachCalled = false
    const originalDetach = model1.detachEntity
    model1.detachEntity = function (key: string) {
      detachCalled = true
      return originalDetach.call(this, key)
    }

    model2.attachEntity(entity.key)

    // Verify detach was called during attach
    assert.isTrue(detachCalled, 'detachEntity should have been called on the source model')

    // Verify final state
    assert.lengthOf(model1.fields, 0, 'Model1 should be empty')
    assert.lengthOf(model2.fields, 1, 'Model2 should have the entity')
    assert.equal(dataDomain.graph.parent(entity.key), model2.key, 'Graph parent should be model2')
  })

  test('verify graph parent is updated before fields array check', ({ assert }) => {
    const dataDomain = new DataDomain()
    const model1 = dataDomain.addModel({ key: 'model1' })
    const model2 = dataDomain.addModel({ key: 'model2' })

    const entity = model1.addEntity({ key: 'entity' })

    // Override setParent to track when it's called
    const parentSetCalls: { nodeKey: string; parentKey?: string; timestamp: number }[] = []
    const originalSetParent = dataDomain.graph.setParent
    dataDomain.graph.setParent = function (nodeKey: string, parentKey?: string) {
      parentSetCalls.push({ nodeKey, parentKey, timestamp: Date.now() })
      return originalSetParent.call(this, nodeKey, parentKey)
    }

    // Override fields.push to track when it's called
    const fieldsPushCalls: { items: unknown[]; timestamp: number }[] = []
    const originalPush = model2.fields.push
    model2.fields.push = function (...items) {
      fieldsPushCalls.push({ items: [...items], timestamp: Date.now() })
      return originalPush.call(this, ...items)
    }

    model2.attachEntity(entity.key)

    // Verify the sequence - setParent should be called twice (detach + attach)
    assert.lengthOf(parentSetCalls, 2, 'setParent should be called twice (detach + attach)')
    assert.lengthOf(fieldsPushCalls, 1, 'fields.push should be called once')

    // First call should clear the parent (detach)
    assert.equal(parentSetCalls[0].nodeKey, entity.key, 'First call should be for entity key')
    assert.isUndefined(parentSetCalls[0].parentKey, 'First call should clear parent')

    // Second call should set the new parent (attach)
    assert.equal(parentSetCalls[1].parentKey, model2.key, 'Second call should set parent to model2')
    assert.equal(parentSetCalls[1].nodeKey, entity.key, 'Second call should be for entity key')
  })

  test('detect inconsistent graph parent - entity in fields but wrong graph parent', ({ assert }) => {
    const dataDomain = new DataDomain()
    const sourceModel = dataDomain.addModel({ key: 'source' })
    const targetModel = dataDomain.addModel({ key: 'target' })

    const entity = sourceModel.addEntity({ key: 'entity' })

    // Simulate race condition where graph parent is not updated during attach
    const originalSetParent = dataDomain.graph.setParent
    let setParentCallCount = 0
    dataDomain.graph.setParent = function (nodeKey: string, parentKey?: string) {
      setParentCallCount++
      // Skip the second setParent call (the attach operation)
      if (setParentCallCount === 2) {
        return this // Don't actually set the parent
      }
      return originalSetParent.call(this, nodeKey, parentKey)
    }

    assert.throws(() => {
      targetModel.attachEntity(entity.key)
    })

    // Restore original method
    dataDomain.graph.setParent = originalSetParent
  })

  test('verify consistency during rapid sequential moves', ({ assert }) => {
    const dataDomain = new DataDomain()
    const models: DomainModel[] = []

    // Create multiple models
    for (let i = 0; i < 5; i++) {
      models.push(dataDomain.addModel({ key: `model${i}` }))
    }

    const entity = models[0].addEntity({ key: 'entity' })

    // Perform rapid moves between models
    for (let iteration = 0; iteration < 20; iteration++) {
      const targetIndex = (iteration + 1) % models.length
      models[targetIndex].attachEntity(entity.key)

      // Verify consistency after each move
      let entityCount = 0
      let modelWithEntity = null

      for (const model of models) {
        const hasEntity = model.fields.some((f) => f.type === 'entity' && f.key === entity.key)
        if (hasEntity) {
          entityCount++
          modelWithEntity = model
        }
      }

      // Entity should exist in exactly one model
      assert.equal(
        entityCount,
        1,
        `Iteration ${iteration}: Entity should exist in exactly one model, found in ${entityCount} models`
      )

      // Graph parent should match the model that has the entity
      const graphParent = dataDomain.graph.parent(entity.key)
      assert.equal(
        graphParent,
        modelWithEntity?.key,
        `Iteration ${iteration}: Graph parent should match model with entity`
      )
    }
  })
})
