/*jslint nomen: true */
/*global Blob, sinon*/
(function (jIO, QUnit, Blob, sinon) {
  "use strict";
  var test = QUnit.test,
    stop = QUnit.stop,
    start = QUnit.start,
    ok = QUnit.ok,
    expect = QUnit.expect,
    deepEqual = QUnit.deepEqual,
    equal = QUnit.equal,
    module = QUnit.module,
    domain = "https://example.org",
    basic_login = "login:passwd";

  /////////////////////////////////////////////////////////////////
  // davStorage constructor
  /////////////////////////////////////////////////////////////////
  module("davStorage.constructor");

  test("Storage store URL", function () {
    var jio = jIO.createJIO({
      type: "dav",
      url: domain
    });

    equal(jio.__type, "dav");
    deepEqual(jio.__storage._url, domain);
    deepEqual(jio.__storage._authorization, undefined);
  });

  test("Storage store basic login", function () {
    var jio = jIO.createJIO({
      type: "dav",
      url: domain,
      basic_login: basic_login
    });

    equal(jio.__type, "dav");
    deepEqual(jio.__storage._url, domain);
    deepEqual(jio.__storage._authorization, "Basic login:passwd");
  });

  /////////////////////////////////////////////////////////////////
  // davStorage.put
  /////////////////////////////////////////////////////////////////
  module("davStorage.put", {
    setup: function () {

      this.server = sinon.fakeServer.create();
      this.server.autoRespond = true;
      this.server.autoRespondAfter = 5;

      this.jio = jIO.createJIO({
        type: "dav",
        url: domain,
        basic_login: basic_login
      });
    },
    teardown: function () {
      this.server.restore();
      delete this.server;
    }
  });

  test("put document", function () {
    var url = domain + "/put1/",
      server = this.server;
    this.server.respondWith("MKCOL", url, [201, {
      "Content-Type": "text/xml"
    }, ""]);

    stop();
    expect(7);

    this.jio.put("/put1/", {})
      .then(function () {
        equal(server.requests.length, 1);
        equal(server.requests[0].method, "MKCOL");
        equal(server.requests[0].url, url);
        equal(server.requests[0].status, 201);
        equal(server.requests[0].requestBody, undefined);
        equal(server.requests[0].responseText, "");
        deepEqual(server.requests[0].requestHeaders, {
          Authorization: "Basic login:passwd",
          "Content-Type": "text/plain;charset=utf-8"
        });
      })
      .fail(function (error) {
        ok(false, error);
      })
      .always(function () {
        start();
      });
  });

  test("reject ID not starting with /", function () {
    stop();
    expect(3);

    this.jio.put("put1/", {})
      .fail(function (error) {
        ok(error instanceof jIO.util.jIOError);
        equal(error.message, "id put1/ is forbidden (no begin /)");
        equal(error.status_code, 400);
      })
      .fail(function (error) {
        ok(false, error);
      })
      .always(function () {
        start();
      });
  });

  test("reject ID not ending with /", function () {
    stop();
    expect(3);

    this.jio.put("/put1", {})
      .fail(function (error) {
        ok(error instanceof jIO.util.jIOError);
        equal(error.message, "id /put1 is forbidden (no end /)");
        equal(error.status_code, 400);
      })
      .fail(function (error) {
        ok(false, error);
      })
      .always(function () {
        start();
      });
  });

  test("reject to store any property", function () {
    stop();
    expect(3);

    this.jio.put("/put1/", {title: "foo"})
      .fail(function (error) {
        ok(error instanceof jIO.util.jIOError);
        equal(error.message, "Can not store properties: title");
        equal(error.status_code, 400);
      })
      .fail(function (error) {
        ok(false, error);
      })
      .always(function () {
        start();
      });
  });

  /////////////////////////////////////////////////////////////////
  // davStorage.remove
  /////////////////////////////////////////////////////////////////
  module("davStorage.remove", {
    setup: function () {

      this.server = sinon.fakeServer.create();
      this.server.autoRespond = true;
      this.server.autoRespondAfter = 5;

      this.jio = jIO.createJIO({
        type: "dav",
        url: domain,
        basic_login: basic_login
      });
    },
    teardown: function () {
      this.server.restore();
      delete this.server;
    }
  });

  test("remove document", function () {
    var url = domain + "/remove1/",
      server = this.server;
    this.server.respondWith("DELETE", url, [204, {
      "Content-Type": "text/xml"
    }, ""]);

    stop();
    expect(7);

    this.jio.remove("/remove1/")
      .then(function () {
        equal(server.requests.length, 1);
        equal(server.requests[0].method, "DELETE");
        equal(server.requests[0].url, url);
        equal(server.requests[0].status, 204);
        equal(server.requests[0].requestBody, undefined);
        equal(server.requests[0].responseText, "");
        deepEqual(server.requests[0].requestHeaders, {
          Authorization: "Basic login:passwd",
          "Content-Type": "text/plain;charset=utf-8"
        });
      })
      .fail(function (error) {
        ok(false, error);
      })
      .always(function () {
        start();
      });
  });

  test("reject ID not starting with /", function () {
    stop();
    expect(3);

    this.jio.remove("remove1/")
      .fail(function (error) {
        ok(error instanceof jIO.util.jIOError);
        equal(error.message, "id remove1/ is forbidden (no begin /)");
        equal(error.status_code, 400);
      })
      .fail(function (error) {
        ok(false, error);
      })
      .always(function () {
        start();
      });
  });

  test("reject ID not ending with /", function () {
    stop();
    expect(3);

    this.jio.remove("/remove1")
      .fail(function (error) {
        ok(error instanceof jIO.util.jIOError);
        equal(error.message, "id /remove1 is forbidden (no end /)");
        equal(error.status_code, 400);
      })
      .fail(function (error) {
        ok(false, error);
      })
      .always(function () {
        start();
      });
  });

  /////////////////////////////////////////////////////////////////
  // davStorage.get
  /////////////////////////////////////////////////////////////////
  module("davStorage.get", {
    setup: function () {

      this.server = sinon.fakeServer.create();
      this.server.autoRespond = true;
      this.server.autoRespondAfter = 5;

      this.jio = jIO.createJIO({
        type: "dav",
        url: domain,
        basic_login: basic_login
      });
    },
    teardown: function () {
      this.server.restore();
      delete this.server;
    }
  });

  test("reject ID not starting with /", function () {
    stop();
    expect(3);

    this.jio.get("get1/")
      .fail(function (error) {
        ok(error instanceof jIO.util.jIOError);
        equal(error.message, "id get1/ is forbidden (no begin /)");
        equal(error.status_code, 400);
      })
      .fail(function (error) {
        ok(false, error);
      })
      .always(function () {
        start();
      });
  });

  test("reject ID not ending with /", function () {
    stop();
    expect(3);

    this.jio.get("/get1")
      .fail(function (error) {
        ok(error instanceof jIO.util.jIOError);
        equal(error.message, "id /get1 is forbidden (no end /)");
        equal(error.status_code, 400);
      })
      .fail(function (error) {
        ok(false, error);
      })
      .always(function () {
        start();
      });
  });

  test("get inexistent document", function () {
    var url = domain + "/inexistent/";
    this.server.respondWith("PROPFIND", url, [404, {
      "Content-Type": "text/html"
    }, "foo"]);

    stop();
    expect(3);

    this.jio.get("/inexistent/")
      .fail(function (error) {
        ok(error instanceof jIO.util.jIOError);
        equal(error.message, "Cannot find document");
        equal(error.status_code, 404);
      })
      .fail(function (error) {
        ok(false, error);
      })
      .always(function () {
        start();
      });
  });

  test("get document without attachment", function () {
    var id = "/id1/";
    this.server.respondWith("PROPFIND", domain + id, [200, {
      "Content-Type": "text/xml"
    }, '<?xml version="1.0" encoding="utf-8"?>' +
        '<D:multistatus xmlns:D="DAV:">' +
        '<D:response xmlns:lp1="DAV:" ' +
        'xmlns:lp2="http://apache.org/dav/props/">' +
        '<D:href>/uploads/</D:href>' +
        '<D:propstat>' +
        '<D:prop>' +
        '<lp1:resourcetype><D:collection/></lp1:resourcetype>' +
        '<lp1:creationdate>2013-10-30T17:19:46Z</lp1:creationdate>' +
        '<lp1:getlastmodified>Wed, 30 Oct 2013 17:19:46 GMT' +
        '</lp1:getlastmodified>' +
        '<lp1:getetag>"240be-1000-4e9f88a305c4e"</lp1:getetag>' +
        '<D:supportedlock>' +
        '<D:lockentry>' +
        '<D:lockscope><D:exclusive/></D:lockscope>' +
        '<D:locktype><D:write/></D:locktype>' +
        '</D:lockentry>' +
        '<D:lockentry>' +
        '<D:lockscope><D:shared/></D:lockscope>' +
        '<D:locktype><D:write/></D:locktype>' +
        '</D:lockentry>' +
        '</D:supportedlock>' +
        '<D:lockdiscovery/>' +
        '<D:getcontenttype>httpd/unix-directory</D:getcontenttype>' +
        '</D:prop>' +
        '<D:status>HTTP/1.1 200 OK</D:status>' +
        '</D:propstat>' +
        '</D:response>' +
        '</D:multistatus>'
      ]);
    stop();
    expect(1);

    this.jio.get(id)
      .then(function (result) {
        deepEqual(result, {
        }, "Check document");
      })
      .fail(function (error) {
        ok(false, error);
      })
      .always(function () {
        start();
      });
  });

  test("get document with attachment", function () {
    var id = "/id1/";
    this.server.respondWith("PROPFIND", domain + id, [200, {
      "Content-Type": "text/xml"
    }, '<?xml version="1.0" encoding="utf-8"?>' +
        '<D:multistatus xmlns:D="DAV:">' +
        '<D:response xmlns:lp1="DAV:" ' +
        'xmlns:lp2="http://apache.org/dav/props/">' +
        '<D:href>/uploads/</D:href>' +
        '<D:propstat>' +
        '<D:prop>' +
        '<lp1:resourcetype><D:collection/></lp1:resourcetype>' +
        '<lp1:creationdate>2013-10-30T17:19:46Z</lp1:creationdate>' +
        '<lp1:getlastmodified>Wed, 30 Oct 2013 17:19:46 GMT' +
        '</lp1:getlastmodified>' +
        '<lp1:getetag>"240be-1000-4e9f88a305c4e"</lp1:getetag>' +
        '<D:supportedlock>' +
        '<D:lockentry>' +
        '<D:lockscope><D:exclusive/></D:lockscope>' +
        '<D:locktype><D:write/></D:locktype>' +
        '</D:lockentry>' +
        '<D:lockentry>' +
        '<D:lockscope><D:shared/></D:lockscope>' +
        '<D:locktype><D:write/></D:locktype>' +
        '</D:lockentry>' +
        '</D:supportedlock>' +
        '<D:lockdiscovery/>' +
        '<D:getcontenttype>httpd/unix-directory</D:getcontenttype>' +
        '</D:prop>' +
        '<D:status>HTTP/1.1 200 OK</D:status>' +
        '</D:propstat>' +
        '</D:response>' +
        '<D:response xmlns:lp1="DAV:" ' +
        'xmlns:lp2="http://apache.org/dav/props/">' +
        '<D:href>/uploads/attachment1</D:href>' +
        '<D:propstat>' +
        '<D:prop>' +
        '<lp1:resourcetype/>' +
        '<lp1:creationdate>2013-10-30T17:19:46Z</lp1:creationdate>' +
        '<lp1:getcontentlength>66</lp1:getcontentlength>' +
        '<lp1:getlastmodified>Wed, 30 Oct 2013 17:19:46 GMT' +
        '</lp1:getlastmodified>' +
        '<lp1:getetag>"20568-42-4e9f88a2ea198"</lp1:getetag>' +
        '<lp2:executable>F</lp2:executable>' +
        '<D:supportedlock>' +
        '<D:lockentry>' +
        '<D:lockscope><D:exclusive/></D:lockscope>' +
        '<D:locktype><D:write/></D:locktype>' +
        '</D:lockentry>' +
        '<D:lockentry>' +
        '<D:lockscope><D:shared/></D:lockscope>' +
        '<D:locktype><D:write/></D:locktype>' +
        '</D:lockentry>' +
        '</D:supportedlock>' +
        '<D:lockdiscovery/>' +
        '</D:prop>' +
        '<D:status>HTTP/1.1 200 OK</D:status>' +
        '</D:propstat>' +
        '</D:response>' +
        '<D:response xmlns:lp1="DAV:" ' +
        'xmlns:lp2="http://apache.org/dav/props/">' +
        '<D:href>/uploads/attachment2</D:href>' +
        '<D:propstat>' +
        '<D:prop>' +
        '<lp1:resourcetype/>' +
        '<lp1:creationdate>2013-10-30T17:19:46Z</lp1:creationdate>' +
        '<lp1:getcontentlength>25</lp1:getcontentlength>' +
        '<lp1:getlastmodified>Wed, 30 Oct 2013 17:19:46 GMT' +
        '</lp1:getlastmodified>' +
        '<lp1:getetag>"21226-19-4e9f88a305c4e"</lp1:getetag>' +
        '<lp2:executable>F</lp2:executable>' +
        '<D:supportedlock>' +
        '<D:lockentry>' +
        '<D:lockscope><D:exclusive/></D:lockscope>' +
        '<D:locktype><D:write/></D:locktype>' +
        '</D:lockentry>' +
        '<D:lockentry>' +
        '<D:lockscope><D:shared/></D:lockscope>' +
        '<D:locktype><D:write/></D:locktype>' +
        '</D:lockentry>' +
        '</D:supportedlock>' +
        '<D:lockdiscovery/>' +
        '</D:prop>' +
        '<D:status>HTTP/1.1 200 OK</D:status>' +
        '</D:propstat>' +
        '</D:response>' +
        '</D:multistatus>'
      ]);
    stop();
    expect(1);

    this.jio.get(id)
      .then(function (result) {
        deepEqual(result, {
          "_attachments": {
            attachment1: {},
            attachment2: {}
          }
        }, "Check document");
      })
      .fail(function (error) {
        ok(false, error);
      })
      .always(function () {
        start();
      });
  });

  /////////////////////////////////////////////////////////////////
  // davStorage.putAttachment
  /////////////////////////////////////////////////////////////////
  module("davStorage.putAttachment", {
    setup: function () {

      this.server = sinon.fakeServer.create();
      this.server.autoRespond = true;
      this.server.autoRespondAfter = 5;

      this.jio = jIO.createJIO({
        type: "dav",
        url: domain,
        basic_login: basic_login
      });
    },
    teardown: function () {
      this.server.restore();
      delete this.server;
    }
  });

  test("reject ID not starting with /", function () {
    stop();
    expect(3);

    this.jio.putAttachment(
      "putAttachment1/",
      "attachment1",
      new Blob([""])
    )
      .fail(function (error) {
        ok(error instanceof jIO.util.jIOError);
        equal(error.message, "id putAttachment1/ is forbidden (no begin /)");
        equal(error.status_code, 400);
      })
      .fail(function (error) {
        ok(false, error);
      })
      .always(function () {
        start();
      });
  });

  test("reject ID not ending with /", function () {
    stop();
    expect(3);

    this.jio.putAttachment(
      "/putAttachment1",
      "attachment1",
      new Blob([""])
    )
      .fail(function (error) {
        ok(error instanceof jIO.util.jIOError);
        equal(error.message, "id /putAttachment1 is forbidden (no end /)");
        equal(error.status_code, 400);
      })
      .fail(function (error) {
        ok(false, error);
      })
      .always(function () {
        start();
      });
  });

  test("reject attachment with / character", function () {
    stop();
    expect(3);

    this.jio.putAttachment(
      "/putAttachment1/",
      "attach/ment1",
      new Blob([""])
    )
      .fail(function (error) {
        ok(error instanceof jIO.util.jIOError);
        equal(error.message, "attachment attach/ment1 is forbidden");
        equal(error.status_code, 400);
      })
      .fail(function (error) {
        ok(false, error);
      })
      .always(function () {
        start();
      });
  });

  test("putAttachment document", function () {
    var blob = new Blob(["foo"]),
      url = domain + "/putAttachment1/attachment1",
      server = this.server;
    this.server.respondWith("PUT", url, [204, {
      "Content-Type": "text/xml"
    }, ""]);

    stop();
    expect(7);

    this.jio.putAttachment(
      "/putAttachment1/",
      "attachment1",
      blob
    )
      .then(function () {
        equal(server.requests.length, 1);
        equal(server.requests[0].method, "PUT");
        equal(server.requests[0].url, url);
        equal(server.requests[0].status, 204);
        equal(server.requests[0].requestBody, blob);
        equal(server.requests[0].responseText, "");
        deepEqual(server.requests[0].requestHeaders, {
          Authorization: "Basic login:passwd",
          "Content-Type": "text/plain;charset=utf-8"
        });
      })
      .fail(function (error) {
        ok(false, error);
      })
      .always(function () {
        start();
      });
  });

  /////////////////////////////////////////////////////////////////
  // davStorage.removeAttachment
  /////////////////////////////////////////////////////////////////
  module("davStorage.removeAttachment", {
    setup: function () {

      this.server = sinon.fakeServer.create();
      this.server.autoRespond = true;
      this.server.autoRespondAfter = 5;

      this.jio = jIO.createJIO({
        type: "dav",
        url: domain,
        basic_login: basic_login
      });
    },
    teardown: function () {
      this.server.restore();
      delete this.server;
    }
  });

  test("reject ID not starting with /", function () {
    stop();
    expect(3);

    this.jio.removeAttachment(
      "removeAttachment1/",
      "attachment1"
    )
      .fail(function (error) {
        ok(error instanceof jIO.util.jIOError);
        equal(error.message, "id removeAttachment1/ is forbidden (no begin /)");
        equal(error.status_code, 400);
      })
      .fail(function (error) {
        ok(false, error);
      })
      .always(function () {
        start();
      });
  });

  test("reject ID not ending with /", function () {
    stop();
    expect(3);

    this.jio.removeAttachment(
      "/removeAttachment1",
      "attachment1"
    )
      .fail(function (error) {
        ok(error instanceof jIO.util.jIOError);
        equal(error.message, "id /removeAttachment1 is forbidden (no end /)");
        equal(error.status_code, 400);
      })
      .fail(function (error) {
        ok(false, error);
      })
      .always(function () {
        start();
      });
  });

  test("reject attachment with / character", function () {
    stop();
    expect(3);

    this.jio.removeAttachment(
      "/removeAttachment1/",
      "attach/ment1"
    )
      .fail(function (error) {
        ok(error instanceof jIO.util.jIOError);
        equal(error.message, "attachment attach/ment1 is forbidden");
        equal(error.status_code, 400);
      })
      .fail(function (error) {
        ok(false, error);
      })
      .always(function () {
        start();
      });
  });

  test("removeAttachment document", function () {
    var url = domain + "/removeAttachment1/attachment1",
      server = this.server;
    this.server.respondWith("DELETE", url, [204, {
      "Content-Type": "text/xml"
    }, ""]);

    stop();
    expect(7);

    this.jio.removeAttachment(
      "/removeAttachment1/",
      "attachment1"
    )
      .then(function () {
        equal(server.requests.length, 1);
        equal(server.requests[0].method, "DELETE");
        equal(server.requests[0].url, url);
        equal(server.requests[0].status, 204);
        equal(server.requests[0].requestBody, undefined);
        equal(server.requests[0].responseText, "");
        deepEqual(server.requests[0].requestHeaders, {
          Authorization: "Basic login:passwd",
          "Content-Type": "text/plain;charset=utf-8"
        });
      })
      .fail(function (error) {
        ok(false, error);
      })
      .always(function () {
        start();
      });
  });

  test("remove inexistent attachment", function () {
    var url = domain + "/removeAttachment1/attachment1";
    this.server.respondWith("DELETE", url, [404, {
      "Content-Type": "text/xml"
    }, ""]);

    stop();
    expect(3);

    this.jio.removeAttachment(
      "/removeAttachment1/",
      "attachment1"
    )
      .then(function () {
        ok(false);
      })
      .fail(function (error) {
        ok(error instanceof jIO.util.jIOError);
        equal(error.message, "Cannot find attachment: /removeAttachment1/ " +
                             ", attachment1");
        equal(error.status_code, 404);
      })
      .always(function () {
        start();
      });
  });

  /////////////////////////////////////////////////////////////////
  // davStorage.getAttachment
  /////////////////////////////////////////////////////////////////
  module("davStorage.getAttachment", {
    setup: function () {

      this.server = sinon.fakeServer.create();
      this.server.autoRespond = true;
      this.server.autoRespondAfter = 5;

      this.jio = jIO.createJIO({
        type: "dav",
        url: domain,
        basic_login: basic_login
      });
    },
    teardown: function () {
      this.server.restore();
      delete this.server;
    }
  });

  test("reject ID not starting with /", function () {
    stop();
    expect(3);

    this.jio.getAttachment(
      "getAttachment1/",
      "attachment1"
    )
      .fail(function (error) {
        ok(error instanceof jIO.util.jIOError);
        equal(error.message, "id getAttachment1/ is forbidden (no begin /)");
        equal(error.status_code, 400);
      })
      .fail(function (error) {
        ok(false, error);
      })
      .always(function () {
        start();
      });
  });

  test("reject ID not ending with /", function () {
    stop();
    expect(3);

    this.jio.getAttachment(
      "/getAttachment1",
      "attachment1"
    )
      .fail(function (error) {
        ok(error instanceof jIO.util.jIOError);
        equal(error.message, "id /getAttachment1 is forbidden (no end /)");
        equal(error.status_code, 400);
      })
      .fail(function (error) {
        ok(false, error);
      })
      .always(function () {
        start();
      });
  });

  test("reject attachment with / character", function () {
    stop();
    expect(3);

    this.jio.getAttachment(
      "/getAttachment1/",
      "attach/ment1"
    )
      .fail(function (error) {
        ok(error instanceof jIO.util.jIOError);
        equal(error.message, "attachment attach/ment1 is forbidden");
        equal(error.status_code, 400);
      })
      .fail(function (error) {
        ok(false, error);
      })
      .always(function () {
        start();
      });
  });

  test("getAttachment document", function () {
    var url = domain + "/getAttachment1/attachment1",
      server = this.server;
    this.server.respondWith("GET", url, [200, {
      "Content-Type": "text/plain"
    }, "foo\nbaré"]);

    stop();
    expect(10);

    this.jio.getAttachment(
      "/getAttachment1/",
      "attachment1"
    )
      .then(function (result) {
        equal(server.requests.length, 1);
        equal(server.requests[0].method, "GET");
        equal(server.requests[0].url, url);
        equal(server.requests[0].status, 200);
        equal(server.requests[0].requestBody, undefined);
        equal(server.requests[0].responseText, "foo\nbaré");
        deepEqual(server.requests[0].requestHeaders, {
          Authorization: "Basic login:passwd"
        });

        ok(result instanceof Blob, "Data is Blob");
        deepEqual(result.type, "text/plain", "Check mimetype");
        return jIO.util.readBlobAsText(result);
      })
      .then(function (result) {
        equal(result.target.result, "foo\nbaré",
              "Attachment correctly fetched");
      })
      .fail(function (error) {
        ok(false, error);
      })
      .always(function () {
        start();
      });
  });

  test("get inexistent attachment", function () {
    var url = domain + "/getAttachment1/attachment1";
    this.server.respondWith("GET", url, [404, {
      "Content-Type": "text/xml"
    }, ""]);

    stop();
    expect(3);

    this.jio.getAttachment(
      "/getAttachment1/",
      "attachment1"
    )
      .then(function () {
        ok(false);
      })
      .fail(function (error) {
        ok(error instanceof jIO.util.jIOError);
        equal(error.message, "Cannot find attachment: /getAttachment1/ " +
                             ", attachment1");
        equal(error.status_code, 404);
      })
      .always(function () {
        start();
      });
  });

}(jIO, QUnit, Blob, sinon));