// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:async';
import 'dart:io';

import 'package:http/http.dart' as http;
import 'package:http_multi_server/http_multi_server.dart';
import 'package:http_multi_server/src/utils.dart';
import 'package:test/test.dart';

void main() {
  group("with multiple HttpServers", () {
    var multiServer;
    var subServer1;
    var subServer2;
    var subServer3;
    setUp(() {
      return Future.wait([
        HttpServer.bind("localhost", 0).then((server) => subServer1 = server),
        HttpServer.bind("localhost", 0).then((server) => subServer2 = server),
        HttpServer.bind("localhost", 0).then((server) => subServer3 = server)
      ]).then((servers) => multiServer = new HttpMultiServer(servers));
    });

    tearDown(() => multiServer.close());

    test("listen listens to all servers", () {
      multiServer.listen((request) {
        request.response.write("got request");
        request.response.close();
      });

      expect(_read(subServer1), completion(equals("got request")));
      expect(_read(subServer2), completion(equals("got request")));
      expect(_read(subServer3), completion(equals("got request")));
    });

    test("serverHeader= sets the value for all servers", () {
      multiServer.serverHeader = "http_multi_server test";

      multiServer.listen((request) {
        request.response.write("got request");
        request.response.close();
      });

      expect(
          _get(subServer1).then((response) {
            expect(
                response.headers['server'], equals("http_multi_server test"));
          }),
          completes);

      expect(
          _get(subServer2).then((response) {
            expect(
                response.headers['server'], equals("http_multi_server test"));
          }),
          completes);

      expect(
          _get(subServer3).then((response) {
            expect(
                response.headers['server'], equals("http_multi_server test"));
          }),
          completes);
    });

    test("autoCompress= sets the value for all servers", () {
      multiServer.autoCompress = true;

      multiServer.listen((request) {
        request.response.write("got request");
        request.response.close();
      });

      expect(
          _get(subServer1).then((response) {
            expect(response.headers['content-encoding'], equals("gzip"));
          }),
          completes);

      expect(
          _get(subServer2).then((response) {
            expect(response.headers['content-encoding'], equals("gzip"));
          }),
          completes);

      expect(
          _get(subServer3).then((response) {
            expect(response.headers['content-encoding'], equals("gzip"));
          }),
          completes);
    });

    test("headers.set sets the value for all servers", () {
      multiServer.defaultResponseHeaders
          .set("server", "http_multi_server test");

      multiServer.listen((request) {
        request.response.write("got request");
        request.response.close();
      });

      expect(
          _get(subServer1).then((response) {
            expect(
                response.headers['server'], equals("http_multi_server test"));
          }),
          completes);

      expect(
          _get(subServer2).then((response) {
            expect(
                response.headers['server'], equals("http_multi_server test"));
          }),
          completes);

      expect(
          _get(subServer3).then((response) {
            expect(
                response.headers['server'], equals("http_multi_server test"));
          }),
          completes);
    });

    test("connectionsInfo sums the values for all servers", () {
      var pendingRequests = 0;
      var awaitingResponseCompleter = new Completer();
      var sendResponseCompleter = new Completer();
      multiServer.listen((request) {
        sendResponseCompleter.future.then((_) {
          request.response.write("got request");
          request.response.close();
        });

        pendingRequests++;
        if (pendingRequests == 2) awaitingResponseCompleter.complete();
      });

      // Queue up some requests, then wait on [awaitingResponseCompleter] to
      // make sure they're in-flight before we check [connectionsInfo].
      expect(_get(subServer1), completes);
      expect(_get(subServer2), completes);

      return awaitingResponseCompleter.future.then((_) {
        var info = multiServer.connectionsInfo();
        expect(info.total, equals(2));
        expect(info.active, equals(2));
        expect(info.idle, equals(0));
        expect(info.closing, equals(0));

        sendResponseCompleter.complete();
      });
    });
  });

  group("HttpMultiServer.loopback", () {
    var server;
    setUp(() {
      return HttpMultiServer.loopback(0).then((server_) => server = server_);
    });

    tearDown(() => server.close());

    test("listens on all localhost interfaces", () async {
      server.listen((request) {
        request.response.write("got request");
        request.response.close();
      });

      if (await supportsIPv4) {
        expect(http.read("http://127.0.0.1:${server.port}/"),
            completion(equals("got request")));
      }

      if (await supportsIPv6) {
        expect(http.read("http://[::1]:${server.port}/"),
            completion(equals("got request")));
      }
    });
  });
}

/// Makes a GET request to the root of [server] and returns the response.
Future<http.Response> _get(HttpServer server) => http.get(_urlFor(server));

/// Makes a GET request to the root of [server] and returns the response body.
Future<String> _read(HttpServer server) => http.read(_urlFor(server));

/// Returns the URL for the root of [server].
String _urlFor(HttpServer server) =>
    "http://${server.address.host}:${server.port}/";
