import { test } from '@japa/runner'
import { IPermission, Permission, PermissionRole, PermissionType } from '../../../../src/models/store/Permission.js'

test.group('Permission.hasRoleIn()', () => {
  const createPerm = (role: PermissionRole, depth: number, type: PermissionType = 'user', id = 'id'): IPermission => ({
    kind: 'Core#Permission',
    key: 'key',
    role,
    depth,
    type,
    granteeId: id,
    itemId: 'item',
    addingUser: 'user',
    sourceRule: 'direct_user_grant',
  })

  test('returns false for empty permissions', ({ assert }) => {
    assert.isFalse(Permission.hasRoleIn('reader', []))
  })

  test('returns true when user has direct role meeting requirement', ({ assert }) => {
    const perms = [createPerm('writer', 0)]
    assert.isTrue(Permission.hasRoleIn('reader', perms))
    assert.isTrue(Permission.hasRoleIn('writer', perms))
  })

  test('returns false when user does not meet requirement', ({ assert }) => {
    const perms = [createPerm('reader', 0)]
    assert.isFalse(Permission.hasRoleIn('writer', perms))
  })

  test('prioritizes closer depth (depth 0 overrides depth 1)', ({ assert }) => {
    // Depth 0: Reader (Direct), Depth 1: Owner (Inherited)
    // Expectation: Reader wins (Specificity of depth) -> returns false for Writer check
    const perms = [createPerm('reader', 0), createPerm('owner', 1)]
    assert.isFalse(Permission.hasRoleIn('writer', perms))
    assert.isTrue(Permission.hasRoleIn('reader', perms))
  })

  test('prioritizes closer depth (depth 1 overrides depth 2)', ({ assert }) => {
    // Depth 1: Writer, Depth 2: Reader
    // Expectation: Writer wins
    const perms = [createPerm('writer', 1), createPerm('reader', 2)]
    assert.isTrue(Permission.hasRoleIn('writer', perms))
  })

  test('prioritizes user over group at same depth', ({ assert }) => {
    // Depth 0: User=Reader, Group=Owner
    // Expectation: User wins -> Reader
    const perms = [createPerm('reader', 0, 'user'), createPerm('owner', 0, 'group')]
    assert.isFalse(Permission.hasRoleIn('writer', perms))
  })

  test('prioritizes group over organization at same depth', ({ assert }) => {
    // Depth 0: Group=Reader, Org=Owner
    // Expectation: Group wins -> Reader
    const perms = [createPerm('reader', 0, 'group'), createPerm('owner', 0, 'organization')]
    assert.isFalse(Permission.hasRoleIn('writer', perms))
  })

  test('uses highest role when multiple groups at same depth', ({ assert }) => {
    // Depth 0: GroupA=Reader, GroupB=Writer
    // Expectation: Writer wins (Union)
    const perms = [createPerm('reader', 0, 'group', 'A'), createPerm('writer', 0, 'group', 'B')]
    assert.isTrue(Permission.hasRoleIn('writer', perms))
  })

  test('uses highest role when multiple users at same depth (theoretical)', ({ assert }) => {
    // Depth 0: User=Reader, User=Writer
    // Expectation: Writer wins
    const perms = [createPerm('reader', 0, 'user'), createPerm('writer', 0, 'user')]
    assert.isTrue(Permission.hasRoleIn('writer', perms))
  })

  test('ignores deeper permissions even if they are more specific types', ({ assert }) => {
    // Depth 0: Org=Reader
    // Depth 1: User=Owner
    // Expectation: Depth 0 (Org=Reader) wins because it's closer.
    // User assumption: "closest to the item directly set... should be considered first"
    const perms = [createPerm('reader', 0, 'organization'), createPerm('owner', 1, 'user')]
    assert.isFalse(Permission.hasRoleIn('writer', perms))
  })
})

test.group('Permission.constructor()', () => {
  test('creates a default permission when no input', ({ assert }) => {
    const p = new Permission()
    assert.equal(p.kind, 'Core#Permission')
    assert.typeOf(p.key, 'string')
    assert.equal(p.role, 'reader')
    assert.equal(p.type, 'user')
    assert.equal(p.depth, 0)
  })

  test('creates from string JSON', ({ assert }) => {
    const json = JSON.stringify({
      kind: 'Core#Permission',
      key: 'key',
      role: 'writer',
      type: 'group',
      granteeId: 'group-id',
      itemId: 'item-id',
      addingUser: 'adder',
    })
    const p = new Permission(json)
    assert.equal(p.role, 'writer')
    assert.equal(p.type, 'group')
  })

  test('creates from object', ({ assert }) => {
    const init: IPermission = {
      kind: 'Core#Permission',
      key: 'key',
      role: 'owner',
      type: 'user',
      granteeId: 'user-id',
      itemId: 'item-id',
      addingUser: 'adder',
      depth: 0,
      sourceRule: 'direct_user_grant',
    }
    const p = new Permission(init)
    assert.equal(p.role, 'owner')
    assert.equal(p.granteeId, 'user-id')
  })

  test('throws when input is not a permission', ({ assert }) => {
    assert.throws(() => new Permission({ kind: 'Wrong' } as unknown as IPermission), /Not a permission/)
  })

  test('restores optional fields', ({ assert }) => {
    const init: IPermission = {
      kind: 'Core#Permission',
      key: 'key',
      role: 'owner',
      type: 'user',
      granteeId: 'user-id',
      itemId: 'item-id',
      addingUser: 'adder',
      displayName: 'Name',
      expirationTime: 12345,
      depth: 0,
      sourceRule: 'direct_user_grant',
    }
    const p = new Permission(init)
    assert.equal(p.displayName, 'Name')
    assert.equal(p.expirationTime, 12345)
  })
})

test.group('Permission static factories', () => {
  test('fromUserRole creates valid permission', ({ assert }) => {
    const p = Permission.fromUserRole('writer', 'item', 'user', 'adder')
    assert.equal(p.type, 'user')
    assert.equal(p.role, 'writer')
    assert.equal(p.itemId, 'item')
    assert.equal(p.granteeId, 'user')
    assert.equal(p.addingUser, 'adder')
    assert.typeOf(p.key, 'string')
  })

  test('fromGroupRole creates valid permission', ({ assert }) => {
    const p = Permission.fromGroupRole('commenter', 'item', 'group', 'adder')
    assert.equal(p.type, 'group')
    assert.equal(p.role, 'commenter')
    assert.equal(p.itemId, 'item')
    assert.equal(p.granteeId, 'group')
  })

  test('fromOrganizationRole creates valid permission', ({ assert }) => {
    const p = Permission.fromOrganizationRole('reader', 'item', 'org', 'adder')
    assert.equal(p.type, 'organization')
    assert.equal(p.role, 'reader')
    assert.equal(p.itemId, 'item')
    assert.equal(p.granteeId, 'org')
  })

  test('fromValues creates permission with new key', ({ assert }) => {
    const base = {
      role: 'owner',
      type: 'user',
      granteeId: 'u',
      itemId: 'i',
      addingUser: 'a',
      depth: 0,
      sourceRule: 'direct_user_grant',
    } as unknown as IPermission as unknown as IPermission
    const p = Permission.fromValues(base)
    assert.equal(p.role, 'owner')
    // fromValues generates a new key
    assert.typeOf(p.key, 'string')
  })
})

test.group('Permission.isPermission()', () => {
  test('returns true for valid permission interface', ({ assert }) => {
    const p = { kind: 'Core#Permission' }
    assert.isTrue(Permission.isPermission(p))
  })

  test('returns false for null', ({ assert }) => {
    assert.isFalse(Permission.isPermission(null))
  })

  test('returns false for wrong kind', ({ assert }) => {
    assert.isFalse(Permission.isPermission({ kind: 'Wrong' }))
  })
})

test.group('Permission.hasRole()', () => {
  test('returns false when current role is missing', ({ assert }) => {
    assert.isFalse(Permission.hasRole('reader', undefined))
  })

  test('owner satisfies all', ({ assert }) => {
    assert.isTrue(Permission.hasRole('owner', 'owner'))
    assert.isTrue(Permission.hasRole('writer', 'owner'))
    assert.isTrue(Permission.hasRole('commenter', 'owner'))
    assert.isTrue(Permission.hasRole('reader', 'owner'))
  })

  test('writer satisfies lower', ({ assert }) => {
    assert.isFalse(Permission.hasRole('owner', 'writer'))
    assert.isTrue(Permission.hasRole('writer', 'writer'))
    assert.isTrue(Permission.hasRole('commenter', 'writer'))
    assert.isTrue(Permission.hasRole('reader', 'writer'))
  })

  test('reader only satisfies reader', ({ assert }) => {
    assert.isFalse(Permission.hasRole('writer', 'reader'))
    assert.isTrue(Permission.hasRole('reader', 'reader'))
  })

  test('instance method calls static', ({ assert }) => {
    const p = Permission.fromUserRole('writer', 'item', 'user', 'adder')
    assert.isTrue(p.hasRole('reader'))
  })
})

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

  test('writer > reader', ({ assert }) => {
    assert.isTrue(Permission.isHigherRole('writer', 'reader'))
  })

  test('reader !> writer', ({ assert }) => {
    assert.isFalse(Permission.isHigherRole('reader', 'writer'))
  })

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

test.group('Permission.toJSON()', () => {
  test('returns plain object with all fields', ({ assert }) => {
    const p = Permission.fromUserRole('owner', 'item', 'user', 'adder')
    p.displayName = 'User Name'
    p.expirationTime = 1000

    const json = p.toJSON()
    assert.equal(json.kind, 'Core#Permission')
    assert.equal(json.role, 'owner')
    assert.equal(json.displayName, 'User Name')
    assert.equal(json.expirationTime, 1000)
    assert.typeOf(json.key, 'string')
  })

  test('omits optional fields if undefined', ({ assert }) => {
    const p = Permission.fromUserRole('owner', 'item', 'user', 'adder')
    const json = p.toJSON()
    assert.isUndefined(json.displayName)
    assert.isUndefined(json.expirationTime)
  })
})
