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

test.group('ApiModel.removeEntity()', () => {
  test('removes an existing entity', ({ assert }) => {
    const domain = new DataDomain()
    domain.info.version = '1.0.0'
    const dm = domain.addModel()
    const e1 = dm.addEntity()
    const model = new ApiModel()
    model.attachDataDomain(domain)
    const exposure = model.exposeEntity({ key: e1.key })
    assert.lengthOf(model.exposes, 1)

    model.removeExposedEntity(exposure.key)
    assert.lengthOf(model.exposes, 0)
  }).tags(['@modeling', '@api'])

  test('removes an entity and its nested children', ({ assert }) => {
    const domain = new DataDomain()
    domain.info.version = '1.0.0'
    const dm = domain.addModel()
    const eA = dm.addEntity({ info: { name: 'A' } })
    const eB = dm.addEntity({ info: { name: 'B' } })
    // A -> B
    eA.addAssociation({ info: { name: 'toB' }, targets: [{ key: eB.key }] })
    const model = new ApiModel()
    model.attachDataDomain(domain)
    const rootExposure = model.exposeEntity({ key: eA.key }, { followAssociations: true })
    // Ensure nested exposure for B was created
    const nestedB = model.exposes.find((e) => !e.isRoot && e.entity.key === eB.key)
    assert.isDefined(nestedB)
    assert.isAbove(model.exposes.length, 1)

    // Remove root exposure for A and expect children to be removed as well
    model.removeExposedEntity(rootExposure.key)
    assert.lengthOf(model.exposes, 0)
  }).tags(['@modeling', '@api'])

  test('throws error if entity does not exist', ({ assert }) => {
    const domain = new DataDomain()
    domain.info.version = '1.0.0'
    const dm = domain.addModel()
    const e1 = dm.addEntity()
    const model = new ApiModel()
    model.attachDataDomain(domain)
    model.exposeEntity({ key: e1.key })

    assert.throws(
      () => model.removeExposedEntity('non-existing-key'),
      'Exposed entity with key "non-existing-key" not found.'
    )
    assert.lengthOf(model.exposes, 1, 'exposes count should remain unchanged')
  }).tags(['@modeling', '@api'])

  test('notifies change when an entity is removed', async ({ assert }) => {
    const domain = new DataDomain()
    domain.info.version = '1.0.0'
    const dm = domain.addModel()
    const e1 = dm.addEntity()
    const model = new ApiModel()
    model.attachDataDomain(domain)
    const exposure = model.exposeEntity({ key: e1.key })

    let notified = false
    model.addEventListener('change', () => {
      notified = true
    })
    model.removeExposedEntity(exposure.key)
    await Promise.resolve() // Allow microtask to run
    assert.isTrue(notified)
  }).tags(['@modeling', '@api'])

  test('does not notify change if entity to remove does not exist', async ({ assert }) => {
    const model = new ApiModel()
    let notified = false
    model.addEventListener('change', () => {
      notified = true
    })
    try {
      model.removeExposedEntity('no-notify-remove-entity')
    } catch {
      // ignore error
    }
    await Promise.resolve() // Allow microtask to run
    assert.isFalse(notified)
  }).tags(['@modeling', '@api'])
})
