require 'spec_helper'
require 'synchromesh/integration/test_components'

describe "synchronizing relationships", js: true do

  before(:all) do
    require 'pusher'
    require 'pusher-fake'
    Pusher.app_id = "MY_TEST_ID"
    Pusher.key =    "MY_TEST_KEY"
    Pusher.secret = "MY_TEST_SECRET"
    require "pusher-fake/support/base"

    HyperMesh.configuration do |config|
      config.transport = :pusher
      config.channel_prefix = "synchromesh"
      config.opts = {app_id: Pusher.app_id, key: Pusher.key, secret: Pusher.secret}.merge(PusherFake.configuration.web_options)
    end
  end

  before(:each) do
    # spec_helper resets the policy system after each test so we have to setup
    # before each test
    stub_const 'TestApplication', Class.new
    stub_const 'TestApplicationPolicy', Class.new
    TestApplicationPolicy.class_eval do
      always_allow_connection
      regulate_all_broadcasts { |policy| policy.send_all }
      allow_change(to: :all, on: [:create, :update, :destroy]) { true }
    end
    size_window(:small, :portrait)
  end

  it "belongs_to with count" do
    parent = FactoryGirl.create(:test_model)
    mount "TestComponent2", model: parent do
      class TestComponent2 < React::Component::Base
        param :model, type: TestModel
        render(:div) do
          puts "RENDERING! #{params.model.child_models.count} items"
          div { "#{params.model.child_models.count} items" }
          #ul { model.child_models.each { |model| li { model.child_attribute }}}
        end
      end
    end

    page.should have_content("0 items")
    FactoryGirl.create(:child_model, test_model: parent, child_attribute: "first child")
    page.should have_content("1 items")
    parent.child_models << FactoryGirl.create(:child_model, child_attribute: "second child")
    page.should have_content("2 items")
    parent.child_models.first.destroy
    page.should have_content("1 items")
  end

  it "belongs_to without the associated record" do
    parent = FactoryGirl.create(:test_model)
    mount "TestComponent2" do
      class TestComponent2 < React::Component::Base
        render(:div) do
          div { "#{ChildModel.count} items" }
          ul { ChildModel.each { |model| li { model.child_attribute }}}
        end
      end
    end
    page.should have_content("0 items")
    FactoryGirl.create(:child_model, test_model: parent, child_attribute: "first child")
    page.should have_content("1 items")
    parent.child_models << FactoryGirl.create(:child_model, child_attribute: "second child")
    page.should have_content("2 items")
    parent.child_models.first.destroy
    page.should have_content("1 items")
  end

  it "adding child to a new model on client" do
    mount "TestComponent2" do
      class TestComponent2 < React::Component::Base
        define_state :foo
        before_mount do
          @parent = TestModel.new
          @child = ChildModel.new
        end
        after_mount do
          after(0) do # simulate external event updating system
            @parent.child_models << @child
            @parent.save
          end
        end
        render(:div) do
          "parent has #{@parent.child_models.count} children"
        end
      end
    end
    page.should have_content("parent has 1 children", wait: 1)
    expect(TestModel.first.child_models.count).to eq(1)
  end

  it "adding child to a new model on client after render" do
    # HyperMesh.configuration do |config|
    #   #config.transport = :none
    # end
    m = FactoryGirl.create(:test_model)
    m.child_models << FactoryGirl.create(:child_model)
    mount "TestComponent2" do
      class TestComponent2 < React::Component::Base
        def self.parent
          @parent ||= TestModel.find(1)
        end
        def self.add_child
          parent.child_models << ChildModel.new
          parent.save
        end
        render(:div) do
          "parent has #{TestComponent2.parent.child_models.count} children".tap { |s| puts s}
        end
      end
    end
    wait_for_ajax
    expect(TestModel.first.child_models.count).to eq(1)
    m.child_models << FactoryGirl.create(:child_model)
    evaluate_ruby("TestComponent2.add_child")
    page.should have_content("parent has 3 children")
  end

  it "will re-render the count after an item is added or removed from a model" do
    m1 = FactoryGirl.create(:test_model)
    mount "TestComponent2" do
      class TestComponent2 < React::Component::Base
        render(:div) do
          "Count of TestModel: #{TestModel.count}".span
        end
      end
    end
    page.should have_content("Count of TestModel: 1")
    m2 = FactoryGirl.create(:test_model)
    page.should have_content("Count of TestModel: 2")
    m1.destroy
    page.should have_content("Count of TestModel: 1")
    m2.destroy
    page.should have_content("Count of TestModel: 0")
  end

  it "will re-render the model's all scope after an item is added or removed" do
    m1 = FactoryGirl.create(:test_model)
    mount "TestComponent2" do
      class TestComponent2 < React::Component::Base
        render(:div) do
          puts "Count of TestModel: #{TestModel.collect { |i| i.id}.length}"
          "Count of TestModel: #{TestModel.collect { |i| i.id}.length}".span
        end
      end
    end
    page.should have_content("Count of TestModel: 1")
    m2 = FactoryGirl.create(:test_model)
    page.should have_content("Count of TestModel: 2")
    m1.destroy
    page.should have_content("Count of TestModel: 1")
    m2.destroy
    page.should have_content("Count of TestModel: 0")
  end

  context "updating a client scoped method when applied to a collection" do

    before(:each) do

      isomorphic do
        ChildModel.class_eval do
          scope :boo_ha, -> { all }, client: -> { true }
        end
      end

      m = FactoryGirl.create(:test_model, test_attribute: 'hello')
      FactoryGirl.create(:child_model, test_model: m)

      mount "TestComponent3" do
        class TestComponent3 < React::Component::Base
          render(OL) do
            TestModel.all[0].child_models.boo_ha.each do |child|
              LI { "child id = #{child.id} "}
            end
          end
        end
      end
      page.should have_content('child id = 1')
    end

    it "will update when sent from the server" do
      ChildModel.create(child_attribute: :foo, test_model: TestModel.find(1))
      page.should have_content('child id = 2')
      ChildModel.find(1).destroy
      sleep 0.1 # necessary for poltergeist to work with pusher faker
      page.should_not have_content('child id = 1', wait: 2)
    end

    it "will update when sent from the client" do
      evaluate_ruby do
        # ReactiveRecord::Collection.hypertrace instrument: :all
        # ReactiveRecord::Collection.hypertrace :class, instrument: :all
        # ReactiveRecord::Base.hypertrace instrument: :sync_unscoped_collection!
        # ReactiveRecord::ScopeDescription.hypertrace instrument: :all

        ChildModel.create(child_attribute: :foo, test_model: TestModel.find(1))
      end
      page.should have_content('child id = 2')
      evaluate_ruby do
        ChildModel.find(1).destroy
      end
      sleep 0.1 # necessary for poltergeist to work with pusher faker
      page.should_not have_content('child id = 1', wait: 2)
    end

  end

  context "updating a has_many relationship" do

    before(:each) do

      m = FactoryGirl.create(:test_model, test_attribute: 'hello')
      FactoryGirl.create(:child_model, test_model: m)

      mount "TestComponent3" do
        class TestComponent3 < React::Component::Base
          render(OL) do
            TestModel.all[0].child_models.each do |child|
              LI { "child id = #{child.id} "}
            end
          end
        end
      end
      page.should have_content('child id = 1')
    end

    it "will update when sent from the server" do
      ChildModel.create(child_attribute: :foo, test_model: TestModel.find(1))
      page.should have_content('child id = 2')
      ChildModel.find(1).destroy
      sleep 0.1 # necessary for poltergeist to work with pusher faker
      page.should_not have_content('child id = 1', wait: 2)
    end

    it "will update when sent from the client" do

      evaluate_ruby do
        ChildModel.create(child_attribute: :foo, test_model: TestModel.find(1))
      end
      page.should have_content('child id = 2')
      evaluate_ruby do
        # ReactiveRecord::Collection.hypertrace instrument: :all
        # ReactiveRecord::Collection.hypertrace :class, instrument: :all
        # ReactiveRecord::Base.hypertrace instrument: :sync_unscoped_collection!
        # ReactiveRecord::ScopeDescription.hypertrace instrument: :all

        ChildModel.find(1).destroy
      end
      sleep 0.1 # necessary for poltergeist to work with pusher faker
      page.should_not have_content('child id = 1', wait: 2)
    end

  end

  it "composed_of"

end