# frozen_string_literal: true

module ActiveEventStore
  # RES event wrapper
  class Event < RubyEventStore::Event
    RESERVED_ATTRIBUTES = %i[event_id type metadata].freeze

    class << self
      attr_writer :identifier

      def identifier
        return @identifier if instance_variable_defined?(:@identifier)

        @identifier = name.underscore.tr("/", ".")
      end

      # define store readers
      def attributes(*fields)
        fields.each do |field|
          raise ArgumentError, "#{field} is reserved" if RESERVED_ATTRIBUTES.include?(field)

          defined_attributes << field

          class_eval <<~CODE, __FILE__, __LINE__ + 1
            def #{field}
              data[:#{field}]
            end
          CODE
        end
      end

      def sync_attributes(*fields)
        fields.each do |field|
          raise ArgumentError, "#{field} is reserved" if RESERVED_ATTRIBUTES.include?(field)

          defined_sync_attributes << field

          attr_reader field
        end
      end

      def defined_attributes
        return @defined_attributes if instance_variable_defined?(:@defined_attributes)

        @defined_attributes =
          if superclass.respond_to?(:defined_attributes)
            superclass.defined_attributes.dup
          else
            []
          end
      end

      def defined_sync_attributes
        return @defined_sync_attributes if instance_variable_defined?(:@defined_sync_attributes)

        @defined_sync_attributes =
          if superclass.respond_to?(:defined_sync_attributes)
            superclass.defined_sync_attributes.dup
          else
            []
          end
      end
    end

    def initialize(metadata: {}, event_id: nil, **params)
      validate_attributes!(params)
      extract_sync_attributes!(params)
      super(**{event_id: event_id, metadata: metadata, data: params}.compact)
    end

    def type
      self.class.identifier
    end

    alias_method :event_type, :type

    def inspect
      "#{self.class.name}<#{event_type}##{message_id}>, data: #{data}, metadata: #{metadata}"
    end

    # Has been removed from RES: https://github.com/RailsEventStore/rails_event_store/pull/726
    def to_h
      {
        event_id: event_id,
        metadata: metadata.to_h,
        data: data,
        type: event_type
      }
    end

    protected

    attr_writer :event_id

    def validate_attributes!(params)
      unknown_fields = params.keys.map(&:to_sym) - self.class.defined_attributes - self.class.defined_sync_attributes
      unless unknown_fields.empty?
        raise ArgumentError, "Unknown event attributes: #{unknown_fields.join(", ")}"
      end
    end

    def extract_sync_attributes!(params)
      params.keys.each do |key|
        next unless self.class.defined_sync_attributes.include?(key.to_sym)

        instance_variable_set(:"@#{key}", params.delete(key))
      end
    end
  end
end
