var assert = require('chai').assert;
var nodemock = require('nodemock');
var Deferred = require('jsdeferred').Deferred;

var utils = require('../test-utils');

var express = require('express');
var restAdaptor = require('../../lib/frontend/rest-adaptor');
var model = require('../../lib/model');
var restCommands = require('../../lib/frontend/default-commands/rest');

suite('REST API', function() {
  test('registeration of plugin commands', function() {
    var basePlugin = {
      getCommand: new model.REST({
        path: '/get'
      }),
      putCommand: new model.REST({
        method: 'PUT',
        path: '/put'
      }),
      postCommand: new model.REST({
        method: 'POST',
        path: '/post'
      }),
      deleteCommand: new model.REST({
        method: 'DELETE',
        path: '/delete'
      }),
      ignored: new model.SocketCommand()
    };
    var overridingPlugin = {
      postCommand: new model.REST({
        method: 'POST',
        path: '/post/overridden'
      }),
      deleteCommand: new model.REST({
        method: 'DELETE',
        path: '/delete/overridden'
      })
    };

    var application = express();
    var registeredCommands = restAdaptor.register(application, {
      prefix:     '',
      connection: utils.createStubbedBackendConnection(),
      plugins: [
        basePlugin,
        overridingPlugin
      ]
    });

    registeredCommands = registeredCommands.map(function(command) {
      return {
        name:       command.name,
        definition: command.definition
      };
    });
    assert.deepEqual(registeredCommands,
                     [{ name:       'search',
                        definition: restCommands.search },
                      { name:       'getCommand',
                        definition: basePlugin.getCommand },
                      { name:       'putCommand',
                        definition: basePlugin.putCommand },
                      { name:       'postCommand',
                        definition: overridingPlugin.postCommand },
                      { name:       'deleteCommand',
                        definition: overridingPlugin.deleteCommand }]);
  });

  suite('registeration', function() {
    var testPlugin = {
      api: new model.REST({
        path: '/path/to/api',
        toBackend: function(event, request) { return [event, 'api requested']; },
        toClient: function(event, data) { return [event, 'api OK']; }
      })
    };

    var connection;
    var application;
    var server;
    var backend;

    setup(function(done) {
      utils.setupApplication()
        .next(function(result) {
          backend = result.backend;
          server = result.server;
          connection = result.connection;
          application = result.application;
          done();
        });
    });

    teardown(function() {
      utils.teardownApplication({ backend:    backend,
                                  server:     server,
                                  connection: connection });
    });

    test('to the document root', function(done) {
      restAdaptor.register(application, {
        prefix:     '',
        connection: connection,
        plugins:    [testPlugin]
      });

      backend.reserveResponse(function(request) {
        return utils.createReplyPacket(request,
                                       {
                                         statusCode: 200,
                                         body:       'API response',
                                       });
      });

      utils.get('/path/to/api')
        .next(function(response) {
          backend.assertReceived([{ type: 'api',
                                    body: 'api requested' }]);
          assert.deepEqual(response,
                          { statusCode: 200,
                            body:       JSON.stringify('api OK') });
          done();
        })
        .error(function(error) {
          done(error);
        });
    });

    test('under specified path', function(done) {
      restAdaptor.register(application, {
        prefix:     '/path/to/droonga',
        connection: connection,
        plugins:    [testPlugin]
      });

      var mockedReceiver = nodemock
            .mock('receive')
              .takes('api OK');

      utils.get('/path/to/droonga/path/to/api')
        .next(function(response) {
          mockedReceiver.receive(response.body);
        });

      Deferred
        .wait(0.01)
        .next(function() {
          backend.assertReceived([{ type: 'api',
                                    body: 'api requested' }]);

          return backend.sendResponse(backend.getMessages()[0],
                                      'api.result',
                                      'api OK?');
        })
        .wait(0.01)
        .next(function() {
          mockedReceiver.assertThrows();
          done();
        })
        .error(function(error) {
          done(error);
        });
    });
  });

  suite('default commands', function() {
    var server;

    teardown(function() {
      if (server) {
        server.close();
        server = undefined;
      }
    });

    test('search', function(done) {
      var receiverCallback = {};
      var connection = utils.createStubbedBackendConnection();
      var application = express();
      restAdaptor.register(application, {
        prefix:     '',
        connection: connection
      });
      utils.setupServer(application)
        .next(function(newServer) {
          server = newServer;
          utils.get('/tables/foo?query=bar');
        })
        .wait(0.1)
        .next(function() {
          assert.equal(1, connection.emitMessageCalledArguments.length);
          var args = connection.emitMessageCalledArguments[0];
          assert.equal(args.type, 'search');

          var expected = {
            queries: {
              result: {
                source: 'foo',
                query:  'bar',
                output: utils.outputAll
              }
            }
          };
          assert.equalJSON(args.message, expected);

          done();
        })
        .error(function(error) {
          done(error);
        });
    });
  });
});


@@ -4182,232 +4182,299 @@
     
-var mockedReceiver = nodemock%0A            .mock('receive')%0A              .takes('api OK');%0A%0A      utils.get('/path/to/droonga/path/to/api')%0A        .next(function(response) %7B%0A          mockedReceiver.receive(response.body);%0A
+backend.reserveResponse(function(request) %7B%0A        return utils.createReplyPacket(request,%0A                                       %7B%0A                                         statusCode: 200,%0A                                         body:       'API response',%0A                               
     
@@ -4485,42 +4485,65 @@
 %7D);%0A
-%0A
       
-Deferred%0A        .wait(0.01
+%7D);%0A%0A      utils.get('/path/to/droonga/path/to/api'
 )%0A  
@@ -4555,32 +4555,40 @@
  .next(function(
+response
 ) %7B%0A          ba
@@ -4687,17 +4687,16 @@
 d' %7D%5D);%0A
-%0A
         
@@ -4701,60 +4701,33 @@
     
-return backend.sendResponse(backend.getMessages()%5B0%5D
+assert.deepEqual(response
 ,%0A  
@@ -4754,32 +4754,25 @@
     
-            'api.result'
+%7B statusCode: 200
 ,%0A  
@@ -4801,126 +4801,46 @@
     
+body:
        
-   
+JSON.stringify(
 'api OK
-?
 ')
-;%0A        %7D)%0A        .wait(0.01)%0A        .next(function() %7B%0A          mockedReceiver.assertThrows(
+ %7D
 );%0A 

return response on receiving request instead of using wait()