require "test_helper"

class User < ActiveRecord::Base
  has_many :skies

  attr_accessible :height if defined?(ProtectedAttributes)
end

class Sky < ActiveRecord::Base
  belongs_to :user
  scope :high, -> { where("height >= 4") }

  attr_accessible :height if defined?(ProtectedAttributes)
end

if ENV["DB"] == "active_record"

  describe Vanity::Metric::ActiveRecord do
    before do
      User.connection.create_table(:users) do |t|
        t.timestamps
      end
      Sky.connection.create_table(:skies) do |t|
        t.integer :user_id
        t.integer :height
        t.timestamps
      end
    end

    after do
      User.connection.drop_table(:users) if User.connection.table_exists?(User.table_name)
      User.reset_callbacks(:create)
      User.reset_callbacks(:save)
      Sky.connection.drop_table(:skies) if Sky.connection.table_exists?(Sky.table_name)
      Sky.reset_callbacks(:create)
      Sky.reset_callbacks(:save)
    end

    it "record count" do
      File.write("tmp/experiments/metrics/sky_is_limit.rb", <<-RUBY)
        metric "Sky is limit" do
          model Sky
        end
      RUBY
      Vanity.playground.metrics
      Sky.create!
      assert_equal 1, Sky.count
      assert_equal 1, Vanity::Metric.data(metric(:sky_is_limit)).last.last
    end

    it "record sum" do
      File.write("tmp/experiments/metrics/sky_is_limit.rb", <<-RUBY)
        metric "Sky is limit" do
          model Sky, :sum=>:height
        end
      RUBY
      Vanity.playground.metrics
      Sky.create! height: 4
      Sky.create! height: 2
      assert_equal 6, Vanity::Metric.data(metric(:sky_is_limit)).last.last
    end

    it "record average" do
      File.write("tmp/experiments/metrics/sky_is_limit.rb", <<-RUBY)
        metric "Sky is limit" do
          model Sky, :average=>:height
        end
      RUBY
      Vanity.playground.metrics
      Sky.create! height: 8
      Sky.create! height: 2
      assert_equal 5, Vanity::Metric.data(metric(:sky_is_limit)).last.last
    end

    it "record minimum" do
      File.write("tmp/experiments/metrics/sky_is_limit.rb", <<-RUBY)
        metric "Sky is limit" do
          model Sky, :minimum=>:height
        end
      RUBY
      Vanity.playground.metrics
      Sky.create! height: 4
      Sky.create! height: 2
      assert_equal 2, Vanity::Metric.data(metric(:sky_is_limit)).last.last
    end

    it "record maximum" do
      File.write("tmp/experiments/metrics/sky_is_limit.rb", <<-RUBY)
        metric "Sky is limit" do
          model Sky, :maximum=>:height
        end
      RUBY
      Vanity.playground.metrics
      Sky.create! height: 4
      Sky.create! height: 2
      assert_equal 4, Vanity::Metric.data(metric(:sky_is_limit)).last.last
    end

    it "with conditions" do
      File.write("tmp/experiments/metrics/sky_is_limit.rb", <<-RUBY)
        metric "Sky is limit" do
          model Sky, :sum=>:height, :conditions=>["height > 4"]
        end
      RUBY
      Vanity.playground.metrics
      high_skies = 0
      metric(:sky_is_limit).hook do |_metric_id, _timestamp, height|
        assert height > 4
        high_skies += height
      end
      [nil, 5, 3, 6].each do |height|
        Sky.create! height: height
      end
      assert_equal 11, Vanity::Metric.data(metric(:sky_is_limit)).sum(&:last)
      assert_equal 11, high_skies
    end

    it "with scope" do
      File.write("tmp/experiments/metrics/sky_is_limit.rb", <<-RUBY)
        metric "Sky is limit" do
          model Sky.high
        end
      RUBY
      Vanity.playground.metrics
      total = 0
      metric(:sky_is_limit).hook do |_metric_id, _timestamp, count|
        total += count
      end
      Sky.create! height: 4
      Sky.create! height: 2
      assert_equal 1, Vanity::Metric.data(metric(:sky_is_limit)).last.last
      assert_equal 1, total
    end

    it "with timestamp" do
      File.write("tmp/experiments/metrics/sky_is_limit.rb", <<-RUBY)
        metric "Sky is limit" do
          model Sky, :timestamp => :created_at
        end
      RUBY
      Vanity.playground.metrics
      Sky.create!
      assert_equal 1, Sky.count
      assert_equal 1, Vanity::Metric.data(metric(:sky_is_limit)).last.last
    end

    it "with timestamp and table" do
      File.write("tmp/experiments/metrics/sky_is_limit.rb", <<-RUBY)
        metric "Sky is limit" do
          model Sky, :timestamp => 'skies.created_at'
        end
      RUBY
      Vanity.playground.metrics
      Sky.create!
      assert_equal 1, Sky.count
      assert_equal 1, Vanity::Metric.data(metric(:sky_is_limit)).last.last
    end

    it "hooks" do
      File.write("tmp/experiments/metrics/sky_is_limit.rb", <<-RUBY)
        metric "Sky is limit" do
          model Sky, :sum=>:height
        end
      RUBY
      Vanity.playground.metrics
      total = 0
      metric(:sky_is_limit).hook do |metric_id, timestamp, count|
        assert_equal :sky_is_limit, metric_id
        assert_in_delta Time.now.to_i, timestamp.to_i, 1
        total += count
      end
      Sky.create! height: 4
      assert_equal 4, total
    end

    it "no hooks when metrics disabled" do
      not_collecting!
      File.write("tmp/experiments/metrics/sky_is_limit.rb", <<-RUBY)
        metric "Sky is limit" do
          model Sky, :sum=>:height
        end
      RUBY
      Vanity.playground.metrics
      total = 0
      metric(:sky_is_limit).hook do |_metric_id, _timestamp, count|
        total += count
      end
      Sky.create! height: 4
      assert_equal 0, total
    end

    it "after_create not after_save" do
      File.write("tmp/experiments/metrics/sky_is_limit.rb", <<-RUBY)
        metric "Sky is limit" do
          model Sky
        end
      RUBY
      Vanity.playground.metrics
      once = nil
      metric(:sky_is_limit).hook do
        raise "Metric tracked twice" if once

        once = true
      end
      Sky.create!
      Sky.last.update height: 4
    end

    it "with after_save" do
      File.write("tmp/experiments/metrics/sky_is_limit.rb", <<-RUBY)
        metric "Sky is limit" do
          model Sky, :conditions=>["height > 3"]
          Sky.after_save do |sky|
            if sky.respond_to?(:saved_change_to_height?) && sky.saved_change_to_height? && sky.height > 3
              Vanity.track!(:sky_is_limit)
            elsif sky.height_changed? && sky.height > 3
              Vanity.track!(:sky_is_limit)
            end
          end
        end
      RUBY
      Vanity.playground.metrics
      times = 0
      metric(:sky_is_limit).hook do
        times += 1
      end
      Sky.create!
      (1..5).each do |height|
        Sky.last.update! height: height
      end
      assert_equal 2, times
    end

    it "with model identity" do
      File.write("tmp/experiments/metrics/sky_is_limit.rb", <<-RUBY)
        metric "Sky is limit" do
          model Sky, :identity => lambda { |record| record.user_id }
        end
      RUBY

      File.write("tmp/experiments/simple.rb", <<-RUBY)
        ab_test "simple" do
          metrics :sky_is_limit
          alternatives :a, :b
          identity { "me" }
        end
      RUBY

      user = User.create
      Vanity.context = stub(vanity_identity: user.id)
      experiment = Vanity.playground.experiment(:simple)
      experiment.choose

      Vanity.context = nil
      # Should count as a conversion for the user in the experiment.
      user.skies.create

      # Should count as a conversion for the newly created User.
      User.create.skies.create

      Vanity.context = stub(vanity_identity: "other")
      experiment.choose
      # Should count as a conversion for "other"
      Sky.create

      assert_equal 3, Sky.count
      assert_equal 2, experiment.alternatives.map(&:participants).sum
      assert_equal 3, experiment.alternatives.map(&:conversions).sum
    end

    it "do it yourself" do
      File.write("tmp/experiments/metrics/sky_is_limit.rb", <<-RUBY)
        metric "Sky is limit" do
          Sky.after_save { |sky| Vanity.track!(:sky_is_limit) if sky.height && sky.height > 3 }
        end
      RUBY
      Vanity.playground.metrics
      Sky.create!
      (1..5).each do |height|
        Sky.create! height: height
      end
      Sky.first.update! height: 4
      assert_equal 3, Vanity::Metric.data(metric(:sky_is_limit)).last.last
    end

    it "last update for new metric" do
      File.write("tmp/experiments/metrics/sky_is_limit.rb", <<-RUBY)
        metric "Sky is limit" do
          model Sky
        end
      RUBY
      assert_nil metric(:sky_is_limit).last_update_at
    end

    it "last update with records" do
      File.write("tmp/experiments/metrics/sky_is_limit.rb", <<-RUBY)
        metric "Sky is limit" do
          model Sky
        end
      RUBY
      Sky.create! height: 1
      Timecop.freeze Time.now + 1.day do
        Sky.create! height: 1
      end
      assert_in_delta metric(:sky_is_limit).last_update_at.to_i, (Time.now + 1.day).to_i, 1
    end

    it "metric is specifiable with a string" do
      File.write("tmp/experiments/metrics/sky_is_limit.rb", <<-RUBY)
        metric "Sky is limit" do
          model 'Sky'
        end
      RUBY
      Vanity.playground.metrics
      Sky.create!
      assert_equal 1, Vanity::Metric.data(metric(:sky_is_limit)).last.last
    end
  end

end
