/*global describe it before after beforeEach afterEach define*/
"use strict";
"use server";
"use blacklist";

require("c9/inline-mocha")(module);

if (typeof define === "undefined") {
    require("amd-loader");
    require("../../test/setup_paths");
}

var fs = require("fs");
var http = require('http');
var baseTest = require('../c9.api/base_test');
var child = require("child_process");

var expect = require("chai").expect;
var assert = require("assert");
var sinon = require("sinon");
var join = require("path").join;

var HOST = "localhost:16565";
var USERNAME = "fjakobs";
var PASSWORD = "open";
var BASE = "/tmp/c9.ide.example";
var VERBOSE = false;
var PID = 123;

describe("cli.publish", function(){
    this.timeout(10000);
    
    var services;
    
    before(function(next) {
        baseTest(function (err, s) {
            // Services can be tested immediately by mocking API signatures params - (req, res, next) - or (user, params, callback)
            services = s;
            HOST = "localhost:" + s.apiPort;
            next(err);
        });
    });

    describe("publish, unpublish and list", function(done) {
        var packagePath = join(BASE, "package.json");
        var readmePath = join(BASE, "README.md");
        var packageJson, readmeMD;
        
        var json = {
          "name": "c9.ide.example",
          "latest": "1.0.0",
          "owner": "https://api.$DOMAIN/user/2000",
          "enabled": true,
          "categories": [
             "example"
          ],
          "repository": {
             "type": "git",
             "url": "http://github.com/javruben/example.git"
          },
          "longname": "c9.ide.example",
          "website": "",
          "description": "",
          "star_count": 0,
          "star_total": 0,
          
          "installs": 0,
          "screenshots": [
            // TODO Screenshots are broken
            //  "example"
          ]
       };
        
        before(function(done){
            // Create git repo that contains a plugin we'll use to test
            var p = child.spawn(join(__dirname, "publish_test.git.sh"));
            
            if (VERBOSE) {
                p.stdout.on("data", function(c){
                    process.stdout.write(c.toString("utf8"));
                });
                p.stderr.on("data", function(c){
                    process.stderr.write(c.toString("utf8"));
                });
            }
            p.on("close", function(code){
                if (code) 
                    return done(new Error("Git setup failed"));
                
                packageJson = fs.readFileSync(packagePath, "utf8");
                readmeMD = fs.readFileSync(readmePath, "utf8");
                
                done();
            });
        });
        
        it("should be fine if json.permissions is missing as it defaults to world", function(done){
            fs.writeFileSync(packagePath, packageJson.replace(/"permissions[\s\S]*?\],/, ""));
            runCLI("publish", ["major"], function(err, stdout, stderr){
                assert(!err, err);
                done();
            });
        });
        
        it("should warn if the package.json is missing", function(done){
            fs.unlinkSync(packagePath);
            runCLI("publish", ["major"], function(err, stdout, stderr){
                expect(stderr).to.match(/ERROR: Could not find package.json/);
                done();
            });
        });
        it("should fail if the package.json cannot be parsed", function(done){
            fs.writeFileSync(packagePath, packageJson + "!@#!@#", "utf8");
            runCLI("publish", ["major"], function(err, stdout, stderr){
                expect(stderr).to.match(/ERROR: Could not parse package.json/);
                done();
            });
        });
        it("should fail if the name in the package.json is missing", function(done){
            fs.writeFileSync(packagePath, packageJson.replace('"name": "c9.ide.example",', ''));
            runCLI("publish", ["major"], function(err, stdout, stderr){
                expect(stderr).to.match(/ERROR: Missing name property in package.json/);
                done();
            });
        });
        it("should fail if the name in the package.json contains invalid characters", function(done){
            fs.writeFileSync(packagePath, packageJson.replace('c9.ide.example', 'c9-ide-example'));
            runCLI("publish", ["major"], function(err, stdout, stderr){
                expect(stderr).to.match(/ERROR: Package name can only contain/);
                done();
            });
        });
        it("should fail if the name in the package.json is not equal to the directory", function(done){
            fs.writeFileSync(packagePath, packageJson.replace('"name": "c9.ide.example"', '"name": "wrongname"'));
            runCLI("publish", ["major"], function(err, stdout, stderr){
                expect(stderr).to.match(/WARNING: The name property in package.json is not equal to the directory name/);
                done();
            });
        });
        it("should fail if the repository in the package.json is missing", function(done){
            fs.writeFileSync(packagePath, packageJson.replace(/"repository[\s\S]*?\},/, ""));
            runCLI("publish", ["major"], function(err, stdout, stderr){
                expect(stderr).to.match(/ERROR: Missing repository property in package.json/);
                done();
            });
        });
        it("should fail if the category length is < 1 in the package.json is missing", function(done){
            fs.writeFileSync(packagePath, packageJson.replace(/"categories[\s\S]*?\],/, ""));
            runCLI("publish", ["major"], function(err, stdout, stderr){
                expect(stderr).to.match(/ERROR: At least one category is required in package.json/);
                done();
            });
        });
        it("should fail if json.permissions is not org or world", function(done){
            var parsedJson = JSON.parse(packageJson);
            parsedJson.permissions = "notvalid";
            fs.writeFileSync(packagePath, JSON.stringify(parsedJson));
            runCLI("publish", ["major"], function(err, stdout, stderr){
                console.log("stdout: ", stdout)
                expect(stderr).to.match(/ERROR: Permissions must be/);
                done();
            });
        });
        it("should warn if a plugin is not listed in the package.json", function(done){
            fs.writeFileSync(packagePath, packageJson.replace('"example": {}', ''));
            runCLI("publish", ["major"], function(err, stdout, stderr){
                expect(stderr).to.match(/WARNING: Plugin 'example.js' is not listed in package.json./);
                done();
            });
        });
        it("should warn if the README.md is missing", function(done){
            fs.writeFileSync(packagePath, packageJson);
            fs.unlink(readmePath);
            runCLI("publish", ["major"], function(err, stdout, stderr){
                expect(stderr).to.match(/WARNING: README.md is missing./);
                done();
            });
        });
        it("should publish when using force and increase the patch version", function(done){
            var strJson = packageJson.replace(/"version": "[\d\.]+"/, '"version": "0.0.0"');
            fs.writeFileSync(packagePath, strJson);
            fs.writeFileSync(readmePath, readmeMD);
            runCLI("publish", ["patch", "--force", "--tag"], function(err, stdout, stderr){
                assert(!err, err);
                expect(stdout).to.match(/Successfully published version 0.0.1/);
                
                runCLI("list", ["--json"], function(err, stdout, stderr){
                    assert(!err, err);
                    
                    json.latest = "0.0.1";
                    
                    var list = JSON.parse(stdout);
                    if (!list.some(function(item){
                        if (item.name == "c9.ide.example") {
                            expect(item).deep.equal(json);
                            return true;
                        }
                    })) throw new Error("Could not find plugin in list");
                    
                    done();
                });
            });
        });
        it("should increase the minor version", function(done){
            fs.writeFileSync(readmePath, readmeMD);
            runCLI("publish", ["minor"], function(err, stdout, stderr){
                assert(!err, err);
                expect(stdout).to.match(/Successfully published version 0.1.0/);
                
                runCLI("list", ["--json"], function(err, stdout, stderr){
                    assert(!err, err);
                    
                    json.latest = "0.1.0";
                    
                    var list = JSON.parse(stdout);
                    if (!list.some(function(item){
                        if (item.name == "c9.ide.example") {
                            expect(item).deep.equal(json);
                            return true;
                        }
                    })) throw new Error("Could not find plugin in list");
                    
                    done();
                });
            });
        });
        it("should increase the major version", function(done){
            runCLI("publish", ["major"], function(err, stdout, stderr){
                assert(!err, err);
                expect(stdout).to.match(/Successfully published version 1.0.0/);
                
                runCLI("list", ["--json"], function(err, stdout, stderr){
                    assert(!err, err);
                    
                    json.latest = "1.0.0";
                    
                    var list = JSON.parse(stdout);
                    if (!list.some(function(item){
                        if (item.name == "c9.ide.example") {
                            expect(item).deep.equal(json);
                            return true;
                        }
                    })) throw new Error("Could not find plugin in list");
                    
                    done();
                });
            });
        });
        it("should hide the package when it is unpublished", function(done){
            runCLI("unpublish", [], function(err, stdout, stderr){
                assert(!err, err);
                expect(stdout).to.match(/Successfully disabled package/);
                
                runCLI("list", ["--json"], function(err, stdout, stderr){
                    assert(!err, err);
                    expect(stdout).to.not.match(/c9\.ide\.example/);
                    done();
                });
            });
        });
    });
    
    describe("install and remove (uninstall)", function() {
        var pluginDir = join(process.env.HOME, ".c9/plugins/c9.ide.example");
        
        // Lets make sure there is at least one package in the database
        before(function(done){
            // Create git repo that contains a plugin we'll use to test
            var p = child.spawn(join(__dirname, "publish_test.git.sh"));
            
            if (VERBOSE) {
                p.stdout.on("data", function(c){
                    process.stdout.write(c.toString("utf8"));
                });
                p.stderr.on("data", function(c){
                    process.stderr.write(c.toString("utf8"));
                });
            }
            p.on("close", function(code){
                if (code) 
                    return done(new Error("Git setup failed"));
                
                runCLI("publish", ["10.0.0"], function(err, stdout, stderr){
                    done();
                });
            });
        });
        
        it("should install a package locally", function(done){
            runCLI("install", ["--local", "c9.ide.example"], function(err, stdout, stderr){
                expect(stdout).to.match(/Successfully installed c9.ide.example@10.0.0/);
                expect(fs.existsSync(pluginDir)).ok;
                done();
            });
        });
        it("should warn if a package is already installed", function(done){
            runCLI("install", ["--debug", "c9.ide.example"], function(err, stdout, stderr){
                expect(stderr).to.match(/WARNING: Directory not empty/);
                done();
            });
        });
        it("should install a package in debug mode", function(done){
            runCLI("install", ["--force", "--debug", "c9.ide.example"], function(err, stdout, stderr){
                expect(stdout).to.match(/Successfully installed c9.ide.example/);
                expect(fs.existsSync(join(pluginDir, "/.git"))).ok;
                done();
            });
        });
        it("should install a package via the database", function(done){
            runCLI("install", ["c9.ide.example", "--force"], function(err, stdout, stderr){
                expect(stdout).to.match(/Successfully installed c9.ide.example/);
                
                // @TODO check if it's actually in the database - add list --own to cli
                
                done();
            });
        });
        // it("should install a package with a specific version via the database", function(done){
        //     runCLI("install", ["c9.ide.example@1.0.0", "--force"], function(err, stdout, stderr){
        //         console.log(stdout, stderr);
        //         expect(stdout).to.match(/Successfully installed c9.ide.example@1.0.0/);
                
        //         // @TODO check if it's actually in the database - add list --own to cli
                
        //         done();
        //     });
        // });
        it("should remove a package locally", function(done){
            runCLI("remove", ["--local", "c9.ide.example"], function(err, stdout, stderr){
                expect(stdout).to.match(/Successfully removed c9.ide.example/);
                expect(fs.existsSync(pluginDir)).not.ok;
                done();
            });
        });
        it("should remove a from the database", function(done){
            runCLI("remove", ["c9.ide.example"], function(err, stdout, stderr){
                expect(stdout).to.match(/Successfully removed c9.ide.example/);
                
                // @TODO check if it's actually in the database - add list --own to cli
                
                done();
            });
        });
    });
});

function runCLI(command, options, callback){
    var env = Object.create(process.env);
    env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0;
    env["C9_APIHOST"] = HOST;
    env["C9_PID"] = PID;
    env["C9_TEST_AUTH"] = USERNAME + ":" + PASSWORD;
    env["C9_TEST_MODE"] = 1;
    
    options.push("--verbose");
    var p = child.spawn(join(__dirname, "../../bin/c9"), [command].concat(options), {
        cwd: BASE,
        env: env
    });
    
    if (VERBOSE)
        process.stdout.write("\n");
    
    var stdout = "";
    p.stdout.on("data", function(c){
        c = c.toString("utf8");
        stdout += c;
        if (VERBOSE) process.stdout.write(c);
    });
    var stderr = "";
    p.stderr.on("data", function(c){
        c = c.toString("utf8");
        stderr += c;
        if (VERBOSE) process.stderr.write(c);
    });
    p.on("close", function(code){
        if (code) return callback(new Error(stderr), stdout, stderr);
        callback(null, stdout, stderr);
    });
}