const expect = require("expect.js");
const eio = require("../");
const env = require("./support/env");

// Disables eslint to capitalise constructor names
/* eslint-disable new-cap */

describe("Transport", () => {
  describe("rememberUpgrade", () => {
    it("should remember websocket connection", (done) => {
      const socket = new eio.Socket();
      expect(socket.transport.name).to.be("polling");

      let timedout = false;
      const timeout = setTimeout(() => {
        timedout = true;
        socket.close();
        done();
      }, 300);

      socket.on("upgrade", (transport) => {
        if (timedout) return;
        clearTimeout(timeout);
        socket.close();
        if (transport.name === "websocket") {
          const socket2 = new eio.Socket({ rememberUpgrade: true });
          expect(socket2.transport.name).to.be("websocket");
        }
        done();
      });
    });

    it("should not remember websocket connection", (done) => {
      const socket = new eio.Socket();
      expect(socket.transport.name).to.be("polling");

      let timedout = false;
      const timeout = setTimeout(() => {
        timedout = true;
        socket.close();
        done();
      }, 300);

      socket.on("upgrade", (transport) => {
        if (timedout) return;
        clearTimeout(timeout);
        socket.close();
        if (transport.name === "websocket") {
          const socket2 = new eio.Socket({ rememberUpgrade: false });
          expect(socket2.transport.name).to.not.be("websocket");
        }
        done();
      });
    });
  });

  describe("public constructors", () => {
    it("should include Transport", () => {
      expect(eio.Transport).to.be.a("function");
    });

    it("should include Polling and WebSocket", () => {
      expect(eio.transports).to.be.an("object");
      expect(eio.transports.polling).to.be.a("function");
      expect(eio.transports.websocket).to.be.a("function");
    });
  });

  describe("transport uris", () => {
    it("should generate an http uri", () => {
      const polling = new eio.transports.polling({
        path: "/engine.io",
        hostname: "localhost",
        secure: false,
        query: { sid: "test" },
        timestampRequests: false,
      });
      expect(polling.uri()).to.contain("http://localhost/engine.io?sid=test");
    });

    it("should generate an http uri w/o a port", () => {
      const polling = new eio.transports.polling({
        path: "/engine.io",
        hostname: "localhost",
        secure: false,
        query: { sid: "test" },
        port: 80,
        timestampRequests: false,
      });
      expect(polling.uri()).to.contain("http://localhost/engine.io?sid=test");
    });

    it("should generate an http uri with a port", () => {
      const polling = new eio.transports.polling({
        path: "/engine.io",
        hostname: "localhost",
        secure: false,
        query: { sid: "test" },
        port: 3000,
        timestampRequests: false,
      });
      expect(polling.uri()).to.contain(
        "http://localhost:3000/engine.io?sid=test",
      );
    });

    it("should generate an https uri w/o a port", () => {
      const polling = new eio.transports.polling({
        path: "/engine.io",
        hostname: "localhost",
        secure: true,
        query: { sid: "test" },
        port: 443,
        timestampRequests: false,
      });
      expect(polling.uri()).to.contain("https://localhost/engine.io?sid=test");
    });

    it("should generate an https uri w/o a port (string)", () => {
      const polling = new eio.transports.polling({
        path: "/engine.io",
        hostname: "localhost",
        secure: true,
        query: { sid: "test" },
        port: "443",
        timestampRequests: false,
      });
      expect(polling.uri()).to.contain("https://localhost/engine.io?sid=test");
    });

    it("should generate an https uri with a port", () => {
      const polling = new eio.transports.polling({
        path: "/engine.io",
        hostname: "localhost",
        secure: true,
        query: { sid: "test" },
        port: 8443,
        timestampRequests: false,
      });
      expect(polling.uri()).to.contain(
        "https://localhost:8443/engine.io?sid=test",
      );
    });

    it("should generate a timestamped uri", () => {
      const polling = new eio.transports.polling({
        path: "/engine.io",
        hostname: "localhost",
        timestampParam: "t",
        timestampRequests: true,
      });
      expect(polling.uri()).to.match(
        /http:\/\/localhost\/engine\.io\?(j=[0-9]+&)?(t=[0-9A-Za-z-_]+)/,
      );
    });

    it("should generate an ipv6 uri", () => {
      const polling = new eio.transports.polling({
        path: "/engine.io",
        hostname: "::1",
        secure: false,
        port: 80,
        timestampRequests: false,
      });
      expect(polling.uri()).to.contain("http://[::1]/engine.io");
    });

    it("should generate an ipv6 uri with port", () => {
      const polling = new eio.transports.polling({
        path: "/engine.io",
        hostname: "::1",
        secure: false,
        port: 8080,
        timestampRequests: false,
      });
      expect(polling.uri()).to.contain("http://[::1]:8080/engine.io");
    });

    it("should generate a ws uri", () => {
      const ws = new eio.transports.websocket({
        path: "/engine.io",
        hostname: "test",
        secure: false,
        query: { transport: "websocket" },
        timestampRequests: false,
      });
      expect(ws.uri()).to.be("ws://test/engine.io?transport=websocket");
    });

    it("should generate a wss uri", () => {
      const ws = new eio.transports.websocket({
        path: "/engine.io",
        hostname: "test",
        secure: true,
        query: {},
        timestampRequests: false,
      });
      expect(ws.uri()).to.be("wss://test/engine.io");
    });

    it("should timestamp ws uris", () => {
      const ws = new eio.transports.websocket({
        path: "/engine.io",
        hostname: "localhost",
        timestampParam: "woot",
        timestampRequests: true,
      });
      expect(ws.uri()).to.match(
        /ws:\/\/localhost\/engine\.io\?woot=[0-9A-Za-z-_]+/,
      );
    });

    it("should generate a ws ipv6 uri", () => {
      const ws = new eio.transports.websocket({
        path: "/engine.io",
        hostname: "::1",
        secure: false,
        port: 80,
        timestampRequests: false,
      });
      expect(ws.uri()).to.be("ws://[::1]/engine.io");
    });

    it("should generate a ws ipv6 uri with port", () => {
      const ws = new eio.transports.websocket({
        path: "/engine.io",
        hostname: "::1",
        secure: false,
        port: 8080,
        timestampRequests: false,
      });
      expect(ws.uri()).to.be("ws://[::1]:8080/engine.io");
    });
  });

  // these are server only
  if (!env.browser) {
    describe("options", () => {
      it("should accept an `agent` option for WebSockets", function (done) {
        if (env.useBuiltinWs) {
          return this.skip();
        }
        const polling = new eio.transports.websocket({
          path: "/engine.io",
          hostname: "localhost",
          agent: {
            addRequest: () => {
              done();
            },
          },
        });
        polling.doOpen();
      });
      it("should accept an `agent` option for XMLHttpRequest", function (done) {
        if (env.useFetch) {
          return this.skip();
        }

        const polling = new eio.transports.polling({
          path: "/engine.io",
          hostname: "localhost",
          agent: {
            addRequest: () => {
              done();
            },
          },
        });
        polling.doOpen();
      });

      describe("for extraHeaders", () => {
        it("should correctly set them for WebSockets", () => {
          const headers = {
            "X-Custom-Header-For-My-Project": "my-secret-access-token",
            Cookie:
              "user_session=NI2JlCKF90aE0sJZD9ZzujtdsUqNYSBYxzlTsvdSUe35ZzdtVRGqYFr0kdGxbfc5gUOkR9RGp20GVKza; path=/; expires=Tue, 07-Apr-2015 18:18:08 GMT; secure; HttpOnly",
          };
          const polling = new eio.transports.websocket({
            path: "/engine.io",
            hostname: "localhost",
            extraHeaders: headers,
          });
          expect(polling.opts.extraHeaders).to.equal(headers);
        });
        it("should correctly set them for XMLHttpRequest", () => {
          const headers = {
            "X-Custom-Header-For-My-Project": "my-secret-access-token",
            Cookie:
              "user_session=NI2JlCKF90aE0sJZD9ZzujtdsUqNYSBYxzlTsvdSUe35ZzdtVRGqYFr0kdGxbfc5gUOkR9RGp20GVKza; path=/; expires=Tue, 07-Apr-2015 18:18:08 GMT; secure; HttpOnly",
          };
          const polling = new eio.transports.polling({
            path: "/engine.io",
            hostname: "localhost",
            extraHeaders: headers,
          });
          expect(polling.opts.extraHeaders).to.equal(headers);
        });
      });

      describe("perMessageDeflate", () => {
        it("should set threshold", function (done) {
          if (env.useBuiltinWs) {
            return this.skip();
          }
          const socket = new eio.Socket({
            transports: ["websocket"],
            perMessageDeflate: { threshold: 0 },
          });
          socket.on("open", () => {
            const ws = socket.transport.ws;
            const send = ws.send;
            ws.send = (data, opts, callback) => {
              ws.send = send;
              ws.send(data, opts, callback);

              expect(opts.compress).to.be(true);
              socket.close();
              done();
            };
            socket.send("hi", { compress: true });
          });
        });

        it("should not compress when the byte size is below threshold", function (done) {
          if (env.useBuiltinWs) {
            return this.skip();
          }
          const socket = new eio.Socket({ transports: ["websocket"] });
          socket.on("open", () => {
            const ws = socket.transport.ws;
            const send = ws.send;
            ws.send = (data, opts, callback) => {
              ws.send = send;
              ws.send(data, opts, callback);

              expect(opts.compress).to.be(false);
              socket.close();
              done();
            };
            socket.send("hi", { compress: true });
          });
        });
      });
    });
  }

  describe("options", () => {
    it("should accept an `extraHeaders` option for XMLHttpRequest in browser", () => {
      const headers = {
        "X-Custom-Header-For-My-Project": "my-secret-access-token",
        Cookie:
          "user_session=NI2JlCKF90aE0sJZD9ZzujtdsUqNYSBYxzlTsvdSUe35ZzdtVRGqYFr0kdGxbfc5gUOkR9RGp20GVKza; path=/; expires=Tue, 07-Apr-2015 18:18:08 GMT; secure; HttpOnly",
      };
      const socket = new eio.Socket({
        transportOptions: {
          polling: {
            extraHeaders: headers,
          },
        },
      });
      expect(socket.transport.name).to.be("polling");
      expect(socket.transport.opts.extraHeaders).to.equal(headers);
    });
  });
});
