#include <curl/curl.h>
#include <gtest/gtest.h>

#include <string>
#include <utility>

#include "mtran/http_translation_client.hpp"

namespace mtran
{
namespace
{

TEST(HttpTranslationClientTest, SendsFixedDirectionPayloadAndReturnsTranslation)
{
  std::string actual_method;
  std::string actual_url;
  std::string actual_body;
  long actual_timeout = 0;

  HttpTranslationClient client(
    "http://127.0.0.1:8989/", 1234,
    [&](const std::string & method, const std::string & url,
    const std::string & body, long timeout_ms) {
      actual_method = method;
      actual_url = url;
      actual_body = body;
      actual_timeout = timeout_ms;
      return HttpResponse{CURLE_OK, 200, R"({"result":"你好"})"};
    });

  const auto result = client.translate("Hello");

  EXPECT_EQ(actual_method, "POST");
  EXPECT_EQ(actual_url, "http://127.0.0.1:8989/translate");
  EXPECT_EQ(actual_body, R"({"from":"en","html":false,"text":"Hello","to":"zh-Hans"})");
  EXPECT_EQ(actual_timeout, 1234);
  EXPECT_TRUE(result.success);
  EXPECT_EQ(result.translation, "你好");
  EXPECT_EQ(result.error, ClientError::kNone);
}

TEST(HttpTranslationClientTest, ReportsUnavailableTransport)
{
  HttpTranslationClient client(
    "http://127.0.0.1:8989", 100,
    [](const auto &, const auto &, const auto &, long) {
      return HttpResponse{CURLE_COULDNT_CONNECT, 0, ""};
    });

  const auto result = client.translate("Hello");

  EXPECT_FALSE(result.success);
  EXPECT_TRUE(result.translation.empty());
  EXPECT_EQ(result.error, ClientError::kUnavailable);
}

TEST(HttpTranslationClientTest, ReportsTimeout)
{
  HttpTranslationClient client(
    "http://127.0.0.1:8989", 100,
    [](const auto &, const auto &, const auto &, long) {
      return HttpResponse{CURLE_OPERATION_TIMEDOUT, 0, ""};
    });

  const auto result = client.translate("Hello");

  EXPECT_FALSE(result.success);
  EXPECT_TRUE(result.translation.empty());
  EXPECT_EQ(result.error, ClientError::kTimeout);
}

TEST(HttpTranslationClientTest, ReportsNonSuccessHttpStatus)
{
  HttpTranslationClient client(
    "http://127.0.0.1:8989", 100,
    [](const auto &, const auto &, const auto &, long) {
      return HttpResponse{CURLE_OK, 500, "failure"};
    });

  const auto result = client.translate("Hello");

  EXPECT_FALSE(result.success);
  EXPECT_TRUE(result.translation.empty());
  EXPECT_EQ(result.error, ClientError::kUpstream);
}

class InvalidResponseTest : public ::testing::TestWithParam<std::string>
{
};

TEST_P(InvalidResponseTest, RejectsMalformedOrWrongShapeResponse)
{
  const auto response_body = GetParam();
  HttpTranslationClient client(
    "http://127.0.0.1:8989", 100,
    [response_body](const auto &, const auto &, const auto &, long) {
      return HttpResponse{CURLE_OK, 200, response_body};
    });

  const auto result = client.translate("Hello");

  EXPECT_FALSE(result.success);
  EXPECT_TRUE(result.translation.empty());
  EXPECT_EQ(result.error, ClientError::kInvalidResponse);
}

INSTANTIATE_TEST_SUITE_P(
  InvalidBodies, InvalidResponseTest,
  ::testing::Values("not-json", R"({"result":7})", R"({"other":"value"})"));

TEST(HttpTranslationClientTest, HealthRequiresSuccessfulTransportAndHttpStatus)
{
  int call_count = 0;
  HttpTranslationClient client(
    "http://127.0.0.1:8989", 100,
    [&](const std::string & method, const std::string & url, const std::string &, long) {
      ++call_count;
      EXPECT_EQ(method, "GET");
      EXPECT_EQ(url, "http://127.0.0.1:8989/health");
      return HttpResponse{CURLE_OK, call_count == 1 ? 503 : 200, ""};
    });

  EXPECT_FALSE(client.health());
  EXPECT_TRUE(client.health());
}

}  // namespace
}  // namespace mtran
