# frozen_string_literal: true

require 'checkpoint/resource/resolver'

module Checkpoint
  class Resource
    # A Resource::Token is an identifier object for a Resource. It includes a
    # type and an identifier. A {Permit} can be granted for a Token. Concrete
    # entities are resolved into a number of resources, and those resources'
    # tokens will be checked for matching permits.
    class Token
      attr_reader :type, :id

      # Create a new Resource representing a domain entity or concept that would
      # be acted upon.
      #
      # @param type [String] the application-determined type of this resource.
      #   This might correspond to a model class or other type of named concept
      #   in the application. The type is always coerced to String with `#to_s`
      #   in case something else is supplied.
      #
      # @param id [String] the application-resolvable identifier for this
      #   resource. For example, this might be the ID of a model object, the
      #   name of a section. The id is always coerced to String with `#to_s` in
      #   case something else is supplied.
      def initialize(type, id)
        @type = type.to_s
        @id = id.to_s
      end

      # Get the special "all" Resource Token. This is a singleton that represents all
      # resources of all types. It is used to grant permissions or roles within
      # a zone, but not specific to a particular resource.
      #
      # @return [Resource::Token] the special "all" Resource Token
      def self.all
        @all ||= new(Resource::ALL, Resource::ALL).freeze
      end

      # @return [Token] self; for convenience of taking a Resource or token
      def token
        self
      end

      # @return [String] a URI for this resource, including its type and id
      def uri
        "resource://#{type}/#{id}"
      end

      # @return [String] a token suitable for granting or matching this resource
      def to_s
        "#{type}:#{id}"
      end

      # Compare with another Resource for equality. Consider them to represent
      # the same resource if `other` is a Resource, has the same type, and same id.
      def eql?(other)
        other.is_a?(Resource::Token) && type == other.type && id == other.id
      end

      alias == eql?
      alias inspect uri
    end
  end
end