import { test } from '@japa/runner'
import {
  SemanticType,
  SemanticTiming,
  SemanticOperation,
  getSemanticsForOperation,
  canSemanticBeDisabled,
  type AppliedDataSemantic,
} from '../../../src/modeling/Semantics.js'

test.group('Semantic Runtime Control', () => {
  test('getSemanticsForOperation should filter semantics by operation and timing', ({ assert }) => {
    const semantics: AppliedDataSemantic[] = [
      { id: SemanticType.Password },
      { id: SemanticType.CreatedTimestamp },
      { id: SemanticType.UpdatedTimestamp },
      { id: SemanticType.Status },
      { id: SemanticType.Title },
    ]

    // Test CREATE operation with BEFORE timing
    const createBeforeSemantics = getSemanticsForOperation(semantics, SemanticOperation.Create, SemanticTiming.Before)
    const createBeforeTypes = createBeforeSemantics.map((s) => s.id)

    assert.includeMembers(createBeforeTypes, [SemanticType.Password, SemanticType.CreatedTimestamp])
    assert.notInclude(createBeforeTypes, SemanticType.UpdatedTimestamp) // Only runs on UPDATE
    assert.notInclude(createBeforeTypes, SemanticType.Title) // No runtime operations

    // Test UPDATE operation with BEFORE timing
    const updateBeforeSemantics = getSemanticsForOperation(semantics, SemanticOperation.Update, SemanticTiming.Before)
    const updateBeforeTypes = updateBeforeSemantics.map((s) => s.id)

    assert.includeMembers(updateBeforeTypes, [SemanticType.Password, SemanticType.UpdatedTimestamp])
    assert.notInclude(updateBeforeTypes, SemanticType.CreatedTimestamp) // Only runs on CREATE

    // Test Status semantic runs on both CREATE and UPDATE
    assert.include(createBeforeTypes, SemanticType.Status)
    assert.include(updateBeforeTypes, SemanticType.Status)
  })

  test('getSemanticsForOperation should sort semantics by priority', ({ assert }) => {
    const semantics: AppliedDataSemantic[] = [
      { id: SemanticType.Password }, // priority: 10
      { id: SemanticType.ResourceOwnerIdentifier }, // priority: 5
      { id: SemanticType.UserRole }, // priority: 20
    ]

    const createSemantics = getSemanticsForOperation(semantics, SemanticOperation.Create, SemanticTiming.Before)

    // Should be sorted by priority: ResourceOwnerIdentifier (5), Password (10), UserRole (20)
    assert.equal(createSemantics[0].id, SemanticType.ResourceOwnerIdentifier)
    assert.equal(createSemantics[1].id, SemanticType.Password)
    assert.equal(createSemantics[2].id, SemanticType.UserRole)
  })

  test('getSemanticsForOperation should handle BOTH timing correctly', ({ assert }) => {
    const semantics: AppliedDataSemantic[] = [
      { id: SemanticType.Status }, // timing: Both
      { id: SemanticType.Password }, // timing: Before
    ]

    const beforeSemantics = getSemanticsForOperation(semantics, SemanticOperation.Create, SemanticTiming.Before)
    const afterSemantics = getSemanticsForOperation(semantics, SemanticOperation.Create, SemanticTiming.After)

    // Status should appear in both BEFORE and AFTER
    assert.equal(beforeSemantics.length, 2) // Status and Password
    assert.equal(afterSemantics.length, 1) // Only Status
    assert.equal(afterSemantics[0].id, SemanticType.Status)
  })

  test('canSemanticBeDisabled should check if semantic can be disabled', ({ assert }) => {
    // Security semantics cannot be disabled
    assert.isFalse(canSemanticBeDisabled(SemanticType.Password))
    assert.isFalse(canSemanticBeDisabled(SemanticType.ResourceOwnerIdentifier))

    // Other semantics can be disabled (default)
    assert.isTrue(canSemanticBeDisabled(SemanticType.CreatedTimestamp))
    assert.isTrue(canSemanticBeDisabled(SemanticType.Email))
    assert.isTrue(canSemanticBeDisabled(SemanticType.Status))
  })

  test('getSemanticsForOperation should return empty array for semantics with no runtime', ({ assert }) => {
    const semantics: AppliedDataSemantic[] = [
      { id: SemanticType.Title }, // No runtime operations
      { id: SemanticType.Description }, // No runtime operations
      { id: SemanticType.User }, // No runtime operations
    ]

    const result = getSemanticsForOperation(semantics, SemanticOperation.Create, SemanticTiming.Before)
    assert.lengthOf(result, 0)
  })

  test('getSemanticsForOperation should filter by specific operations', ({ assert }) => {
    const semantics: AppliedDataSemantic[] = [
      { id: SemanticType.CreatedTimestamp }, // Only CREATE
      { id: SemanticType.UpdatedTimestamp }, // Only UPDATE
      { id: SemanticType.DeletedTimestamp }, // Only DELETE
    ]

    const createSemantics = getSemanticsForOperation(semantics, SemanticOperation.Create, SemanticTiming.Before)
    const updateSemantics = getSemanticsForOperation(semantics, SemanticOperation.Update, SemanticTiming.Before)
    const deleteSemantics = getSemanticsForOperation(semantics, SemanticOperation.Delete, SemanticTiming.Before)

    assert.lengthOf(createSemantics, 1)
    assert.equal(createSemantics[0].id, SemanticType.CreatedTimestamp)

    assert.lengthOf(updateSemantics, 1)
    assert.equal(updateSemantics[0].id, SemanticType.UpdatedTimestamp)

    assert.lengthOf(deleteSemantics, 1)
    assert.equal(deleteSemantics[0].id, SemanticType.DeletedTimestamp)
  })
})
