import { Namespace, Context, SubjectSet } from "@ory/keto-definitions"

class User implements Namespace {}

class System implements Namespace {
  related: {
    super_admins: User[]
    authenticated_users: User[]
  }

  permits = {
    manage_all: (ctx: Context): boolean =>
      this.related.super_admins.includes(ctx.subject)
  }
}

class Tenant implements Namespace {
  related: {
    owners: (User | SubjectSet<System, "super_admins">)[]
    admins: (User | SubjectSet<System, "super_admins">)[]
    members: (User | SubjectSet<System, "super_admins"> | SubjectSet<Tenant, "admins"> | SubjectSet<Tenant, "owners">)[]
    parents: Tenant[]
    developer_console_viewer: (User | SubjectSet<System, "super_admins">)[]
    developer_console_grant_manager: (User | SubjectSet<System, "super_admins">)[]
  }

  permits = {
    view: (ctx: Context): boolean =>
      this.related.members.includes(ctx.subject) ||
      this.related.admins.includes(ctx.subject) ||
      this.related.owners.includes(ctx.subject) ||
      this.related.parents.traverse((p) => p.permits.view(ctx)),

    manage: (ctx: Context): boolean =>
      this.related.admins.includes(ctx.subject) ||
      this.related.owners.includes(ctx.subject) ||
      this.related.parents.traverse((p) => p.permits.manage(ctx)),

    manage_admins: (ctx: Context): boolean =>
      this.related.owners.includes(ctx.subject) ||
      this.related.parents.traverse((p) => p.permits.manage_admins(ctx)),

    create_subtenant: (ctx: Context): boolean =>
      this.permits.manage(ctx),

    view_dev_console: (ctx: Context): boolean =>
      this.related.developer_console_viewer.includes(ctx.subject) ||
      this.permits.grant_dev_permissions(ctx) ||
      this.permits.manage(ctx) ||
      this.related.parents.traverse((p) => p.permits.view_dev_console(ctx)),

    grant_dev_permissions: (ctx: Context): boolean =>
      this.related.developer_console_grant_manager.includes(ctx.subject) ||
      this.permits.manage_admins(ctx) ||
      this.related.parents.traverse((p) => p.permits.grant_dev_permissions(ctx))
  }
}

class RelyingParty implements Namespace {
  related: {
    admins: (User | SubjectSet<System, "super_admins"> | SubjectSet<Tenant, "admins"> | SubjectSet<Tenant, "owners">)[]
    parents: Tenant[]
    access: (User | SubjectSet<Tenant, "members"> | SubjectSet<System, "authenticated_users"> | SubjectSet<System, "super_admins">)[]
    creator: (User | SubjectSet<System, "super_admins">)[]
    config_editor: (User | SubjectSet<System, "super_admins">)[]
    secret_viewer: (User | SubjectSet<System, "super_admins">)[]
    secret_rotator: (User | SubjectSet<System, "super_admins">)[]
    jwks_viewer: (User | SubjectSet<System, "super_admins">)[]
    jwks_operator: (User | SubjectSet<System, "super_admins">)[]
    consent_viewer: (User | SubjectSet<System, "super_admins">)[]
    consent_revoker: (User | SubjectSet<System, "super_admins">)[]
    relationship_viewer: (User | SubjectSet<System, "super_admins">)[]
    audit_viewer: (User | SubjectSet<System, "super_admins">)[]
    status_operator: (User | SubjectSet<System, "super_admins">)[]
  }

  permits = {
    view: (ctx: Context): boolean =>
      this.related.admins.includes(ctx.subject) ||
      this.related.config_editor.includes(ctx.subject) ||
      this.related.secret_viewer.includes(ctx.subject) ||
      this.related.secret_rotator.includes(ctx.subject) ||
      this.related.jwks_viewer.includes(ctx.subject) ||
      this.related.jwks_operator.includes(ctx.subject) ||
      this.related.consent_viewer.includes(ctx.subject) ||
      this.related.consent_revoker.includes(ctx.subject) ||
      this.related.relationship_viewer.includes(ctx.subject) ||
      this.related.audit_viewer.includes(ctx.subject) ||
      this.related.status_operator.includes(ctx.subject) ||
      this.related.parents.traverse((t) => t.permits.view(ctx)) ||
      this.related.parents.traverse((t) => t.permits.view_dev_console(ctx)),

    manage: (ctx: Context): boolean =>
      this.related.admins.includes(ctx.subject) ||
      this.related.parents.traverse((t) => t.permits.manage(ctx)),

    create: (ctx: Context): boolean =>
      this.related.creator.includes(ctx.subject) ||
      this.related.parents.traverse((t) => t.permits.grant_dev_permissions(ctx)) ||
      this.permits.manage(ctx),

    edit_config: (ctx: Context): boolean =>
      this.related.config_editor.includes(ctx.subject) ||
      this.permits.manage(ctx),

    view_secret: (ctx: Context): boolean =>
      this.related.secret_viewer.includes(ctx.subject) ||
      this.permits.rotate_secret(ctx) ||
      this.permits.manage(ctx),

    rotate_secret: (ctx: Context): boolean =>
      this.related.secret_rotator.includes(ctx.subject) ||
      this.permits.manage(ctx),

    view_jwks: (ctx: Context): boolean =>
      this.related.jwks_viewer.includes(ctx.subject) ||
      this.permits.operate_jwks(ctx) ||
      this.permits.manage(ctx),

    operate_jwks: (ctx: Context): boolean =>
      this.related.jwks_operator.includes(ctx.subject) ||
      this.permits.manage(ctx),

    view_consents: (ctx: Context): boolean =>
      this.related.consent_viewer.includes(ctx.subject) ||
      this.permits.revoke_consents(ctx) ||
      this.permits.manage(ctx),

    revoke_consents: (ctx: Context): boolean =>
      this.related.consent_revoker.includes(ctx.subject) ||
      this.permits.manage(ctx),

    view_relationships: (ctx: Context): boolean =>
      this.related.relationship_viewer.includes(ctx.subject) ||
      this.related.parents.traverse((t) => t.permits.grant_dev_permissions(ctx)) ||
      this.permits.manage(ctx),

    view_audit_logs: (ctx: Context): boolean =>
      this.related.audit_viewer.includes(ctx.subject) ||
      this.permits.manage(ctx),

    change_status: (ctx: Context): boolean =>
      this.related.status_operator.includes(ctx.subject) ||
      this.permits.manage(ctx),

    access: (ctx: Context): boolean =>
      this.related.access.includes(ctx.subject) ||
      this.permits.manage(ctx)
  }
}
