var check = require('check-types'),
    es = require('elasticsearch'),
    logger = require( 'pelias-logger' ).get( 'api' ),
    exceptions = require('elasticsearch-exceptions/lib/exceptions/SupportedExceptions');

// create a list of regular expressions to match against.
// note: list created when the server starts up; for performance reasons.
var exceptionRegexList = exceptions.map( function( exceptionName ){
  return new RegExp( '^' + exceptionName );
});

function sendJSONResponse(req, res, next) {

  // do nothing if no result data set
  if (!res || !check.object(res.body) || !check.object(res.body.geocoding)) {
    return next();
  }

  // default status
  var statusCode = 200; // 200 OK

  // vary status code whenever an error was reported
  var geocoding = res.body.geocoding;

  if( check.array( geocoding.errors ) && geocoding.errors.length ){

    // default status for errors is 400 Bad Request
    statusCode = 400; // 400 Bad Request

    // iterate over all reported errors
    geocoding.errors.forEach( function( err ){

      // custom status codes for instances of the Error() object.
      if( err instanceof Error ){
        /*
          elasticsearch errors
          see: https://github.com/elastic/elasticsearch-js/blob/master/src/lib/errors.js

          408 Request Timeout
          500 Internal Server Error
          502 Bad Gateway
        */
        if( err instanceof es.errors.RequestTimeout ){ statusCode = Math.max( statusCode, 408 ); }
        else if( err instanceof es.errors.NoConnections ){ statusCode = Math.max( statusCode, 502 ); }
        else if( err instanceof es.errors.ConnectionFault ){ statusCode = Math.max( statusCode, 502 ); }
        else {
          logger.warn( 'unknown geocoding error object:', err.constructor.name, err );
          statusCode = Math.max( statusCode, 500 );
        }

      /*
        some elasticsearch errors are only returned as strings (not instances of Error).
        in this case we (unfortunately) need to match the exception at position 0 inside the string.
      */
      } else if( check.string( err ) ){
        for( var i=0; i<exceptionRegexList.length; i++ ){
          // check error string against a list of known elasticsearch exceptions
          if( err.match( exceptionRegexList[i] ) ){
            statusCode = Math.max( statusCode, 500 );
            break; // break on first match
          }
        }
        logger.warn( 'unknown geocoding error string:', err );
      } else {
        logger.warn( 'unknown geocoding error type:', typeof err, err );
      }
    });
  }

  // respond
  return res.status(statusCode).json(res.body);
}

module.exports = sendJSONResponse;