/*!
 * Module dependencies.
 */

var PhoneGap = require('../../lib/phonegap');
var events = require('events');
var cordova = require('../../lib/cordova');
var fs = require('fs');
var path = require('path');
var shell = require('shelljs');
var phonegap;
var processSpy;
var options;

/*!
 * Specification: phonegap.cordova(options, [callback])
 */

describe('phonegap.cordova(options, [callback])', function () {
    beforeEach(function () {
        phonegap = new PhoneGap();
        options = {
            cmd: 'cordova build ios'
        };
        spyOn(cordova.util, 'listPlatforms').andReturn(['ios']);
        processSpy = {
            stdout: new events.EventEmitter(),
            stderr: new events.EventEmitter()
        };
        spyOn(shell, 'exec').andReturn(processSpy);
        // disable adding phonegap.js
        spyOn(cordova.util, 'isCordova').andReturn('/some/path');
        spyOn(fs, 'existsSync').andReturn(false);
        spyOn(shell, 'cp');
        // disable phonegap.js deprecation warning
        spyOn(shell, 'grep').andReturn('');
    });

    it('should require options', function () {
        expect(function () {
            options = undefined;
            phonegap.cordova(options, function (e) {});
        }).toThrow();
    });

    it('should require options.cmd', function () {
        expect(function () {
            options.cmd = undefined;
            phonegap.cordova(options, function (e) {});
        }).toThrow();
    });

    it('should not require callback', function () {
        expect(function () {
            phonegap.cordova(options);
        }).not.toThrow();
    });

    it('should return itself', function () {
        expect(phonegap.cordova(options)).toEqual(phonegap);
    });

    it('should try to execute the cordova command', function () {
        phonegap.cordova(options);
        expect(shell.exec).toHaveBeenCalled();
        expect(shell.exec.mostRecentCall.args[0]).toMatch(options.cmd);
    });

    describe('executing a cordova command', function () {
        beforeEach(function () {
            options.verbose = true;
        });

        it('should support spaces in path to cordova executable', function () {
            var fakeResolvedPath = 'C:\\Users\\User Name\\AppData\\Roaming\\npm';
            var _join = path.join;
            // resolve returns a fake Windows OS styled path with a space
            spyOn(path, 'resolve').andReturn(fakeResolvedPath);
            // spy on join and only modify the faked path, allowing the rest to call the normal join
            // this is required to allow the test to run on both Windows and Unix, since both use
            // different delimiting slashes.
            spyOn(path, 'join').andCallFake(function (p1, p2) {
                if (p1 === fakeResolvedPath) { return fakeResolvedPath + '\\' + p2; } else { return _join.apply(path, arguments); }
            });

            phonegap.cordova(options);
            expect(shell.exec.mostRecentCall.args[0]).toEqual('"C:\\Users\\User Name\\AppData\\Roaming\\npm\\cordova" build ios --no-telemetry');
        });

        it('should output stdout data', function (done) {
            phonegap.on('raw', function (data) {
                expect(data).toEqual('hello stdout');
                done();
            });
            phonegap.cordova(options);
            processSpy.stdout.emit('data', 'hello stdout');
        });

        it('should output stderr data', function (done) {
            phonegap.on('raw', function (data) {
                expect(data).toEqual('hello stderr');
                done();
            });
            phonegap.cordova(options);
            processSpy.stderr.emit('data', 'hello stderr');
        });

        describe('successful', function (done) {
            beforeEach(function () {
                shell.exec.andCallFake(function (cmd, options, callback) {
                    process.nextTick(function () {
                        // eslint-disable-next-line
                        callback(0, ''); // exit code 0, '' output
                    });
                    return processSpy;
                });
            });

            it('should trigger the callback without an error', function (done) {
                phonegap.cordova(options, function (e) {
                    expect(e).toBeUndefined();
                    done();
                });
            });
        });

        describe('failure', function (done) {
            beforeEach(function () {
                shell.exec.andCallFake(function (cmd, options, callback) {
                    process.nextTick(function () {
                        callback(1, 'bad error'); // eslint-disable-line
                    });
                    return processSpy;
                });
            });

            it('should trigger the callback without an error', function (done) {
                phonegap.cordova(options, function (e) {
                    expect(e).toEqual(jasmine.any(Error));
                    expect(e.exitCode).toEqual(1);
                    done();
                });
            });
        });

        describe('when not a valid cordova project', function () {
            beforeEach(function () {
                cordova.util.isCordova.andReturn(false);
                shell.exec.andCallThrough();
            });

            it('should trigger the callback without an error', function (done) {
                phonegap.cordova(options, function (e) {
                    expect(e).toEqual(jasmine.any(Error));
                    done();
                });
            });
        });

        describe('will add cordova do not track flag', function () {
            it('$ cordova build ios', function () {
                phonegap.cordova(options);
                expect(options.cmd).toBe('cordova build ios --no-telemetry');
            });

            it('$ cordova platform ls', function () {
                options.cmd = 'cordova platform ls';
                phonegap.cordova(options);
                expect(options.cmd).toBe('cordova platform ls --no-telemetry');
            });

            it('$ cordova run android', function () {
                options.cmd = 'cordova run android';
                phonegap.cordova(options);
                expect(options.cmd).toBe('cordova run android --no-telemetry');
            });
        });
    });

    describe('adding plugin paths', function () {
        it('should not alter the plugin path', function () {
            options.cmd = 'cordova plugin add http://path/to/cordova-plugin.git';
            phonegap.cordova(options);
            expect(shell.exec).toHaveBeenCalled();
            expect(shell.exec.mostRecentCall.args[0]).toMatch(options.cmd);
        });
    });

    describe('adding/removing plugin', function () {
        it('should save/remove the plugin in config.xml without --save flag', function () {
            var options1 = {};
            // test for phonegap plugin add
            options1.cmd = options.cmd = 'cordova plugin add cordova-plugin-camera';
            phonegap.cordova(options);
            // options.cmd now has --save appended.
            expect(shell.exec).toHaveBeenCalled();
            expect(options.cmd).toBe(options1.cmd + ' --no-telemetry --save');
            expect(shell.exec.mostRecentCall.args[0]).toMatch(options1.cmd + ' --no-telemetry --save');
            // test for phonegap plugin rm
            options1.cmd = options.cmd = 'cordova plugin rm cordova-plugin-camera';
            phonegap.cordova(options);
            expect(shell.exec).toHaveBeenCalled();
            expect(options.cmd).toBe(options1.cmd + ' --no-telemetry --save');
            expect(shell.exec.mostRecentCall.args[0]).toMatch(options1.cmd + ' --no-telemetry --save');

            // test for phonegap plugin remove
            options1.cmd = options.cmd = 'cordova plugin remove cordova-plugin-camera';
            phonegap.cordova(options);
            expect(shell.exec).toHaveBeenCalled();
            expect(options.cmd).toBe(options1.cmd + ' --no-telemetry --save');
            expect(shell.exec.mostRecentCall.args[0]).toMatch(options1.cmd + ' --no-telemetry --save');
        });
    });

    describe('add platforms', function () {
        describe('when the command is of the type:', function () {
            beforeEach(function () {
                cordova.util.listPlatforms.andReturn([]);
            });

            it('cordova prepare <platform>', function () {
                options.cmd = 'cordova prepare ios';
                phonegap.cordova(options);
                expect(shell.exec.mostRecentCall.args[0]).toMatch('platform add --save ios');
            });

            it('cordova compile <platform>', function () {
                options.cmd = 'cordova compile ios';
                phonegap.cordova(options);
                expect(shell.exec.mostRecentCall.args[0]).toMatch('platform add --save ios');
            });

            it('cordova build <platform>', function () {
                options.cmd = 'cordova build ios';
                phonegap.cordova(options);
                expect(shell.exec.mostRecentCall.args[0]).toMatch('platform add --save ios');
            });

            it('cordova run <platform>', function () {
                options.cmd = 'cordova run ios';
                phonegap.cordova(options);
                expect(shell.exec.mostRecentCall.args[0]).toMatch('platform add --save ios');
            });

            it('cordova emulate <platform>', function () {
                options.cmd = 'cordova emulate ios';
                phonegap.cordova(options);
                expect(shell.exec.mostRecentCall.args[0]).toMatch('platform add --save ios');
            });

            it('not cordova prepare', function () {
                options.cmd = 'cordova prepare';
                phonegap.cordova(options);
                expect(shell.exec.mostRecentCall.args[0]).not.toMatch('platform add --save ios');
            });

            it('not cordova compile', function () {
                options.cmd = 'cordova compile';
                phonegap.cordova(options);
                expect(shell.exec.mostRecentCall.args[0]).not.toMatch('platform add --save ios');
            });

            it('not cordova build', function () {
                options.cmd = 'cordova build';
                phonegap.cordova(options);
                expect(shell.exec.mostRecentCall.args[0]).not.toMatch('platform add --save ios');
            });

            it('not cordova run', function () {
                options.cmd = 'cordova run';
                phonegap.cordova(options);
                expect(shell.exec.mostRecentCall.args[0]).not.toMatch('platform add --save ios');
            });

            it('not cordova emulate', function () {
                options.cmd = 'cordova emulate';
                phonegap.cordova(options);
                expect(shell.exec.mostRecentCall.args[0]).not.toMatch('platform add --save ios');
            });

            it('not cordova create <path>', function () {
                options.cmd = 'cordova create my-app';
                phonegap.cordova(options);
                expect(shell.exec.mostRecentCall.args[0]).not.toMatch('platform add');
            });
        });

        describe('when no platform is specified', function () {
            beforeEach(function () {
                options.cmd = 'cordova run';
            });

            it('should not add a platform', function () {
                phonegap.cordova(options);
                expect(shell.exec.mostRecentCall.args[0]).not.toMatch('platform add --save');
            });

            it('should ignore an option and not add a platform', function () {
                options.cmd = 'cordova run --emulator';
                phonegap.cordova(options);
                expect(shell.exec.mostRecentCall.args[0]).not.toMatch('platform add --save');
            });
            it('should ignore options and not add a platform', function () {
                options.cmd = 'cordova run --emulator --target="Sim"';
                phonegap.cordova(options);
                expect(shell.exec.mostRecentCall.args[0]).not.toMatch('platform add --save');
            });
        });

        describe('when a platform does not exist', function () {
            beforeEach(function () {
                options.cmd = 'cordova build ios';
                cordova.util.listPlatforms.andReturn([]);
            });

            it('should try to add the platform', function () {
                phonegap.cordova(options);
                expect(shell.exec.mostRecentCall.args[0]).toMatch('platform add --save ios');
            });
        });

        describe('when multiple platforms do not exist', function () {
            beforeEach(function () {
                options.cmd = 'cordova build ios android';
                cordova.util.listPlatforms.andReturn([]);
            });

            it('should try to add all of the platforms', function () {
                phonegap.cordova(options);
                expect(shell.exec.mostRecentCall.args[0]).toMatch('platform add --save ios android');
            });
        });

        describe('when some platforms do not exist', function () {
            beforeEach(function () {
                options.cmd = 'cordova build ios android wp8';
                cordova.util.listPlatforms.andReturn(['android']);
            });

            it('should try to add the missing platforms', function () {
                phonegap.cordova(options);
                expect(shell.exec.mostRecentCall.args[0]).toMatch('platform add --save ios wp8');
            });
        });

        describe('after adding the platform(s)', function () {
            beforeEach(function () {
                options.cmd = 'cordova build ios';
                cordova.util.listPlatforms.andReturn([]);
                shell.exec.andCallFake(function (cmd, options, callback) {
                    process.nextTick(function () {
                        callback(0, ''); // eslint-disable-line
                    });
                    return processSpy;
                });
            });

            it('should execute the original command', function (done) {
                phonegap.cordova(options, function (e) {
                    expect(shell.exec.calls[0].args[0]).toMatch('platform add --save ios');
                    expect(shell.exec.calls[1].args[0]).toMatch('cordova build ios');
                    done();
                });
            });
        });
    });

    describe('adding phonegap.js backwards compatibility', function () {
        beforeEach(function () {
            // phonegap.js reference found
            shell.grep.andReturn('<script src="phonegap.js"></script>');
            // enable injecting phonegap.js
            fs.existsSync.andCallFake(function (filepath) {
                // return true if checking cordova.js path
                return (filepath.match('cordova.js'));
            });
        });

        it('should add phonegap.js for ios', function () {
            phonegap.cordova(options);
            expect(shell.cp).toHaveBeenCalled();
            expect(shell.cp.mostRecentCall.args[1]).toMatch(/ios.*cordova\.js/);
            expect(shell.cp.mostRecentCall.args[2]).toMatch(/ios.*phonegap\.js/);
        });

        it('should add phonegap.js for ios and android', function () {
            options.cmd = 'cordova build ios android';
            cordova.util.listPlatforms.andReturn(['ios', 'android']);
            phonegap.cordova(options);
            expect(shell.cp).toHaveBeenCalled();
            expect(shell.cp.calls.length).toEqual(2);
            expect(shell.cp.calls[0].args[1]).toMatch(/ios.*cordova\.js/);
            expect(shell.cp.calls[0].args[2]).toMatch(/ios.*phonegap\.js/);
            expect(shell.cp.calls[1].args[1]).toMatch(/android.*cordova\.js/);
            expect(shell.cp.calls[1].args[2]).toMatch(/android.*phonegap\.js/);
        });
    });

    describe('adding phonegap.js deprecation warning', function () {
        describe('when app does not reference phonegap.js', function () {
            beforeEach(function () {
                // no phonegap.js reference found
                shell.grep.andReturn('');
            });

            it('should not emit a deprecation warning', function (done) {
                phonegap.on('warn', function (message) {
                    expect(false).toBe(true);
                    done();
                });
                phonegap.cordova(options);
                process.nextTick(function () {
                    done(); // given time for warn event to be emitted
                });
            });
        });

        describe('when app references phonegap.js', function () {
            beforeEach(function () {
                // phonegap.js reference found
                shell.grep.andReturn('<script src="phonegap.js"></script>');
            });

            it('should not emit a deprecation warning', function (done) {
                phonegap.on('warn', function (message) {
                    expect(message).toMatch(/phonegap\.js/i);
                    done();
                });
                phonegap.cordova(options);
            });
        });
    });
});
