#include "connectors/connector.h"

#include <assert.h>
#include <stdio.h>
#include <string.h>

typedef struct {
  int32 calls;
  char method[16];
  char url[2048];
  char body[4096];
  char token_body[4096];
} Fake_Google;

static Connector_Status fake_transport(
    const Connector_HTTP_Request *request, const char *access_token,
    Connector_HTTP_Response *response, Dowa_Arena *arena, void *context)
{
  Fake_Google *fake = context;
  ++fake->calls;
  strcpy(fake->method, request->method);
  strcpy(fake->url, request->url);
  if (request->body && request->body_length < sizeof(fake->body)) {
    memcpy(fake->body, request->body, request->body_length);
    fake->body[request->body_length] = '\0';
  } else
    fake->body[0] = '\0';
  const char *body;
  if (strstr(request->url, "userinfo")) {
    assert(access_token && !strcmp(access_token, "new-access"));
    body = "{\"sub\":\"subject-7\",\"email\":\"person@example.com\"}";
  } else if (strstr(request->url, "token")) {
    strcpy(fake->token_body, fake->body);
    body = strstr(fake->body, "grant_type=refresh_token")
        ? "{\"access_token\":\"refreshed\",\"expires_in\":3600}"
        : "{\"access_token\":\"new-access\",\"refresh_token\":\"refresh\","
          "\"expires_in\":3600,\"scope\":\"drive gmail\"}";
  } else if (strstr(request->url, "revoke"))
    body = "{}";
  else
    body = "{\"ok\":true}";
  response->status_code = 200;
  response->body_length = strlen(body);
  response->body = Dowa_Arena_Allocate(arena, response->body_length + 1);
  strcpy(response->body, body);
  return CONNECTOR_OK;
}

static Connector_Google_Config config(Fake_Google *fake)
{
  Connector_Google_Config config = {
    .client_id = "client id",
    .client_secret = "secret&value",
    .redirect_uri = "https://example.test/callback",
    .oauth_authorize_url = "https://fake/authorize",
    .oauth_token_url = "https://fake/token",
    .oauth_revoke_url = "https://fake/revoke",
    .identity_url = "https://fake/userinfo",
    .drive_api_url = "https://fake",
    .drive_upload_url = "https://fake",
    .gmail_api_url = "https://fake",
    .transport = fake_transport,
    .transport_context = fake
  };
  return config;
}

int main(void)
{
  Dowa_Arena *arena = Dowa_Arena_Create(256 * 1024);
  Fake_Google fake = {0};
  Connector_Google_Config google = config(&fake);
  Connector_OAuth_Start start;
  assert(Connector_OAuth_PKCE_Start(&start));
  char *authorization = Connector_Google_Authorization_URL(
      &google, &start, arena);
  assert(authorization && strstr(authorization, "code_challenge_method=S256"));
  assert(strstr(authorization, "client_id=client%20id"));

  Connector_Account account;
  Connector_Provider_Error error;
  assert(Connector_Google_Exchange_Code(
      &google, "code+value", start.code_verifier, &account, &error, arena) ==
      CONNECTOR_OK);
  assert(!strcmp(account.account_id, "google:subject-7"));
  assert(!strcmp(account.email, "person@example.com"));
  assert(strstr(fake.token_body, "code=code%2Bvalue"));
  assert(strstr(fake.token_body, "client_secret=secret%26value"));

  assert(Connector_Google_Refresh(&google, &account, arena) == CONNECTOR_OK);
  assert(!strcmp(account.access_token, "refreshed"));
  assert(strstr(fake.token_body, "grant_type=refresh_token"));
  assert(Connector_Google_Revoke(&google, account.refresh_token, arena) ==
      CONNECTOR_OK);

  Connector_Provider_Request request = {
    .method = "GET",
    .path = "/drive/v3/files",
    .query = "pageSize=10"
  };
  Connector_Provider_Response response;
  assert(Connector_Google_Execute(
      &google, CONNECTOR_OP_DRIVE_LIST, &request, account.access_token,
      &response, arena) == CONNECTOR_OK);
  assert(!strcmp(fake.method, "GET"));
  assert(!strcmp(fake.url, "https://fake/drive/v3/files?pageSize=10"));

  request.method = "POST";
  request.path = "/gmail/v1/users/me/messages/send";
  request.query = NULL;
  request.body = "{\"raw\":\"abc\"}";
  request.body_length = strlen(request.body);
  assert(Connector_Google_Execute(
      &google, CONNECTOR_OP_GMAIL_SEND, &request, account.access_token,
      &response, arena) == CONNECTOR_OK);
  assert(!strcmp(fake.method, "POST"));
  assert(strstr(fake.url, "/gmail/v1/users/me/messages/send"));

  assert(Connector_Operation_Is_Allowed(CONNECTOR_OP_DRIVE_UPDATE));
  assert(Connector_Operation_Is_Mutation(CONNECTOR_OP_GMAIL_SEND));
  assert(!Connector_Operation_Is_Mutation(CONNECTOR_OP_GMAIL_HISTORY));
  Dowa_Arena_Free(arena);
  printf("google_provider_test: ok\n");
  return 0;
}
