import { test } from '@japa/runner'
import { Action, AccessRule, RateLimitingConfiguration } from '../../../../src/modeling/index.js'
import { type ActionSchema } from '../../../../src/modeling/actions/Action.js'

test.group('Action', () => {
  test('initializes with default values', ({ assert }) => {
    const action = new Action()
    assert.equal(action.kind, '')
    assert.deepEqual(action.accessRule, [])
    assert.isUndefined(action.rateLimiting)
  }).tags(['@modeling', '@action'])

  test('initializes with provided values', ({ assert }) => {
    const schema: ActionSchema = {
      kind: 'read',
      accessRule: [{ type: 'allowPublic' }],
      rateLimiting: { rules: [] },
    }
    const action = new Action(schema)
    assert.equal(action.kind, 'read')
    assert.lengthOf(action.accessRule, 1)
    assert.instanceOf(action.accessRule[0], AccessRule)
    assert.equal(action.accessRule[0].type, 'allowPublic')
    assert.instanceOf(action.rateLimiting, RateLimitingConfiguration)
  }).tags(['@modeling', '@action'])

  test('serializes to JSON', ({ assert }) => {
    const action = new Action({
      kind: 'write',
      accessRule: [{ type: 'allowPublic' }],
      rateLimiting: { rules: [] },
    })
    const json = action.toJSON()

    assert.equal(json.kind, 'write')
    assert.deepEqual(json.accessRule, [{ type: 'allowPublic' }])
    assert.deepEqual(json.rateLimiting, { rules: [] })
  }).tags(['@modeling', '@action'])

  test('notifies change when kind changes', async ({ assert }) => {
    const action = new Action({ kind: 'read' })
    let notified = false
    action.addEventListener('change', () => {
      notified = true
    })

    action.kind = 'write'
    // allow microtask to process the change
    await Promise.resolve()

    assert.isTrue(notified)
  }).tags(['@modeling', '@action', '@observed'])

  test('notifies change when accessRule array is replaced', async ({ assert }) => {
    const action = new Action()
    let notified = false
    action.addEventListener('change', () => {
      notified = true
    })

    action.accessRule = [new AccessRule({ type: 'allowPublic' })]
    await Promise.resolve()
    assert.isTrue(notified)
  }).tags(['@modeling', '@action', '@observed'])

  test('notifies change when rateLimiting is replaced', async ({ assert }) => {
    const action = new Action()
    let notified = false
    action.addEventListener('change', () => {
      notified = true
    })

    action.rateLimiting = new RateLimitingConfiguration()
    await Promise.resolve()
    assert.isTrue(notified)
  }).tags(['@modeling', '@action', '@observed'])

  test('constructor copies accessRule array (immutability)', ({ assert }) => {
    const rules = [{ type: 'allowPublic' }]
    const action = new Action({ kind: 'read', accessRule: rules })

    // Modify original array
    rules.push({ type: 'other' })
    rules[0].type = 'changed'

    assert.lengthOf(action.accessRule, 1)
    assert.equal(action.accessRule[0].type, 'allowPublic')
  }).tags(['@modeling', '@action', '@immutability'])

  test('toJSON returns safe copy', ({ assert }) => {
    const action = new Action({
      kind: 'read',
      accessRule: [{ type: 'allowPublic' }],
      rateLimiting: { rules: [] },
    })
    const json = action.toJSON()

    // Modify JSON
    json.kind = 'write'
    if (json.accessRule) {
      json.accessRule[0].type = 'changed'
      json.accessRule.push({ type: 'new' })
    }

    assert.equal(action.kind, 'read')
    assert.lengthOf(action.accessRule, 1)
    assert.equal(action.accessRule[0].type, 'allowPublic')
  }).tags(['@modeling', '@action', '@immutability'])
})
