import { test } from '@japa/runner'
import { DataDomain } from '../../../src/modeling/DataDomain.js'
import { DomainEntityKind, DomainNamespaceKind, DomainPropertyKind } from '../../../src/models/kinds.js'

test.group('DataDomain Search', () => {
  test('should return all nodes when no search criteria provided', ({ assert }) => {
    const domain = new DataDomain({
      key: 'test-domain',
      info: { name: 'Test Domain' },
    })

    const namespace = domain.addNamespace({ info: { name: 'TestNamespace' } })
    const model = namespace.addModel({ info: { name: 'TestModel' } })
    const entity = model.addEntity({ info: { name: 'TestEntity' } })
    entity.addProperty({ info: { name: 'testProperty' }, type: 'string' })

    const results = domain.search()

    assert.isAtLeast(results.length, 4) // namespace, model, entity, property
    assert.isTrue(results.every((result) => result.matchedFields.length === 0))
  })

  test('should search by text query in name field', ({ assert }) => {
    const domain = new DataDomain({
      key: 'test-domain',
      info: { name: 'Test Domain' },
    })

    const namespace = domain.addNamespace({ info: { name: 'UserNamespace' } })
    const model = namespace.addModel({ info: { name: 'UserModel' } })
    const entity = model.addEntity({ info: { name: 'User' } })
    entity.addProperty({ info: { name: 'email' }, type: 'string' })

    const results = domain.search({ query: 'User' })

    assert.equal(results.length, 3) // namespace, model, entity
    assert.isTrue(results.every((result) => result.matchedFields.includes('name')))
  })

  test('should search by text query in displayName field', ({ assert }) => {
    const domain = new DataDomain({
      key: 'test-domain',
      info: { name: 'Test Domain' },
    })

    domain.addNamespace({ info: { name: 'TestNamespace', displayName: 'User Management' } })

    const results = domain.search({ query: 'Management' })

    assert.equal(results.length, 1)
    assert.equal(results[0].node.kind, DomainNamespaceKind)
    assert.isTrue(results[0].matchedFields.includes('displayName'))
  })

  test('should search by text query in description field', ({ assert }) => {
    const domain = new DataDomain({
      key: 'test-domain',
      info: { name: 'Test Domain' },
    })

    domain.addNamespace({
      info: { name: 'TestNamespace', description: 'Handles user authentication' },
    })

    const results = domain.search({ query: 'authentication' })

    assert.equal(results.length, 1)
    assert.equal(results[0].node.kind, DomainNamespaceKind)
    assert.isTrue(results[0].matchedFields.includes('description'))
  })

  test('should filter by node type', ({ assert }) => {
    const domain = new DataDomain({
      key: 'test-domain',
      info: { name: 'Test Domain' },
    })

    const namespace = domain.addNamespace({ info: { name: 'UserNamespace' } })
    const model = namespace.addModel({ info: { name: 'UserModel' } })
    const entity = model.addEntity({ info: { name: 'User' } })
    entity.addProperty({ info: { name: 'userId' }, type: 'string' })

    const entityResults = domain.search({ nodeTypes: [DomainEntityKind] })
    const propertyResults = domain.search({ nodeTypes: [DomainPropertyKind] })

    assert.equal(entityResults.length, 1)
    assert.equal(entityResults[0].node.kind, DomainEntityKind)

    assert.equal(propertyResults.length, 1)
    assert.equal(propertyResults[0].node.kind, DomainPropertyKind)
  })

  test('should combine text query and node type filters', ({ assert }) => {
    const domain = new DataDomain({
      key: 'test-domain',
      info: { name: 'Test Domain' },
    })

    const namespace = domain.addNamespace({ info: { name: 'UserNamespace' } })
    const model = namespace.addModel({ info: { name: 'UserModel' } })
    const entity = model.addEntity({ info: { name: 'User' } })
    entity.addProperty({ info: { name: 'userName' }, type: 'string' })

    const results = domain.search({
      query: 'User',
      nodeTypes: [DomainEntityKind, DomainPropertyKind],
    })

    assert.equal(results.length, 2) // entity and property
    assert.isTrue(
      results.every((result) => result.node.kind === DomainEntityKind || result.node.kind === DomainPropertyKind)
    )
  })

  test('should support case-sensitive search', ({ assert }) => {
    const domain = new DataDomain({
      key: 'test-domain',
      info: { name: 'Test Domain' },
    })

    domain.addNamespace({ info: { name: 'user' } })
    domain.addNamespace({ info: { name: 'User' } })

    const caseSensitiveResults = domain.search({ query: 'User', caseSensitive: true })
    const caseInsensitiveResults = domain.search({ query: 'User', caseSensitive: false })

    assert.equal(caseSensitiveResults.length, 1)
    assert.equal(caseInsensitiveResults.length, 2)
  })

  test('should support regex queries', ({ assert }) => {
    const domain = new DataDomain({
      key: 'test-domain',
      info: { name: 'Test Domain' },
    })

    domain.addNamespace({ info: { name: 'user1' } })
    domain.addNamespace({ info: { name: 'user2' } })
    domain.addNamespace({ info: { name: 'admin' } })

    const results = domain.search({ query: /^user\d+$/ })

    assert.equal(results.length, 2)
    assert.isTrue(results.every((result) => /^user\d+$/.test(result.node.info.name || '')))
  })

  test('should identify foreign nodes correctly', ({ assert }) => {
    const foreignDomain = new DataDomain({
      key: 'foreign-domain',
      info: { name: 'Foreign Domain', version: '1.0.0' },
    })
    foreignDomain.addNamespace({ info: { name: 'ForeignNamespace' } })

    const domain = new DataDomain({
      key: 'main-domain',
      info: { name: 'Main Domain' },
    })
    domain.registerForeignDomain(foreignDomain)

    const resultsWithoutForeign = domain.search({ query: 'Foreign' })
    const resultsWithForeign = domain.search({ query: 'Foreign', includeForeignDomains: true })

    assert.equal(resultsWithoutForeign.length, 0)
    assert.equal(resultsWithForeign.length, 1)
    assert.isTrue(resultsWithForeign[0].isForeign)
  })

  test('should handle multiple matched fields', ({ assert }) => {
    const domain = new DataDomain({
      key: 'test-domain',
      info: { name: 'Test Domain' },
    })

    domain.addNamespace({
      info: {
        name: 'TestNamespace',
        displayName: 'Test Display',
        description: 'Test description',
      },
    })

    const results = domain.search({ query: 'Test' })

    assert.equal(results.length, 1)
    assert.equal(results[0].matchedFields.length, 3)
    assert.includeMembers(results[0].matchedFields, ['name', 'displayName', 'description'])
  })
})
