import { test } from '@japa/runner'
import { Permission } from '../../../../src/mocking/lib/Permission.js'
import { Kind as PermissionKind } from '../../../../src/models/store/Permission.js'
import type { IPermission } from '../../../../src/models/store/Permission.js'

test.group('permission()', (group) => {
  let permission: Permission

  group.each.setup(() => {
    permission = new Permission()
  })

  test('returns an object', ({ assert }) => {
    const result = permission.permission()
    assert.typeOf(result, 'object')
  })

  test('has the {property} property of a type {type}')
    .with([
      { property: 'kind', type: 'string' },
      { property: 'key', type: 'string' },
      { property: 'type', type: 'string' },
      { property: 'granteeId', type: 'string' },
      { property: 'itemId', type: 'string' },
      { property: 'role', type: 'string' },
      { property: 'addingUser', type: 'string' },
      { property: 'depth', type: 'number' },
      { property: 'sourceRule', type: 'string' },
    ])
    .run(({ assert }, { property, type }) => {
      const result = permission.permission()
      assert.typeOf(result[property as keyof IPermission], type)
    })

  test('has the correct kind', ({ assert }) => {
    const result = permission.permission()
    assert.equal(result.kind, PermissionKind)
  })

  test('type is one of the valid values', ({ assert }) => {
    const result = permission.permission()
    assert.include(['user', 'group', 'organization'], result.type)
  })

  test('role is one of the valid values', ({ assert }) => {
    const result = permission.permission()
    assert.include(['reader', 'commenter', 'writer', 'owner'], result.role)
  })

  test('sourceRule is one of the valid values', ({ assert }) => {
    const result = permission.permission()
    assert.include(['direct_user_grant', 'creator_default_owner', 'parent_owner_editor_rule'], result.sourceRule)
  })

  test('uses passed key', ({ assert }) => {
    const result = permission.permission({ key: 'custom-key' })
    assert.equal(result.key, 'custom-key')
  })

  test('uses passed type', ({ assert }) => {
    const result = permission.permission({ type: 'group' })
    assert.equal(result.type, 'group')
  })

  test('uses passed granteeId', ({ assert }) => {
    const result = permission.permission({ granteeId: 'grantee-123' })
    assert.equal(result.granteeId, 'grantee-123')
  })

  test('uses passed itemId', ({ assert }) => {
    const result = permission.permission({ itemId: 'item-456' })
    assert.equal(result.itemId, 'item-456')
  })

  test('uses passed role', ({ assert }) => {
    const result = permission.permission({ role: 'writer' })
    assert.equal(result.role, 'writer')
  })

  test('uses passed addingUser', ({ assert }) => {
    const result = permission.permission({ addingUser: 'user-789' })
    assert.equal(result.addingUser, 'user-789')
  })

  test('uses passed depth', ({ assert }) => {
    const result = permission.permission({ depth: 2 })
    assert.equal(result.depth, 2)
  })

  test('uses passed sourceRule', ({ assert }) => {
    const result = permission.permission({ sourceRule: 'creator_default_owner' })
    assert.equal(result.sourceRule, 'creator_default_owner')
  })

  test('generates displayName by default', ({ assert }) => {
    const result = permission.permission()
    assert.typeOf(result.displayName, 'string')
  })

  test('uses passed displayName', ({ assert }) => {
    const result = permission.permission({ displayName: 'John Doe' })
    assert.equal(result.displayName, 'John Doe')
  })

  test('can explicitly set displayName to undefined', ({ assert }) => {
    const result = permission.permission({ displayName: undefined })
    assert.isUndefined(result.displayName)
  })

  test('generates expirationTime for user type by default', ({ assert }) => {
    const result = permission.permission({ type: 'user' })
    assert.typeOf(result.expirationTime, 'number')
  })

  test('generates expirationTime for group type by default', ({ assert }) => {
    const result = permission.permission({ type: 'group' })
    assert.typeOf(result.expirationTime, 'number')
  })

  test('does not generate expirationTime for organization type', ({ assert }) => {
    const result = permission.permission({ type: 'organization' })
    assert.isUndefined(result.expirationTime)
  })

  test('uses passed expirationTime', ({ assert }) => {
    const time = Date.now() + 86400000
    const result = permission.permission({ expirationTime: time })
    assert.equal(result.expirationTime, time)
  })

  test('can explicitly set expirationTime to undefined', ({ assert }) => {
    const result = permission.permission({ type: 'user', expirationTime: undefined })
    assert.isUndefined(result.expirationTime)
  })
})

test.group('userPermission()', (group) => {
  let permission: Permission

  group.each.setup(() => {
    permission = new Permission()
  })

  test('returns an object', ({ assert }) => {
    const result = permission.userPermission()
    assert.typeOf(result, 'object')
  })

  test('has type set to user', ({ assert }) => {
    const result = permission.userPermission()
    assert.equal(result.type, 'user')
  })

  test('has displayName', ({ assert }) => {
    const result = permission.userPermission()
    assert.typeOf(result.displayName, 'string')
  })

  test('has expirationTime', ({ assert }) => {
    const result = permission.userPermission()
    assert.typeOf(result.expirationTime, 'number')
  })

  test('uses passed init values', ({ assert }) => {
    const result = permission.userPermission({ role: 'owner', itemId: 'item-123' })
    assert.equal(result.type, 'user')
    assert.equal(result.role, 'owner')
    assert.equal(result.itemId, 'item-123')
  })
})

test.group('groupPermission()', (group) => {
  let permission: Permission

  group.each.setup(() => {
    permission = new Permission()
  })

  test('returns an object', ({ assert }) => {
    const result = permission.groupPermission()
    assert.typeOf(result, 'object')
  })

  test('has type set to group', ({ assert }) => {
    const result = permission.groupPermission()
    assert.equal(result.type, 'group')
  })

  test('has displayName', ({ assert }) => {
    const result = permission.groupPermission()
    assert.typeOf(result.displayName, 'string')
  })

  test('has expirationTime', ({ assert }) => {
    const result = permission.groupPermission()
    assert.typeOf(result.expirationTime, 'number')
  })

  test('uses passed init values', ({ assert }) => {
    const result = permission.groupPermission({ role: 'commenter', granteeId: 'group-456' })
    assert.equal(result.type, 'group')
    assert.equal(result.role, 'commenter')
    assert.equal(result.granteeId, 'group-456')
  })
})

test.group('organizationPermission()', (group) => {
  let permission: Permission

  group.each.setup(() => {
    permission = new Permission()
  })

  test('returns an object', ({ assert }) => {
    const result = permission.organizationPermission()
    assert.typeOf(result, 'object')
  })

  test('has type set to organization', ({ assert }) => {
    const result = permission.organizationPermission()
    assert.equal(result.type, 'organization')
  })

  test('has displayName', ({ assert }) => {
    const result = permission.organizationPermission()
    assert.typeOf(result.displayName, 'string')
  })

  test('does not have expirationTime', ({ assert }) => {
    const result = permission.organizationPermission()
    assert.isUndefined(result.expirationTime)
  })

  test('uses passed init values', ({ assert }) => {
    const result = permission.organizationPermission({ role: 'reader', addingUser: 'admin-789' })
    assert.equal(result.type, 'organization')
    assert.equal(result.role, 'reader')
    assert.equal(result.addingUser, 'admin-789')
  })
})

test.group('permissions()', (group) => {
  let permission: Permission

  group.each.setup(() => {
    permission = new Permission()
  })

  test('returns an array', ({ assert }) => {
    const result = permission.permissions()
    assert.isArray(result)
  })

  test('returns 25 items by default', ({ assert }) => {
    const result = permission.permissions()
    assert.lengthOf(result, 25)
  })

  test('returns specified number of items', ({ assert }) => {
    const result = permission.permissions(10)
    assert.lengthOf(result, 10)
  })

  test('all items have the correct kind', ({ assert }) => {
    const result = permission.permissions(5)
    result.forEach((item) => {
      assert.equal(item.kind, PermissionKind)
    })
  })

  test('uses passed init values for all items', ({ assert }) => {
    const result = permission.permissions(5, { type: 'user', role: 'owner' })
    result.forEach((item) => {
      assert.equal(item.type, 'user')
      assert.equal(item.role, 'owner')
    })
  })

  test('generates unique keys', ({ assert }) => {
    const result = permission.permissions(10)
    const keys = result.map((item) => item.key)
    const uniqueKeys = new Set(keys)
    assert.equal(keys.length, uniqueKeys.size)
  })
})
