import { test } from '@japa/runner'
import { DataDomain } from '../../../src/modeling/DataDomain.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' })
  })

  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`
    )
  })

  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`
    )
  })

  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)
  })

  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 }
    )
  })

  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}`))
  })

  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}`))
  })

  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}`)
    )
  })

  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}`))
  })
})

test.group('DataDomain.findForeignEntity()', () => {
  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.findForeignEntity('test-entity', foreignDomain.key)
    assert.deepEqual(foundEntity, entity)
  })

  test('returns undefined if the foreign domain does not exist', ({ assert }) => {
    const dataDomain = new DataDomain()
    const foundEntity = dataDomain.findForeignEntity('test-entity', 'non-existent-domain')
    assert.isUndefined(foundEntity)
  })

  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.findForeignEntity('non-existent-entity', foreignDomain.key)
    assert.isUndefined(foundEntity)
  })

  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.findForeignEntity(model.key, foreignDomain.key)
    assert.isUndefined(foundEntity)
  })

  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.findForeignEntity(undefined, foreignDomain.key)
    assert.isUndefined(foundEntity)
  })

  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.findForeignEntity(null, foreignDomain.key)
    assert.isUndefined(foundEntity)
  })

  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.findForeignEntity('', foreignDomain.key)
    assert.isUndefined(foundEntity)
  })

  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.findForeignEntity(123, foreignDomain.key)
    assert.isUndefined(foundEntity)
  })

  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.findForeignEntity(true, foreignDomain.key)
    assert.isUndefined(foundEntity)
  })

  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.findForeignEntity({}, foreignDomain.key)
    assert.isUndefined(foundEntity)
  })

  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.findForeignEntity([], foreignDomain.key)
    assert.isUndefined(foundEntity)
  })

  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.findForeignEntity(() => {}, foreignDomain.key)
    assert.isUndefined(foundEntity)
  })

  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)
    // @ts-expect-error For testing purposes
    const foundEntity = dataDomain.findForeignEntity(entity.key, undefined)
    assert.isUndefined(foundEntity)
  })

  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.findForeignEntity(entity.key, null)
    assert.isUndefined(foundEntity)
  })

  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.findForeignEntity(entity.key, '')
    assert.isUndefined(foundEntity)
  })

  test('returns undefined if 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)
    // @ts-expect-error For testing purposes
    const foundEntity = dataDomain.findForeignEntity(entity.key, 123)
    assert.isUndefined(foundEntity)
  })

  test('returns undefined if 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)
    // @ts-expect-error For testing purposes
    const foundEntity = dataDomain.findForeignEntity(entity.key, true)
    assert.isUndefined(foundEntity)
  })

  test('returns undefined if 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)
    // @ts-expect-error For testing purposes
    const foundEntity = dataDomain.findForeignEntity(entity.key, {})
    assert.isUndefined(foundEntity)
  })

  test('returns undefined if 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)
    // @ts-expect-error For testing purposes
    const foundEntity = dataDomain.findForeignEntity(entity.key, [])
    assert.isUndefined(foundEntity)
  })

  test('returns undefined if 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)
    // @ts-expect-error For testing purposes
    const foundEntity = dataDomain.findForeignEntity(entity.key, () => {})
    assert.isUndefined(foundEntity)
  })
})
