module FeatureFlagger
  # Model provides convinient methods for Rails Models
  # class Account
  #   include FeatureFlagger::Model
  # end
  #
  # Example:
  # Account.first.rollout?([:email_marketing, :new_awesome_feature])
  # #=> true
  module Model
    def self.included(base)
      base.extend ClassMethods
    end

    def released?(*feature_key, **options)
      self.class.released_id?(feature_flagger_identifier, *feature_key, **options)
    end

    def release(*feature_key)
      self.class.release_id(feature_flagger_identifier, *feature_key)
    end

    def unrelease(*feature_key)
      resource_name = self.class.feature_flagger_model_settings.entity_name
      feature = Feature.new(feature_key, resource_name)
      FeatureFlagger.control.unrelease(feature.key, id)
    end

    def releases(options = {})
      resource_name = self.class.feature_flagger_model_settings.entity_name
      FeatureFlagger.control.releases(resource_name, id, options)
    end

    private

    def feature_flagger_identifier
      public_send(self.class.feature_flagger_model_settings.identifier_field)
    end

    module ClassMethods
      def feature_flagger
        raise ArgumentError unless block_given?
        yield feature_flagger_model_settings
      end

      def released_id?(resource_id, *feature_key, **options)
        feature = Feature.new(feature_key, feature_flagger_model_settings.entity_name)
        FeatureFlagger.control.released?(feature.key, resource_id, options)
      end

      def release_id(resource_id, *feature_key)
        feature = Feature.new(feature_key, feature_flagger_model_settings.entity_name)
        FeatureFlagger.control.release(feature.key, resource_id)
      end

      def unrelease_id(resource_id, *feature_key)
        feature = Feature.new(feature_key, feature_flagger_model_settings.entity_name)
        FeatureFlagger.control.unrelease(feature.key, resource_id)
      end

      def all_released_ids_for(*feature_key, **options)
        feature_key.flatten!
        feature = Feature.new(feature_key, feature_flagger_model_settings.entity_name)
        FeatureFlagger.control.resource_ids(feature.key, options)
      end

      def release_to_all(*feature_key)
        feature = Feature.new(feature_key, feature_flagger_model_settings.entity_name)
        FeatureFlagger.control.release_to_all(feature.key)
      end

      def unrelease_to_all(*feature_key)
        feature = Feature.new(feature_key, feature_flagger_model_settings.entity_name)
        FeatureFlagger.control.unrelease_to_all(feature.key)
      end

      def released_features_to_all(options = {})
        FeatureFlagger.control.released_features_to_all(options)
      end

      def released_to_all?(*feature_key, **options)
        feature = Feature.new(feature_key, feature_flagger_model_settings.entity_name)
        FeatureFlagger.control.released_to_all?(feature.key, options)
      end

      def detached_feature_keys
        rollout_resource_name = feature_flagger_model_settings.entity_name
        persisted_features = FeatureFlagger.control.search_keys("#{rollout_resource_name}:*").to_a
        mapped_feature_keys = FeatureFlagger.config.mapped_feature_keys(rollout_resource_name)
        (persisted_features - mapped_feature_keys).map { |key| key.sub("#{rollout_resource_name}:",'') }
      end

      def cleanup_detached(*feature_key)
        complete_feature_key = feature_key.map(&:to_s).insert(0, feature_flagger_model_settings.entity_name)
        key_value = FeatureFlagger.config.info.dig(*complete_feature_key)
        raise "key is still mapped" if key_value
        FeatureFlagger.control.unrelease_to_all(complete_feature_key.join(':'))
      end

      def rollout_resource_name
        klass_name = self.to_s
        klass_name.gsub!(/::/, '_')
        klass_name.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
        klass_name.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
        klass_name.tr!("-", "_")
        klass_name.downcase!
        klass_name
      end

      def feature_flagger_model_settings
        @feature_flagger_model_settings ||= FeatureFlagger::ModelSettings.new(
          identifier_field: :id,
          entity_name: rollout_resource_name
        )
      end

      def feature_flagger_identifier
        public_send(feature_flagger_model_settings.identifier_field)
      end
    end
  end
end
