# encoding: utf-8

require File.join(File.dirname(__FILE__), "../spec_helper.rb")

describe Her::Model::Relation do
  describe :where do
    context "for base classes" do
      before do
        Her::API.setup url: "https://api.example.com" do |builder|
          builder.use Her::Middleware::FirstLevelParseJSON
          builder.adapter :test do |stub|
            stub.get("/users?foo=1&bar=2") { ok! [{ id: 2, fullname: "Tobias Fünke" }] }
            stub.get("/users?admin=1") { ok! [{ id: 1, fullname: "Tobias Fünke" }] }
            stub.get("/users?id=3&foo=2") { ok! [{ id: 3, fullname: "Tobias Fünke" }] }

            stub.get("/users") do
              ok! [
                { id: 1, fullname: "Tobias Fünke" },
                { id: 2, fullname: "Lindsay Fünke" },
                @created_user
              ].compact
            end

            stub.post("/users") do
              @created_user = { id: 3, fullname: "George Michael Bluth" }
              ok! @created_user
            end
          end
        end

        spawn_model "Foo::User"
      end

      it "doesn't fetch the data immediatly" do
        expect(Foo::User).to receive(:request).never
        @users = Foo::User.where(admin: 1)
      end

      it "fetches the data and passes query parameters" do
        expect(Foo::User).to receive(:request).once.and_call_original
        @users = Foo::User.where(admin: 1)
        expect(@users).to respond_to(:length)
        expect(@users.size).to eql 1
      end

      it "fetches the data by parameters including primary_key" do
        expect(Foo::User).to receive(:request).once.and_call_original
        @users = Foo::User.where(id: 3, foo: 2)
        expect(@users).to respond_to(:length)
        expect(@users.size).to eql 1
      end

      it "chains multiple where statements" do
        @user = Foo::User.where(foo: 1).where(bar: 2).first
        expect(@user.id).to eq(2)
      end

      it "does not reuse relations" do
        expect(Foo::User.all.size).to eql 2
        expect(Foo::User.create(fullname: "George Michael Bluth").id).to eq(3)
        expect(Foo::User.all.size).to eql 3
      end
    end

    context "for parent class" do
      before do
        Her::API.setup url: "https://api.example.com" do |builder|
          builder.use Her::Middleware::FirstLevelParseJSON
          builder.adapter :test do |stub|
            stub.get("/users?page=2") { ok! [{ id: 1, fullname: "Tobias Fünke" }, { id: 2, fullname: "Lindsay Fünke" }] }
          end
        end

        spawn_model("Foo::Model") do
          scope :page, ->(page) { where(page: page) }
        end

        class User < Foo::Model; end
        @spawned_models << :User
      end

      it "propagates the scopes through its children" do
        expect(User.page(2).length).to eq(2)
        expect(User.scoped.page(2).length).to eq(2)
      end
    end
  end

  describe :create do
    before do
      Her::API.setup url: "https://api.example.com" do |builder|
        builder.use Her::Middleware::FirstLevelParseJSON
        builder.use Faraday::Request::UrlEncoded
        builder.adapter :test do |stub|
          stub.post("/users") { |env| ok! id: 1, fullname: params(env)[:fullname], email: params(env)[:email] }
        end
      end

      spawn_model "Foo::User"
    end

    context "with a single where call" do
      it "creates a resource and passes the query parameters" do
        @user = Foo::User.where(fullname: "Tobias Fünke", email: "tobias@bluth.com").create
        expect(@user.id).to eq(1)
        expect(@user.fullname).to eq("Tobias Fünke")
        expect(@user.email).to eq("tobias@bluth.com")
      end
    end

    context "with multiple where calls" do
      it "creates a resource and passes the query parameters" do
        @user = Foo::User.where(fullname: "Tobias Fünke").create(email: "tobias@bluth.com")
        expect(@user.id).to eq(1)
        expect(@user.fullname).to eq("Tobias Fünke")
        expect(@user.email).to eq("tobias@bluth.com")
      end
    end
  end

  describe :build do
    before { spawn_model "Foo::User" }

    it "handles new resource with build" do
      @new_user = Foo::User.where(fullname: "Tobias Fünke").build
      expect(@new_user.new?).to be_truthy
      expect(@new_user.fullname).to eq("Tobias Fünke")
    end
  end

  describe :scope do
    before do
      Her::API.setup url: "https://api.example.com" do |builder|
        builder.use Her::Middleware::FirstLevelParseJSON
        builder.adapter :test do |stub|
          stub.get("/users?what=4&where=3") { ok! [{ id: 3, fullname: "Maeby Fünke" }] }
          stub.get("/users?what=2") { ok! [{ id: 2, fullname: "Lindsay Fünke" }] }
          stub.get("/users?where=6") { ok! [{ id: 4, fullname: "Tobias Fünke" }] }
          stub.get('/bar/users') { ok! [] }
        end
      end

      spawn_model "Foo::User" do
        scope :foo, ->(v) { where(what: v) }
        scope :bar, ->(v) { where(where: v) }
        scope :baz, -> { bar(6) }
      end

      spawn_model "Bar::User" do
        collection_path '/bar/users'
        scope :baz, -> { where(where: 7) }
      end
    end

    it "passes query parameters" do
      @user = Foo::User.foo(2).first
      expect(@user.id).to eq(2)
    end

    it "passes multiple query parameters" do
      @user = Foo::User.foo(4).bar(3).first
      expect(@user.id).to eq(3)
    end

    it "handles embedded scopes" do
      @user = Foo::User.baz.first
      expect(@user.id).to eq(4)
    end

    it "does not share scope with other models" do
      expect(Bar::User.scoped).not_to respond_to(:foo, :bar)
      expect(Foo::User.scoped.baz.params[:where]).to eq(6)
      expect(Bar::User.scoped.baz.params[:where]).to eq(7)
    end
  end

  describe :default_scope do
    context "for new objects" do
      before do
        spawn_model "Foo::User" do
          default_scope -> { where(active: true) }
          default_scope -> { where(admin: true) }
        end
      end

      it "should apply the scope to the attributes" do
        expect(Foo::User.new).to be_active
        expect(Foo::User.new).to be_admin
      end
    end

    context "for fetched resources" do
      before do
        Her::API.setup url: "https://api.example.com" do |builder|
          builder.use Her::Middleware::FirstLevelParseJSON
          builder.use Faraday::Request::UrlEncoded
          builder.adapter :test do |stub|
            stub.post("/users") { |env| ok! id: 3, active: (params(env)[:active] == "true") }
          end
        end

        spawn_model "Foo::User" do
          default_scope -> { where(active: true) }
        end
      end

      it("should apply the scope to the request") { expect(Foo::User.create).to be_active }
    end

    context "for fetched collections" do
      before do
        Her::API.setup url: "https://api.example.com" do |builder|
          builder.use Her::Middleware::FirstLevelParseJSON
          builder.use Faraday::Request::UrlEncoded
          builder.adapter :test do |stub|
            stub.get("/users?active=true") { |env| ok! [{ id: 3, active: (params(env)[:active] == "true") }] }
          end
        end

        spawn_model "Foo::User" do
          default_scope -> { where(active: true) }
        end
      end

      it("should apply the scope to the request") { expect(Foo::User.all.first).to be_active }
    end
  end

  describe :map do
    before do
      Her::API.setup url: "https://api.example.com" do |builder|
        builder.use Her::Middleware::FirstLevelParseJSON
        builder.adapter :test do |stub|
          stub.get("/users") do
            ok! [{ id: 1, fullname: "Tobias Fünke" }, { id: 2, fullname: "Lindsay Fünke" }]
          end
        end
      end

      spawn_model "Foo::User"
    end

    it "delegates the method to the fetched collection" do
      expect(Foo::User.all.map(&:fullname)).to eq(["Tobias Fünke", "Lindsay Fünke"])
    end
  end
end
