class ActiveScaffold::Tableless < ActiveRecord::Base
  class AssociationScope < ActiveRecord::Associations::AssociationScope
    def column_for(table_name, column_name, alias_tracker = nil)
      klass = alias_tracker ? alias_tracker.connection.klass : self.klass
      if table_name == klass.table_name
        klass.columns_hash[column_name]
      else
        super
      end
    end

    if defined?(ActiveRecord::Associations::AssociationScope::INSTANCE) # rails >= 4.1
      INSTANCE = respond_to?(:create) ? create : new # create for rails >= 4.2
      def self.scope(association, connection)
        INSTANCE.scope association, connection
      end
    end
  end
  
  class Connection < ActiveRecord::ConnectionAdapters::AbstractAdapter
    attr_reader :klass
    def initialize(klass, *args)
      super(nil, *args)
      @klass = klass
    end
  end

  class Column < ActiveRecord::ConnectionAdapters::Column
    if instance_method(:initialize).arity == -4 # rails >= 4.2
      def initialize(name, default, sql_type = nil, null = true)
        cast_type = ActiveRecord::Base.connection.send :lookup_cast_type, sql_type
        super(name, default, cast_type, sql_type, null)
      end
    end
  end

  module Association
    def self.included(base)
      base.alias_method_chain :association_scope, :tableless
      base.alias_method_chain :target_scope, :tableless
    end

    def association_scope_with_tableless
      @association_scope ||= overrided_association_scope if klass < ActiveScaffold::Tableless
      association_scope_without_tableless
    end
    
    def overrided_association_scope
      if AssociationScope.respond_to?(:scope) # rails >= 4.1
        AssociationScope.scope(self, klass.connection)
      else # rails < 4.1
        AssociationScope.new(self).scope
      end
    end

    def target_scope_with_tableless
      target_scope_without_tableless.tap do |scope|
        if klass < ActiveScaffold::Tableless
          class << scope; include RelationExtension; end
        end
      end
    end
  end
  
  module CollectionAssociation
    def self.included(base)
      base.alias_method_chain :get_records, :tableless if Rails.version >= '4.2'
    end
    def get_records_with_tableless
      klass < ActiveScaffold::Tableless ? scope.to_a : get_records_without_tableless
    end
  end
  
  module SingularAssociation
    def self.included(base)
      base.alias_method_chain :get_records, :tableless if Rails.version >= '4.2'
    end
    def get_records_with_tableless
      klass < ActiveScaffold::Tableless ? scope.limit(1).to_a : get_records_without_tableless
    end
  end

  module RelationExtension
    def conditions
      @conditions
    end
  
    def initialize(klass, table)
      super
      @conditions ||= []
    end

    def initialize_copy(other)
      @conditions = @conditions.try(:dup) || []
      super
    end

    def where(opts, *rest)
      unless opts.blank?
        opts = opts.with_indifferent_access if opts.is_a? Hash
        @conditions << (rest.empty? ? opts : [opts, *rest])
      end
      self
    end

    def merge(r)
      super.tap do |merged|
        merged.conditions.concat r.conditions unless r.nil? || r.is_a?(Array)
      end
    end

    def except(*skips)
      super.tap do |new_relation|
        new_relation.conditions = conditions unless skips.include? :where
      end
    end

    def to_a
      @klass.find_all(self)
    end

    def find_one(id)
      @klass.find_one(id, self) or raise ActiveRecord::RecordNotFound
    end

    def execute_simple_calculation(operation, column_name, distinct)
      @klass.execute_simple_calculation(self, operation, column_name, distinct)
    end
  end

  class Relation < ActiveRecord::Relation
    include RelationExtension
  end
  class << self
    private
    def relation
      ActiveScaffold::Tableless::Relation.new(self, arel_table)
    end
  end

  class StatementCache
    def initialize(key)
      @key = key
    end

    def execute(values, model, connection)
      model.where(@key => values)
    end
  end

  unless Rails.version < '4.2'
    def self.columns_hash
      if self < ActiveScaffold::Tableless
        @columns_hash ||= Hash[self.columns.map { |c| [c.name, c] }]
      else
        super
      end
    end
    def self.initialize_find_by_cache
      self.find_by_statement_cache = Hash.new { |h, k| h[k] = StatementCache.new(k) }
    end
  end

  def self.columns; @columns ||= []; end
  def self.table_name; @table_name ||= ActiveModel::Naming.plural(self); end
  def self.table_exists?; true; end
  self.abstract_class = true
  
  def self.connection
    @connection ||= Connection.new(self)
  end

  def self.column(name, sql_type = nil, options = {})
    column = Column.new(name.to_s, options[:default], sql_type.to_s, options.has_key?(:null) ? options[:null] : true)
    column.tap { columns << column }
  end

  def self.find_all(relation)
    raise 'self.find_all must be implemented in a Tableless model'
  end

  def self.find_one(id, relation)
    raise 'self.find_one must be implemented in a Tableless model'
  end

  def self.execute_simple_calculation(relation, operation, column_name, distinct)
    if operation == 'count' && [relation.klass.primary_key, :all].include?(column_name)
      find_all(relation).size
    else
      raise "self.execute_simple_calculation must be implemented in a Tableless model to support #{operation} #{column_name}#{' distinct' if distinct} columns"
    end
  end

  def destroy
    raise 'destroy must be implemented in a Tableless model'
  end

  def create_record #:nodoc:
    run_callbacks(:create) {}
  end
  alias_method :create, :create_record # for rails3

  def update_record(*) #:nodoc:
    run_callbacks(:update) {}
  end
  alias_method :update, :update_record # for rails3
end
