/**
 * Copyright 2014, 2016 IBM Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/

var should = require("should");
var request = require('supertest');
var express = require('express');
var bodyParser = require('body-parser');
var sinon = require('sinon');
var when = require('when');

var flow = require("../../../red/api/flow");

describe("flow api", function() {

    var app;

    before(function() {
        app = express();
        app.use(bodyParser.json());
        app.get("/flow/:id",flow.get);
        app.post("/flow",flow.post);
        app.put("/flow/:id",flow.put);
        app.delete("/flow/:id",flow.delete);
    });

    describe("get", function() {
        before(function() {
            flow.init({
                settings:{},
                nodes: {
                    getFlow: function(id) {
                        if (id === '123') {
                            return {id:'123'}
                        } else {
                            return null;
                        }
                    }
                },
                log:{ audit: sinon.stub() }
            });
        })
        it('gets a known flow', function(done) {
            request(app)
                .get('/flow/123')
                .set('Accept', 'application/json')
                .expect(200)
                .end(function(err,res) {
                    if (err) {
                        return done(err);
                    }
                    res.body.should.has.a.property('id','123');
                    done();
                });
        })
        it('404s an unknown flow', function(done) {
            request(app)
                .get('/flow/456')
                .set('Accept', 'application/json')
                .expect(404)
                .end(done);
        })
    });

    describe("add", function() {
        before(function() {
            flow.init({
                settings:{},
                nodes: {
                    addFlow: function(f) {
                        if (f.id === "123") {
                            return when.resolve('123')
                        } else {
                            return when.reject(new Error("test error"));
                        }
                    }
                },
                log:{ audit: sinon.stub() }
            });
        })
        it('adds a new flow', function(done) {
            request(app)
                .post('/flow')
                .set('Accept', 'application/json')
                .send({id:'123'})
                .expect(200)
                .end(function(err,res) {
                    if (err) {
                        return done(err);
                    }
                    res.body.should.has.a.property('id','123');
                    done();
                });
        })
        it('400 an invalid flow', function(done) {
            request(app)
                .post('/flow')
                .set('Accept', 'application/json')
                .send({id:'error'})
                .expect(400)
                .end(function(err,res) {
                    if (err) {
                        return done(err);
                    }
                    res.body.should.has.a.property('error','unexpected_error');
                    res.body.should.has.a.property('message','Error: test error');

                    done();
                });
        })
    })

    describe("update", function() {
        var nodes;
        before(function() {
            nodes = {
                updateFlow: function(id,f) {
                    var err;
                    if (id === "123") {
                        return when.resolve()
                    } else if (id === "unknown") {
                        err = new Error();
                        err.code = 404;
                        throw err;
                    } else if (id === "unexpected") {
                        err = new Error();
                        err.code = 500;
                        throw err;
                    } else {
                        return when.reject(new Error("test error"));
                    }
                }
            };
            flow.init({
                settings:{},
                nodes: nodes,
                log:{ audit: sinon.stub() }
            });
        })

        it('updates an existing flow', function(done) {
            sinon.spy(nodes,"updateFlow");
            request(app)
                .put('/flow/123')
                .set('Accept', 'application/json')
                .send({id:'123'})
                .expect(200)
                .end(function(err,res) {
                    if (err) {
                        return done(err);
                    }
                    res.body.should.has.a.property('id','123');
                    nodes.updateFlow.calledOnce.should.be.true;
                    nodes.updateFlow.lastCall.args[0].should.eql('123');
                    nodes.updateFlow.lastCall.args[1].should.eql({id:'123'});
                    nodes.updateFlow.restore();
                    done();
                });
        })

        it('404s on an unknown flow', function(done) {
            request(app)
                .put('/flow/unknown')
                .set('Accept', 'application/json')
                .send({id:'123'})
                .expect(404)
                .end(done);
        })

        it('400 on async update error', function(done) {
            request(app)
                .put('/flow/async_error')
                .set('Accept', 'application/json')
                .send({id:'123'})
                .expect(400)
                .end(function(err,res) {
                    if (err) {
                        return done(err);
                    }
                    res.body.should.has.a.property('error','unexpected_error');
                    res.body.should.has.a.property('message','Error: test error');
                    done();
                });
        })

        it('400 on sync update error', function(done) {
            request(app)
                .put('/flow/unexpected')
                .set('Accept', 'application/json')
                .send({id:'123'})
                .expect(400)
                .end(function(err,res) {
                    if (err) {
                        return done(err);
                    }
                    res.body.should.has.a.property('error',500);
                    res.body.should.has.a.property('message','Error');
                    done();
                });
        })
    })

    describe("delete", function() {
        var nodes;
        before(function() {
            nodes = {
                removeFlow: function(id) {
                    var err;
                    if (id === "123") {
                        return when.resolve()
                    } else if (id === "unknown") {
                        err = new Error();
                        err.code = 404;
                        throw err;
                    } else if (id === "unexpected") {
                        err = new Error();
                        err.code = 500;
                        throw err;
                    }
                }
            };
            flow.init({
                settings:{},
                nodes: nodes,
                log:{ audit: sinon.stub() }
            });
        })

        it('updates an existing flow', function(done) {
            sinon.spy(nodes,"removeFlow");
            request(app)
                .delete('/flow/123')
                .expect(204)
                .end(function(err,res) {
                    if (err) {
                        return done(err);
                    }
                    nodes.removeFlow.calledOnce.should.be.true;
                    nodes.removeFlow.lastCall.args[0].should.eql('123');
                    nodes.removeFlow.restore();
                    done();
                });
        })

        it('404s on an unknown flow', function(done) {
            request(app)
                .delete('/flow/unknown')
                .expect(404)
                .end(done);
        })

        it('400 on remove error', function(done) {
            request(app)
                .delete('/flow/unexpected')
                .expect(400)
                .end(function(err,res) {
                    if (err) {
                        return done(err);
                    }
                    res.body.should.has.a.property('error',500);
                    res.body.should.has.a.property('message','Error');
                    done();
                });
        })
    })

});
