import { test } from '@japa/runner'
import { IPermission, Permission, PermissionKind, PermissionRole } from '../../../../src/index.js'

test.group('Permission', () => {
  test('creates a default Permission', ({ assert }) => {
    const result = new Permission()
    assert.equal(result.kind, PermissionKind)
    assert.typeOf(result.key, 'string')
    assert.isNotEmpty(result.key)
    assert.equal(result.addingUser, '')
    assert.equal(result.role, 'reader')
    assert.equal(result.type, 'user')
    assert.isUndefined(result.displayName)
    assert.isUndefined(result.expirationTime)
    assert.equal(result.granteeId, '')
  })

  test('creates permissions from the schema', ({ assert }) => {
    const schema: IPermission = {
      kind: PermissionKind,
      key: '1',
      addingUser: '2',
      itemId: '3',
      granteeId: '4',
      role: 'commenter',
      type: 'group',
      displayName: 'Name',
      expirationTime: 321,
      depth: 3,
      sourceRule: 'direct_user_grant',
    }
    const result = new Permission(schema)
    assert.equal(result.kind, PermissionKind)
    assert.equal(result.key, '1')
    assert.equal(result.addingUser, '2')
    assert.equal(result.itemId, '3')
    assert.equal(result.granteeId, '4')
    assert.equal(result.role, 'commenter')
    assert.equal(result.type, 'group')
    assert.equal(result.displayName, 'Name')
    assert.strictEqual(result.expirationTime, 321)
    assert.strictEqual(result.depth, 3)
    assert.strictEqual(result.sourceRule, 'direct_user_grant')
  })

  test('creates permissions from the JSON schema', ({ assert }) => {
    const schema: IPermission = {
      kind: PermissionKind,
      key: '1',
      addingUser: '2',
      itemId: '3',
      granteeId: '4',
      role: 'commenter',
      type: 'group',
      displayName: 'Name',
      expirationTime: 321,
      depth: 3,
      sourceRule: 'parent_owner_editor_rule',
    }
    const result = new Permission(JSON.stringify(schema))
    assert.equal(result.kind, PermissionKind)
    assert.equal(result.key, '1')
    assert.equal(result.addingUser, '2')
    assert.equal(result.itemId, '3')
    assert.equal(result.granteeId, '4')
    assert.equal(result.role, 'commenter')
    assert.equal(result.type, 'group')
    assert.equal(result.displayName, 'Name')
    assert.strictEqual(result.expirationTime, 321)
    assert.strictEqual(result.depth, 3)
    assert.strictEqual(result.sourceRule, 'parent_owner_editor_rule')
  })
})

test.group('Permission.fromUserRole', () => {
  test('creates basic meta', ({ assert }) => {
    const result = Permission.fromUserRole('owner', '3', '1', '2')
    assert.equal(result.kind, PermissionKind)
    assert.typeOf(result.key, 'string')
    assert.isNotEmpty(result.key)
    assert.isUndefined(result.displayName)
    assert.isUndefined(result.expirationTime)
  })

  test('sets the type', ({ assert }) => {
    const result = Permission.fromUserRole('owner', '3', '1', '2')
    assert.equal(result.type, 'user')
  })

  test('sets the role', ({ assert }) => {
    const result = Permission.fromUserRole('owner', '3', '1', '2')
    assert.equal(result.role, 'owner')
  })

  test('sets the owner', ({ assert }) => {
    const result = Permission.fromUserRole('owner', '3', '1', '2')
    assert.equal(result.granteeId, '1')
  })

  test('sets the addingUser', ({ assert }) => {
    const result = Permission.fromUserRole('owner', '3', '1', '2')
    assert.equal(result.addingUser, '2')
  })

  test('sets the itemId', ({ assert }) => {
    const result = Permission.fromUserRole('owner', '3', '1', '2')
    assert.equal(result.itemId, '3')
  })

  test('sets the depth', ({ assert }) => {
    const result = Permission.fromUserRole('owner', '3', '1', '2')
    assert.equal(result.depth, 0)
  })

  test('sets the sourceRule', ({ assert }) => {
    const result = Permission.fromUserRole('owner', '3', '1', '2')
    assert.equal(result.sourceRule, 'direct_user_grant')
  })
})

test.group('Permission.fromGroupRole', () => {
  test('creates basic meta', ({ assert }) => {
    const result = Permission.fromGroupRole('owner', '3', '1', '2')
    assert.equal(result.kind, PermissionKind)
    assert.typeOf(result.key, 'string')
    assert.isNotEmpty(result.key)
    assert.isUndefined(result.displayName)
    assert.isUndefined(result.expirationTime)
  })

  test('sets the type', ({ assert }) => {
    const result = Permission.fromGroupRole('owner', '3', '1', '2')
    assert.equal(result.type, 'group')
  })

  test('sets the role', ({ assert }) => {
    const result = Permission.fromGroupRole('owner', '3', '1', '2')
    assert.equal(result.role, 'owner')
  })

  test('sets the owner', ({ assert }) => {
    const result = Permission.fromGroupRole('owner', '3', '1', '2')
    assert.equal(result.granteeId, '1')
  })

  test('sets the addingUser', ({ assert }) => {
    const result = Permission.fromGroupRole('owner', '3', '1', '2')
    assert.equal(result.addingUser, '2')
  })

  test('sets the itemId', ({ assert }) => {
    const result = Permission.fromGroupRole('owner', '3', '1', '2')
    assert.equal(result.itemId, '3')
  })

  test('sets the depth', ({ assert }) => {
    const result = Permission.fromUserRole('owner', '3', '1', '2')
    assert.equal(result.depth, 0)
  })

  test('sets the sourceRule', ({ assert }) => {
    const result = Permission.fromUserRole('owner', '3', '1', '2')
    assert.equal(result.sourceRule, 'direct_user_grant')
  })
})

test.group('Permission.fromOrganizationRole', () => {
  test('creates basic meta', ({ assert }) => {
    const result = Permission.fromOrganizationRole('owner', '3', '1', '2')
    assert.equal(result.kind, PermissionKind)
    assert.typeOf(result.key, 'string')
    assert.isNotEmpty(result.key)
    assert.isUndefined(result.displayName)
    assert.isUndefined(result.expirationTime)
  })

  test('sets the type', ({ assert }) => {
    const result = Permission.fromOrganizationRole('owner', '3', '1', '2')
    assert.equal(result.type, 'organization')
  })

  test('sets the role', ({ assert }) => {
    const result = Permission.fromOrganizationRole('owner', '3', '1', '2')
    assert.equal(result.role, 'owner')
  })

  test('sets the addingUser', ({ assert }) => {
    const result = Permission.fromOrganizationRole('owner', '3', '1', '2')
    assert.equal(result.addingUser, '2')
  })

  test('sets the granteeId', ({ assert }) => {
    const result = Permission.fromOrganizationRole('owner', '3', '1', '2')
    assert.equal(result.granteeId, '1')
  })

  test('sets the itemId', ({ assert }) => {
    const result = Permission.fromOrganizationRole('owner', '3', '1', '2')
    assert.equal(result.itemId, '3')
  })

  test('sets the depth', ({ assert }) => {
    const result = Permission.fromUserRole('owner', '3', '1', '2')
    assert.equal(result.depth, 0)
  })

  test('sets the sourceRule', ({ assert }) => {
    const result = Permission.fromUserRole('owner', '3', '1', '2')
    assert.equal(result.sourceRule, 'direct_user_grant')
  })
})

test.group('Permission.fromValues()', () => {
  test('creates basic meta', ({ assert }) => {
    const result = Permission.fromValues({
      addingUser: '1',
      role: 'commenter',
      type: 'organization',
      granteeId: '2',
      itemId: '3',
      depth: 3,
      sourceRule: 'direct_user_grant',
    })
    assert.equal(result.kind, PermissionKind)
    assert.typeOf(result.key, 'string')
    assert.isNotEmpty(result.key)
    assert.isUndefined(result.displayName)
    assert.isUndefined(result.expirationTime)
    assert.isUndefined(result.expirationTime)
  })

  test('sets the type', ({ assert }) => {
    const result = Permission.fromValues({
      addingUser: '1',
      role: 'commenter',
      type: 'organization',
      granteeId: '2',
      itemId: '3',
      depth: 3,
      sourceRule: 'direct_user_grant',
    })
    assert.equal(result.type, 'organization')
  })

  test('sets the role', ({ assert }) => {
    const result = Permission.fromValues({
      addingUser: '1',
      role: 'commenter',
      type: 'organization',
      granteeId: '2',
      itemId: '3',
      depth: 3,
      sourceRule: 'direct_user_grant',
    })
    assert.equal(result.role, 'commenter')
  })

  test('sets the addingUser', ({ assert }) => {
    const result = Permission.fromValues({
      addingUser: '1',
      role: 'commenter',
      type: 'organization',
      granteeId: '2',
      itemId: '3',
      depth: 3,
      sourceRule: 'direct_user_grant',
    })
    assert.equal(result.addingUser, '1')
  })

  test('sets the granteeId', ({ assert }) => {
    const result = Permission.fromValues({
      addingUser: '1',
      role: 'commenter',
      type: 'organization',
      granteeId: '2',
      itemId: '3',
      depth: 3,
      sourceRule: 'direct_user_grant',
    })
    assert.equal(result.granteeId, '2')
  })

  test('sets the itemId', ({ assert }) => {
    const result = Permission.fromValues({
      addingUser: '1',
      role: 'commenter',
      type: 'organization',
      granteeId: '2',
      itemId: '3',
      depth: 3,
      sourceRule: 'direct_user_grant',
    })
    assert.equal(result.itemId, '3')
  })

  test('sets the depth', ({ assert }) => {
    const result = Permission.fromValues({
      addingUser: '1',
      role: 'commenter',
      type: 'organization',
      granteeId: '2',
      itemId: '3',
      depth: 3,
      sourceRule: 'direct_user_grant',
    })
    assert.equal(result.depth, 3)
  })

  test('sets the sourceRule', ({ assert }) => {
    const result = Permission.fromValues({
      addingUser: '1',
      role: 'commenter',
      type: 'organization',
      granteeId: '2',
      itemId: '3',
      depth: 3,
      sourceRule: 'parent_owner_editor_rule',
    })
    assert.equal(result.sourceRule, 'parent_owner_editor_rule')
  })
})

test.group('Permission.hasRole()', () => {
  test('{$i} checks for role')
    .tags(['@model', '@file'])
    .with([
      ['reader', 'reader', true],
      ['reader', 'commenter', true],
      ['reader', 'writer', true],
      ['reader', 'owner', true],
      ['commenter', 'reader', false],
      ['commenter', 'commenter', true],
      ['commenter', 'writer', true],
      ['commenter', 'owner', true],
      ['writer', 'reader', false],
      ['writer', 'commenter', false],
      ['writer', 'writer', true],
      ['writer', 'owner', true],
      ['owner', 'reader', false],
      ['owner', 'commenter', false],
      ['owner', 'writer', false],
      ['owner', 'owner', true],
      ['reader', undefined, false],
      ['reader', undefined, false],
      ['reader', undefined, false],
      ['reader', undefined, false],
    ])
    .run(({ assert }, row) => {
      const result = Permission.hasRole(row[0] as PermissionRole, row[1] as PermissionRole)
      assert.strictEqual(result, row[2])
    })
})

test.group('Permission.isHigherRole', () => {
  test('returns true for owner > writer', ({ assert }) => {
    assert.isTrue(Permission.isHigherRole('owner', 'writer'))
  })

  test('returns true for writer > commenter', ({ assert }) => {
    assert.isTrue(Permission.isHigherRole('writer', 'commenter'))
  })

  test('returns true for commenter > reader', ({ assert }) => {
    assert.isTrue(Permission.isHigherRole('commenter', 'reader'))
  })

  test('returns false for writer > owner', ({ assert }) => {
    assert.isFalse(Permission.isHigherRole('writer', 'owner'))
  })

  test('returns false for commenter > writer', ({ assert }) => {
    assert.isFalse(Permission.isHigherRole('commenter', 'writer'))
  })

  test('returns false for reader > commenter', ({ assert }) => {
    assert.isFalse(Permission.isHigherRole('reader', 'commenter'))
  })

  test('returns false for equal roles', ({ assert }) => {
    assert.isFalse(Permission.isHigherRole('reader', 'reader'))
    assert.isFalse(Permission.isHigherRole('commenter', 'commenter'))
    assert.isFalse(Permission.isHigherRole('writer', 'writer'))
    assert.isFalse(Permission.isHigherRole('owner', 'owner'))
  })
})

test.group('toJSON()', () => {
  test('serializes all required values', ({ assert }) => {
    const p = Permission.fromOrganizationRole('commenter', '3', '1', '2')
    const result = p.toJSON()
    assert.equal(result.kind, PermissionKind)
    assert.typeOf(result.key, 'string')
    assert.equal(result.role, 'commenter')
    assert.equal(result.type, 'organization')
    assert.equal(result.addingUser, '2')
    assert.isUndefined(result.displayName)
    assert.isUndefined(result.expirationTime)
  })

  test('serializes the itemId', ({ assert }) => {
    const p = Permission.fromUserRole('commenter', '3', '1', '2')
    const result = p.toJSON()
    assert.equal(result.itemId, '3')
  })

  test('serializes the granteeId', ({ assert }) => {
    const p = Permission.fromUserRole('commenter', '3', '1', '2')
    const result = p.toJSON()
    assert.equal(result.granteeId, '1')
  })

  test('serializes the displayName', ({ assert }) => {
    const p = Permission.fromUserRole('commenter', '3', '1', '2')
    p.displayName = 'test'
    const result = p.toJSON()
    assert.equal(result.displayName, 'test')
  })

  test('serializes the expirationTime', ({ assert }) => {
    const p = Permission.fromUserRole('commenter', '3', '1', '2')
    p.expirationTime = 1234
    const result = p.toJSON()
    assert.equal(result.expirationTime, 1234)
  })
})
