require 'active_support/concern'

module ZeroDowntime
  class DeprecatedColumn < RuntimeError
  end

  # Deprecatable adds the ability to deprecate columns
  # to ActiveRecord
  #
  #   class MyModel < ActiveRecord::Base
  #     deprecate_column :name
  #   end
  #
  module Deprecatable
    extend ActiveSupport::Concern

    included do
      class_attribute :deprecated_columns

      class << self
        alias_method_chain :columns, :deprecations
      end
    end

    module ClassMethods
      # deprecate a given column
      # so it will be ignore by activerecord
      # we can remove it once the deprecation is deployed
      def deprecate_column(column_name)
        deprecate_column_reader(column_name)
        deprecate_column_writer(column_name)
        override_attribute_methods

        self.deprecated_columns ||= []
        deprecated_columns << column_name.to_s
      end

      def columns_with_deprecations
        all_columns = columns_without_deprecations
        return all_columns unless deprecated_columns

        all_columns.reject { |c| deprecated_columns.include?(c.name) }
      end

      private

      def override_attribute_methods
        return false if @attribute_methods_overriden

        class_eval do
          def attributes_with_deprecations
            attributes_without_deprecations.except(*self.class.deprecated_columns)
          end
          alias_method_chain :attributes, :deprecations

          def attribute_names_with_deprecations
            attribute_names_without_deprecations - self.class.deprecated_columns
          end
          alias_method_chain :attribute_names, :deprecations
        end
        @attribute_methods_overriden = true
      end

      def deprecate_column_reader(column_name)
        define_method(column_name) do
          fail DeprecatedColumn, "attempted to read #{column_name}"
        end
      end

      def deprecate_column_writer(column_name)
        define_method("#{column_name}=") do |_|
          fail DeprecatedColumn, "attempted to write #{column_name}"
        end
      end
    end
  end
end