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

test.group('ExposedEntity Path Setter Validation', () => {
  test('throws when setting collection path that collides with another root entity', ({ assert }) => {
    const domain = new DataDomain()
    domain.info.version = '1.0.0'
    const dm = domain.addModel()
    const e1 = dm.addEntity({ info: { name: 'A' } })
    const e2 = dm.addEntity({ info: { name: 'B' } })

    const model = new ApiModel()
    model.attachDataDomain(domain)

    model.exposeEntity({ key: e1.key }) // /as
    const exp2 = model.exposeEntity({ key: e2.key }) // /bs

    // Try to rename exp2's collection path to /as (collision)
    assert.throws(
      () => exp2.setCollectionPath('/as'),
      'Collection path "/as" is already in use by another root entity.'
    )
  })

  test('throws when setting resource path that collides with another root entity (singleton)', ({ assert }) => {
    const domain = new DataDomain()
    domain.info.version = '1.0.0'
    const dm = domain.addModel()
    const e1 = dm.addEntity({ info: { name: 'A' } })
    const e2 = dm.addEntity({ info: { name: 'B' } }) // Collection-less

    const model = new ApiModel()
    model.attachDataDomain(domain)

    const exp1 = model.exposeEntity({ key: e1.key }) // /as, /as/{id}
    // Manually force a collision for testing resource path (singleton vs singleton or singleton vs resource)
    // Let's make exp2 a singleton
    const exp2 = model.exposeEntity({ key: e2.key })
    // Remove collection from exp2 so we can set arbitrary resource path
    // Note: implementation of setResourcePath for collection-less allows any 2 segments
    // We need to simulate the state where hasCollection is false
    exp2.hasCollection = false

    // Set exp1 resource path to something specific
    exp1.hasCollection = false
    exp1.setResourcePath('/shared/path')

    // Try to set exp2 resource path to same
    assert.throws(
      () => exp2.setResourcePath('/shared/path'),
      'Resource path "/shared/path" is already in use by another root entity.'
    )
  })

  test('allows setting non-colliding paths for root entity', ({ assert }) => {
    const domain = new DataDomain()
    domain.info.version = '1.0.0'
    const dm = domain.addModel()
    const e1 = dm.addEntity({ info: { name: 'A' } })

    const model = new ApiModel()
    model.attachDataDomain(domain)

    const exp1 = model.exposeEntity({ key: e1.key }) // /as

    exp1.setCollectionPath('/new-path')
    assert.equal(exp1.collectionPath, '/new-path')

    exp1.hasCollection = false // allow arbitrary resource path
    exp1.setResourcePath('/custom/resource')
    assert.equal(exp1.resourcePath, '/custom/resource')
  })

  test('does not validate collision for non-root entities', ({ 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' } })
    eA.addAssociation({ targets: [{ key: eB.key }], key: 'toB' })

    const model = new ApiModel()
    model.attachDataDomain(domain)

    // expose A -> B
    const rootExp = model.exposeEntity({ key: eA.key }, { followAssociations: true })
    const nestedExp = model.exposes.find((e) => !e.isRoot)

    assert.isDefined(nestedExp)
    // Assuming root entity has collection path /as
    assert.equal(rootExp.collectionPath, '/as')

    // Try to set nested entity's collection path to /as
    // Since it's not root, it should NOT check for collision with rootExp
    // (In reality this path is technically valid relative to parent, but here we just check that
    // it doesn't throw the specific root collision error)

    // setCollectionPath logic for non-root:
    // It doesn't check checks.

    try {
      nestedExp?.setCollectionPath('/as')
    } catch (e) {
      assert.notInclude((e as Error).message, 'already in use by another root entity')
    }
  })
})
