var assert = require("assert");
var path = require("path");
var fse = require("fs-extra");
var local = path.join.bind(path, __dirname);

describe("Stash", function() {
  var NodeGit = require("../../");
  var Repository = NodeGit.Repository;
  var Stash = NodeGit.Stash;

  var reposPath = local("../repos/workdir");

  before(function() {
    var test = this;
    return Repository.open(reposPath)
      .then(function(repository) {
        test.repository = repository;
      });
  });

  it("gets no stashes on clean working directory", function() {
    var stashes = [];
    var stashCb = function(index, message, oid) {
      stashes.push({index: index, message: message, oid: oid});
    };

    return Stash.foreach(this.repository, stashCb)
      .then(function() {
        assert.equal(stashes.length, 0);
      });
  });

  function saveDropStash(repo, stashMessage) {
    const fileName = "README.md";
    const fileContent = "Cha-cha-cha-chaaaaaangessssss";
    const filePath = path.join(repo.workdir(), fileName);
    let oldContent;
    let stashes = [];
    let stashOid;

    return fse.readFile(filePath)
      .then((content) => {
        oldContent = content;
        return fse.writeFile(filePath, fileContent);
      })
      .then(() => repo.defaultSignature())
      .then((signature) => {
        return Stash.save(repo, signature, stashMessage, 0);
      })
      .then((oid) => {
        stashOid = oid;
        var stashCb = function(index, message, oid) {
          stashes.push({index: index, message: message, oid: oid});
        };

        return Stash.foreach(repo, stashCb);
      })
      .then(() => {
        assert.equal(stashes.length, 1);
        assert.equal(stashes[0].index, 0);
        const expectedMessage = !stashMessage ?
          "WIP on master: 32789a7 Fixes EJS not being installed via NPM" :
          "On master: " + stashMessage;
        assert.equal(stashes[0].message, expectedMessage);
        assert.equal(stashes[0].oid.toString(), stashOid.toString());

        return Stash.drop(repo, 0);
      })
      .then(() => {
        stashes = [];
        var stashCb = (index, message, oid) => {
          stashes.push({index: index, message: message, oid: oid});
        };

        return Stash.foreach(repo, stashCb);
      })
      .then(() => {
        assert.equal(stashes.length, 0);
      })
      .catch((e) => {
        return fse.writeFile(filePath, oldContent)
          .then(() => {
            return Promise.reject(e);
          });
      });
  }

  it("can save and drop a stash", function() {
    return saveDropStash(this.repository, "stash test");
  });

  it("can save a stash with no message and drop it", function() {
    return saveDropStash(this.repository, null);
  });

  it("can save and pop a stash", function() {
    const fileNameA = "README.md";
    const fileNameB = "install.js";
    let oldContentA;
    let oldContentB;
    const fileContent = "Cha-cha-cha-chaaaaaangessssss";
    const repo = this.repository;
    const filePathA = path.join(repo.workdir(), fileNameA);
    const filePathB = path.join(repo.workdir(), fileNameB);
    const stashMessage = "stash test";

    return fse.readFile(filePathA, "utf-8")
      .then((content) => {
        oldContentA = content;
        return fse.writeFile(filePathA, fileContent);
      })
      .then(() => {
        return fse.readFile(filePathB, "utf-8");
      })
      .then((content) => {
        oldContentB = content;
        return fse.writeFile(filePathB, fileContent);
      })
      .then(() => repo.defaultSignature())
      .then((signature) => {
        return Stash.save(repo, signature, stashMessage, 0);
      })
      .then(() => {
        return fse.readFile(filePathA, "utf-8");
      })
      .then((content) => {
        assert.equal(oldContentA, content);
        return fse.readFile(filePathB, "utf-8");
      })
      .then((content) => {
        assert.equal(oldContentB, content);
        return Stash.pop(repo, 0);
      })
      .then(() => {
        return fse.readFile(filePathA, "utf-8");
      })
      .then((content) => {
        assert.equal(fileContent, content);
        return fse.readFile(filePathB, "utf-8");
      })
      .then((content) => {
        assert.equal(fileContent, content);
      });
  });

  it("can save a stash, change files, and fail to pop stash", function() {
      const fileName = "README.md";
      const fileContent = "Cha-cha-cha-chaaaaaangessssss";
      const fileContent2 = "Somewhere over the repo, changes were made.";
      const repo = this.repository;
      const filePath = path.join(repo.workdir(), fileName);
      let oldContent;
      const stashMessage = "stash test";

      return fse.readFile(filePath)
        .then((content) => {
          oldContent = content;
          return fse.writeFile(filePath, fileContent);
        })
        .then(() => repo.defaultSignature())
        .then((signature) => {
          return Stash.save(repo, signature, stashMessage, 0);
        })
        .then(() => {
          return fse.writeFile(filePath, fileContent2);
        })
        .then(() => {
          return Stash.pop(repo, 0);
        })
        .catch((reason) => {
          if (reason.message !== "1 conflict prevents checkout") {
            throw reason;
          } else {
            return Promise.resolve();
          }
        });
  });

  it("can save, apply, then drop the stash", function() {
      const fileName = "README.md";
      const fileContent = "Cha-cha-cha-chaaaaaangessssss";
      const repo = this.repository;
      const filePath = path.join(repo.workdir(), fileName);
      let oldContent;
      const stashMessage = "stash test";

      return fse.readFile(filePath)
        .then((content) => {
          oldContent = content;
          return fse.writeFile(filePath, fileContent);
        })
        .then(() => repo.defaultSignature())
        .then((signature) => {
          return Stash.save(repo, signature, stashMessage, 0);
        })
        .then(() => {
          return Stash.apply(repo, 0);
        })
        .then(() => {
          return Stash.drop(repo, 0);
        }, () => {
          throw new Error("Unable to drop stash after apply.");
        })
        .then(() => {
          return Stash.drop(repo, 0);
        })
        .catch((reason) => {
          if (reason.message !== "reference 'refs/stash' not found") {
            throw reason;
          }
        });
  });

  it("can save multiple stashes and pop an arbitrary stash", function() {
    const fileName = "README.md";
    const fileContentA = "Hi. It's me. I'm the dog. My name is the dog.";
    const fileContentB = "Everyone likes me. I'm cute.";
    const fileContentC =
      "I think I will bark at nothing now. Ba. Ba. Baba Baba.";
    const repo = this.repository;
    const filePath = path.join(repo.workdir(), fileName);
    let oldContent;
    const stashMessageA = "stash test A";
    const stashMessageB = "stash test B";
    const stashMessageC = "stash test C";

    const writeAndStash = (path, content, message) => {
      return fse.writeFile(path, content)
        .then(() => repo.defaultSignature())
        .then((signature) => {
          return Stash.save(repo, signature, message, 0);
        });
    };

    return fse.readFile(filePath, "utf-8")
      .then((content) => {
        oldContent = content;
        return writeAndStash(filePath, fileContentA, stashMessageA);
      })
      .then(() => {
        return writeAndStash(filePath, fileContentB, stashMessageB);
      })
      .then(() => {
        return writeAndStash(filePath, fileContentC, stashMessageC);
      })
      .then(() => {
        return fse.readFile(filePath, "utf-8");
      })
      .then((content) => {
        assert.equal(oldContent, content);
        return Stash.pop(repo, 1);
      })
      .then(() => {
        return fse.readFile(filePath, "utf-8");
      })
      .then((content) => {
        assert.equal(fileContentB, content);
      });
  });
});
