
var qs = require("querystring");
var uuid = require("uuid");
var should = require("should");
var sinon = require("sinon");
var url = require("url");

var ua = require("../lib/index.js");
var utils = require("../lib/utils.js");
var config = require("../lib/config.js");
var request = require("../lib/request");


describe("ua", function () {


	describe("#item", function () {
		var _enqueue;

		beforeEach(function () {
			_enqueue = sinon.stub(ua.Visitor.prototype, "_enqueue", function (type, params, fn) {
				if (fn) {
					(typeof fn).should.equal('function', "#_enqueue should receive a callback")
					fn();
				}
				return this;
			});
		});

		afterEach(function () {
			_enqueue.restore()
		});


		it("should be available via the #i shortcut", function () {
			var visitor = ua()
			visitor.i.should.equal(visitor.item)
		});


		it("should accept arguments (price) when chained to transaction", function () {
			var transaction = Math.random().toString();
			var price = Math.random();

			var visitor = ua().transaction(transaction);

			var result = visitor.item(price);

			visitor._context = result._context;
			result.should.eql(visitor, "should return a visitor that is identical except for the context");
			result.should.be.instanceof(ua.Visitor);
			result._context.should.eql(_enqueue.args[1][1], "the transaction params should be persisted as the context of the visitor clone")

			_enqueue.calledTwice.should.equal(true, "#_enqueue should have been called twice, once for the transaction before");
			_enqueue.args[0][0].should.equal("transaction");
			_enqueue.args[1][0].should.equal("item");
			_enqueue.args[1][1].should.have.keys("ti", "ip")
			_enqueue.args[1][1].ti.should.equal(transaction);
			_enqueue.args[1][1].ip.should.equal(price);
		});


		it("should accept arguments (price, fn) when chained to transaction", function () {
			var transaction = Math.random().toString();
			var price = Math.random();
			var fn = sinon.spy()

			var visitor = ua().transaction(transaction);

			var result = visitor.item(price, fn);

			visitor._context = result._context;
			result.should.eql(visitor, "should return a visitor that is identical except for the context");
			result.should.be.instanceof(ua.Visitor);
			result._context.should.eql(_enqueue.args[1][1], "the transaction params should be persisted as the context of the visitor clone")

			_enqueue.calledTwice.should.equal(true, "#_enqueue should have been called twice, once for the transaction before");
			_enqueue.args[0][0].should.equal("transaction");
			_enqueue.args[1][0].should.equal("item");
			_enqueue.args[1][1].should.have.keys("ti", "ip")
			_enqueue.args[1][1].ti.should.equal(transaction);
			_enqueue.args[1][1].ip.should.equal(price);

			fn.calledOnce.should.equal(true, "callback should have been called once")
		});


		it("should accept arguments (price, quantity) when chained to transaction", function () {
			var transaction = Math.random().toString();
			var price = Math.random();
			var quantity = Math.random();

			var visitor = ua().transaction(transaction);

			var result = visitor.item(price, quantity);

			visitor._context = result._context;
			result.should.eql(visitor, "should return a visitor that is identical except for the context");
			result.should.be.instanceof(ua.Visitor);
			result._context.should.eql(_enqueue.args[1][1], "the transaction params should be persisted as the context of the visitor clone")

			_enqueue.calledTwice.should.equal(true, "#_enqueue should have been called twice, once for the transaction before");
			_enqueue.args[0][0].should.equal("transaction");
			_enqueue.args[1][0].should.equal("item");
			_enqueue.args[1][1].should.have.keys("ti", "ip", "iq")
			_enqueue.args[1][1].ti.should.equal(transaction);
			_enqueue.args[1][1].ip.should.equal(price);
			_enqueue.args[1][1].iq.should.equal(quantity);
		});


		it("should accept arguments (price, quantity, fn) when chained to transaction", function () {
			var transaction = Math.random().toString();
			var price = Math.random();
			var quantity = Math.random();
			var fn = sinon.spy()

			var visitor = ua().transaction(transaction);

			var result = visitor.item(price, quantity, fn);

			visitor._context = result._context;
			result.should.eql(visitor, "should return a visitor that is identical except for the context");
			result.should.be.instanceof(ua.Visitor);
			result._context.should.eql(_enqueue.args[1][1], "the transaction params should be persisted as the context of the visitor clone")

			_enqueue.calledTwice.should.equal(true, "#_enqueue should have been called twice, once for the transaction before");
			_enqueue.args[0][0].should.equal("transaction");
			_enqueue.args[1][0].should.equal("item");
			_enqueue.args[1][1].should.have.keys("ti", "ip", "iq")
			_enqueue.args[1][1].ti.should.equal(transaction);
			_enqueue.args[1][1].ip.should.equal(price);
			_enqueue.args[1][1].iq.should.equal(quantity);

			fn.calledOnce.should.equal(true, "callback should have been called once")
		});


		it("should accept arguments (price, quantity, sku) when chained to transaction", function () {
			var transaction = Math.random().toString();
			var price = Math.random();
			var quantity = Math.random();
			var sku = Math.random().toString();

			var visitor = ua().transaction(transaction);

			var result = visitor.item(price, quantity, sku);

			visitor._context = result._context;
			result.should.eql(visitor, "should return a visitor that is identical except for the context");
			result.should.be.instanceof(ua.Visitor);
			result._context.should.eql(_enqueue.args[1][1], "the transaction params should be persisted as the context of the visitor clone")

			_enqueue.calledTwice.should.equal(true, "#_enqueue should have been called twice, once for the transaction before");
			_enqueue.args[0][0].should.equal("transaction");
			_enqueue.args[1][0].should.equal("item");
			_enqueue.args[1][1].should.have.keys("ti", "ip", "iq", "ic")
			_enqueue.args[1][1].ti.should.equal(transaction);
			_enqueue.args[1][1].ip.should.equal(price);
			_enqueue.args[1][1].iq.should.equal(quantity);
			_enqueue.args[1][1].ic.should.equal(sku);
		});


		it("should accept arguments (price, quantity, sku, fn) when chained to transaction", function () {
			var transaction = Math.random().toString();
			var price = Math.random();
			var quantity = Math.random();
			var sku = Math.random().toString();
			var fn = sinon.spy()

			var visitor = ua().transaction(transaction);

			var result = visitor.item(price, quantity, sku, fn);

			visitor._context = result._context;
			result.should.eql(visitor, "should return a visitor that is identical except for the context");
			result.should.be.instanceof(ua.Visitor);
			result._context.should.eql(_enqueue.args[1][1], "the transaction params should be persisted as the context of the visitor clone")

			_enqueue.calledTwice.should.equal(true, "#_enqueue should have been called twice, once for the transaction before");
			_enqueue.args[0][0].should.equal("transaction");
			_enqueue.args[1][0].should.equal("item");
			_enqueue.args[1][1].should.have.keys("ti", "ip", "iq", "ic")
			_enqueue.args[1][1].ti.should.equal(transaction);
			_enqueue.args[1][1].ip.should.equal(price);
			_enqueue.args[1][1].iq.should.equal(quantity);
			_enqueue.args[1][1].ic.should.equal(sku);

			fn.calledOnce.should.equal(true, "callback should have been called once")
		});


		it("should accept arguments (price, quantity, sku, name) when chained to transaction", function () {
			var transaction = Math.random().toString();
			var price = Math.random();
			var quantity = Math.random();
			var sku = Math.random().toString();
			var name = Math.random().toString();

			var visitor = ua().transaction(transaction);

			var result = visitor.item(price, quantity, sku, name);

			visitor._context = result._context;
			result.should.eql(visitor, "should return a visitor that is identical except for the context");
			result.should.be.instanceof(ua.Visitor);
			result._context.should.eql(_enqueue.args[1][1], "the transaction params should be persisted as the context of the visitor clone")

			_enqueue.calledTwice.should.equal(true, "#_enqueue should have been called twice, once for the transaction before");
			_enqueue.args[0][0].should.equal("transaction");
			_enqueue.args[1][0].should.equal("item");
			_enqueue.args[1][1].should.have.keys("ti", "ip", "iq", "ic", "in")
			_enqueue.args[1][1].ti.should.equal(transaction);
			_enqueue.args[1][1].ip.should.equal(price);
			_enqueue.args[1][1].iq.should.equal(quantity);
			_enqueue.args[1][1].ic.should.equal(sku);
			_enqueue.args[1][1].in.should.equal(name);
		});


		it("should accept arguments (price, quantity, sku, name, fn) when chained to transaction", function () {
			var transaction = Math.random().toString();
			var price = Math.random();
			var quantity = Math.random();
			var sku = Math.random().toString();
			var name = Math.random().toString();
			var fn = sinon.spy()

			var visitor = ua().transaction(transaction);

			var result = visitor.item(price, quantity, sku, name, fn);

			visitor._context = result._context;
			result.should.eql(visitor, "should return a visitor that is identical except for the context");
			result.should.be.instanceof(ua.Visitor);
			result._context.should.eql(_enqueue.args[1][1], "the transaction params should be persisted as the context of the visitor clone")

			_enqueue.calledTwice.should.equal(true, "#_enqueue should have been called twice, once for the transaction before");
			_enqueue.args[0][0].should.equal("transaction");
			_enqueue.args[1][0].should.equal("item");
			_enqueue.args[1][1].should.have.keys("ti", "ip", "iq", "ic", "in")
			_enqueue.args[1][1].ti.should.equal(transaction);
			_enqueue.args[1][1].ip.should.equal(price);
			_enqueue.args[1][1].iq.should.equal(quantity);
			_enqueue.args[1][1].ic.should.equal(sku);
			_enqueue.args[1][1].in.should.equal(name);

			fn.calledOnce.should.equal(true, "callback should have been called once")
		});


		it("should accept arguments (price, quantity, sku, name, variation) when chained to transaction", function () {
			var transaction = Math.random().toString();
			var price = Math.random();
			var quantity = Math.random();
			var sku = Math.random().toString();
			var name = Math.random().toString();
			var variation = Math.random().toString();

			var visitor = ua().transaction(transaction);

			var result = visitor.item(price, quantity, sku, name, variation);

			visitor._context = result._context;
			result.should.eql(visitor, "should return a visitor that is identical except for the context");
			result.should.be.instanceof(ua.Visitor);
			result._context.should.eql(_enqueue.args[1][1], "the transaction params should be persisted as the context of the visitor clone")

			_enqueue.calledTwice.should.equal(true, "#_enqueue should have been called twice, once for the transaction before");
			_enqueue.args[0][0].should.equal("transaction");
			_enqueue.args[1][0].should.equal("item");
			_enqueue.args[1][1].should.have.keys("ti", "ip", "iq", "ic", "in", "iv")
			_enqueue.args[1][1].ti.should.equal(transaction);
			_enqueue.args[1][1].ip.should.equal(price);
			_enqueue.args[1][1].iq.should.equal(quantity);
			_enqueue.args[1][1].ic.should.equal(sku);
			_enqueue.args[1][1].in.should.equal(name);
			_enqueue.args[1][1].iv.should.equal(variation);
		});


		it("should accept arguments (price, quantity, sku, name, variation, fn) when chained to transaction", function () {
			var transaction = Math.random().toString();
			var price = Math.random();
			var quantity = Math.random();
			var sku = Math.random().toString();
			var name = Math.random().toString();
			var variation = Math.random().toString();
			var fn = sinon.spy()

			var visitor = ua().transaction(transaction);

			var result = visitor.item(price, quantity, sku, name, variation, fn);

			visitor._context = result._context;
			result.should.eql(visitor, "should return a visitor that is identical except for the context");
			result.should.be.instanceof(ua.Visitor);
			result._context.should.eql(_enqueue.args[1][1], "the transaction params should be persisted as the context of the visitor clone")

			_enqueue.calledTwice.should.equal(true, "#_enqueue should have been called twice, once for the transaction before");
			_enqueue.args[0][0].should.equal("transaction");
			_enqueue.args[1][0].should.equal("item");
			_enqueue.args[1][1].should.have.keys("ti", "ip", "iq", "ic", "in", "iv")
			_enqueue.args[1][1].ti.should.equal(transaction);
			_enqueue.args[1][1].ip.should.equal(price);
			_enqueue.args[1][1].iq.should.equal(quantity);
			_enqueue.args[1][1].ic.should.equal(sku);
			_enqueue.args[1][1].in.should.equal(name);
			_enqueue.args[1][1].iv.should.equal(variation);

			fn.calledOnce.should.equal(true, "callback should have been called once")
		});


		it("should accept arguments (price, quantity, sku, name, variation, params) when chained to transaction", function () {
			var transaction = Math.random().toString();
			var price = Math.random();
			var quantity = Math.random();
			var sku = Math.random().toString();
			var name = Math.random().toString();
			var variation = Math.random().toString();
			var params = {foo: Math.random().toString()};

			var visitor = ua().transaction(transaction);

			var result = visitor.item(price, quantity, sku, name, variation, params);

			visitor._context = result._context;
			result.should.eql(visitor, "should return a visitor that is identical except for the context");
			result.should.be.instanceof(ua.Visitor);
			result._context.should.eql(_enqueue.args[1][1], "the transaction params should be persisted as the context of the visitor clone")

			_enqueue.calledTwice.should.equal(true, "#_enqueue should have been called twice, once for the transaction before");
			_enqueue.args[0][0].should.equal("transaction");
			_enqueue.args[1][0].should.equal("item");
			_enqueue.args[1][1].should.have.keys("ti", "ip", "iq", "ic", "in", "iv", "foo")
			_enqueue.args[1][1].ti.should.equal(transaction);
			_enqueue.args[1][1].ip.should.equal(price);
			_enqueue.args[1][1].iq.should.equal(quantity);
			_enqueue.args[1][1].ic.should.equal(sku);
			_enqueue.args[1][1].in.should.equal(name);
			_enqueue.args[1][1].iv.should.equal(variation);
			_enqueue.args[1][1].foo.should.equal(params.foo);
		});


		it("should accept arguments (price, quantity, sku, name, variation, params, fn) when chained to transaction", function () {
			var transaction = Math.random().toString();
			var price = Math.random();
			var quantity = Math.random();
			var sku = Math.random().toString();
			var name = Math.random().toString();
			var variation = Math.random().toString();
			var params = {foo: Math.random().toString()};
			var fn = sinon.spy()

			var visitor = ua().transaction(transaction);

			var result = visitor.item(price, quantity, sku, name, variation, params, fn);

			visitor._context = result._context;
			result.should.eql(visitor, "should return a visitor that is identical except for the context");
			result.should.be.instanceof(ua.Visitor);
			result._context.should.eql(_enqueue.args[1][1], "the transaction params should be persisted as the context of the visitor clone")

			_enqueue.calledTwice.should.equal(true, "#_enqueue should have been called twice, once for the transaction before");
			_enqueue.args[0][0].should.equal("transaction");
			_enqueue.args[1][0].should.equal("item");
			_enqueue.args[1][1].should.have.keys("ti", "ip", "iq", "ic", "in", "iv", "foo")
			_enqueue.args[1][1].ti.should.equal(transaction);
			_enqueue.args[1][1].ip.should.equal(price);
			_enqueue.args[1][1].iq.should.equal(quantity);
			_enqueue.args[1][1].ic.should.equal(sku);
			_enqueue.args[1][1].in.should.equal(name);
			_enqueue.args[1][1].iv.should.equal(variation);
			_enqueue.args[1][1].foo.should.equal(params.foo);

			fn.calledOnce.should.equal(true, "callback should have been called once")
		});


		it("should accept arguments (price, quantity, sku, name, variation, params, fn) when params include transaction", function () {
			var price = Math.random();
			var quantity = Math.random();
			var sku = Math.random().toString();
			var name = Math.random().toString();
			var variation = Math.random().toString();
			var params = {ti: Math.random()};
			var fn = sinon.spy()

			var visitor = ua();

			var result = visitor.item(price, quantity, sku, name, variation, params, fn);

			visitor._context = result._context;
			result.should.eql(visitor, "should return a visitor that is identical except for the context");
			result.should.be.instanceof(ua.Visitor);
			result._context.should.eql(_enqueue.args[0][1], "the transaction params should be persisted as the context of the visitor clone")

			_enqueue.calledOnce.should.equal(true, "#_enqueue should have been called once");
			_enqueue.args[0][0].should.equal("item");
			_enqueue.args[0][1].should.have.keys("ti", "ip", "iq", "ic", "in", "iv")
			_enqueue.args[0][1].ip.should.equal(price);
			_enqueue.args[0][1].iq.should.equal(quantity);
			_enqueue.args[0][1].ic.should.equal(sku);
			_enqueue.args[0][1].in.should.equal(name);
			_enqueue.args[0][1].iv.should.equal(variation);
			_enqueue.args[0][1].ti.should.equal(params.ti);

			fn.calledOnce.should.equal(true, "callback should have been called once")
		});


		it("should accept arguments (params, fn) when params include transaction", function () {
			var params = {
				ip: Math.random(),
				iq: Math.random(),
				ic: Math.random().toString(),
				in: Math.random().toString(),
				iv: Math.random().toString(),
				ti: Math.random()
			};
			var fn = sinon.spy()

			var visitor = ua();

			var result = visitor.item(params, fn);

			visitor._context = result._context;
			result.should.eql(visitor, "should return a visitor that is identical except for the context");
			result.should.be.instanceof(ua.Visitor);
			result._context.should.eql(_enqueue.args[0][1], "the transaction params should be persisted as the context of the visitor clone")

			_enqueue.calledOnce.should.equal(true, "#_enqueue should have been called once");
			_enqueue.args[0][0].should.equal("item");
			_enqueue.args[0][1].should.have.keys("ti", "ip", "iq", "ic", "in", "iv")
			_enqueue.args[0][1].ip.should.equal(params.ip);
			_enqueue.args[0][1].iq.should.equal(params.iq);
			_enqueue.args[0][1].ic.should.equal(params.ic);
			_enqueue.args[0][1].in.should.equal(params.in);
			_enqueue.args[0][1].iv.should.equal(params.iv);
			_enqueue.args[0][1].ti.should.equal(params.ti);

			fn.calledOnce.should.equal(true, "callback should have been called once")
		});


		it("should accept arguments (params, fn) when chained to a transaction", function () {
			var transaction = Math.random().toString();
			var params = {
				ip: Math.random(),
				iq: Math.random(),
				ic: Math.random().toString(),
				in: Math.random().toString(),
				iv: Math.random().toString()
			};
			var fn = sinon.spy()

			var visitor = ua().transaction(transaction);

			var result = visitor.item(params, fn);

			visitor._context = result._context;
			result.should.eql(visitor, "should return a visitor that is identical except for the context");
			result.should.be.instanceof(ua.Visitor);
			result._context.should.eql(_enqueue.args[1][1], "the transaction params should be persisted as the context of the visitor clone")

			_enqueue.calledTwice.should.equal(true, "#_enqueue should have been called twice, once for the transaction");
			_enqueue.args[1][0].should.equal("item");
			_enqueue.args[1][1].should.have.keys("ti", "ip", "iq", "ic", "in", "iv")
			_enqueue.args[1][1].ip.should.equal(params.ip);
			_enqueue.args[1][1].iq.should.equal(params.iq);
			_enqueue.args[1][1].ic.should.equal(params.ic);
			_enqueue.args[1][1].in.should.equal(params.in);
			_enqueue.args[1][1].iv.should.equal(params.iv);
			_enqueue.args[1][1].ti.should.equal(transaction);

			fn.calledOnce.should.equal(true, "callback should have been called once")
		});


		it("should allow daisy-chaining to re-use arguments", function () {
			var transaction = Math.random().toString();
			var price = Math.random();
			var quantity = Math.random();
			var sku = Math.random().toString();
			var name = Math.random().toString();
			var name2 = Math.random().toString();
			var variation = Math.random().toString();
			var fn = sinon.spy()

			var visitor = ua().transaction(transaction);

			var result = visitor.item(price, quantity, sku, name, variation).item({in: name2}, fn);

			_enqueue.calledThrice.should.equal(true, "#_enqueue should have been called thrice, once for the transaction before");
			_enqueue.args[0][0].should.equal("transaction");

			_enqueue.args[1][0].should.equal(_enqueue.args[2][0]);
			_enqueue.args[1][1].ti.should.equal(_enqueue.args[2][1].ti, "ti should be equal on both");
			_enqueue.args[1][1].ip.should.equal(_enqueue.args[2][1].ip, "ip should be equal on both");
			_enqueue.args[1][1].iq.should.equal(_enqueue.args[2][1].iq, "iq should be equal on both");
			_enqueue.args[1][1].ic.should.equal(_enqueue.args[2][1].ic, "ic should be equal on both");
			_enqueue.args[1][1].iv.should.equal(_enqueue.args[2][1].iv, "iv should be equal on both");
			_enqueue.args[2][1].in.should.equal(name2, "name should have changed on second item");

			fn.calledOnce.should.equal(true, "callback should have been called once")
		});


		it("should fail without transaction ID", function () {
			var params = {
				ip: Math.random(),
				iq: Math.random(),
				ic: Math.random().toString(),
				in: Math.random().toString(),
				iv: Math.random().toString()
			};
			var fn = sinon.spy()

			var visitor = ua();

			var result = visitor.item(params, fn);

			visitor._context = result._context;
			result.should.eql(visitor, "should return a visitor that is identical except for the context");

			result.should.be.instanceof(ua.Visitor);
			result._context.should.eql({}, "the transaction params should not be persisted")

			_enqueue.called.should.equal(false, "#_enqueue should have not been called once");
			fn.calledOnce.should.equal(true, "callback should have been called once");
			fn.args[0][0].should.be.instanceof(Error);
			fn.thisValues[0].should.equal(visitor);
		});

	});

});










