#include "oidc-token_options.h"

#include "utils/config/client_config.h"
#include "utils/memory.h"
#include "utils/string/stringUtils.h"

#define OPT_NAME 2
#define OPT_AUDIENCE 3
#define OPT_IDTOKEN 4
#define OPT_BEARER 5
#define OPT_AUTHHEADER 6

static struct argp_option options[] = {
    {0, 0, 0, 0, "General:", 1},
    {"time", 't', "SECONDS", 0,
     "Minimum number of seconds the access token should be valid", 1},
    {"issuer", 'i', "OIDC_ISS", OPTION_ARG_OPTIONAL,
     "Return the issuer associated with the requested access token. If neither "
     "-e nor -o is set and OIDC_ISS is not passed, the issuer is printed to "
     "stdout. Otherwise shell commands are printed that will export the value "
     "into an environment variable. The name of this variable can be set with "
     "OIDC_ISS.",
     1},
    {"expires-at", 'e', "OIDC_EXP", OPTION_ARG_OPTIONAL,
     "Return the expiration time for the requested access token. If neither "
     "-i nor -o is set and OIDC_EXP is not passed, the expiration time is "
     "printed to stdout. Otherwise shell commands are printed that will export "
     "the value into an environment variable. The name of this variable can be "
     "set with OIDC_EXP.",
     1},
    {"token", 'o', "OIDC_AT", OPTION_ARG_OPTIONAL,
     "Return the requested access token. If neither "
     "-i nor -e is set and OIDC_AT is not passed, the token is printed to "
     "stdout (Same behaviour as without this option). Otherwise shell commands "
     "are printed that will export the value "
     "into an environment variable. The name of this variable can be set with "
     "OIDC_AT.",
     1},
    {"env", 'c', 0, 0,
     "This will get all available information (same as -a), but will print "
     "shell commands that export environment variables (default names).  The "
     "result for this option is the same as for using 'oidc-token -o -i -e'. "
     "With "
     "the -o, -i and -e options the name of each environment variable can be "
     "changed.",
     1},
    {"all", 'a', 0, 0,
     "Return all available information (token, issuer, expiration time). Each "
     "value is printed in one line.",
     1},
    {"force-new", 'f', 0, 0,
     "Forces that a new access token is issued and returned.", 1},
    {"bearer", OPT_BEARER, 0, 0,
     "Returns the token in the bearer format that is suitable as the value"
     " for an http authorization header",
     1},
    {"auth-header", OPT_AUTHHEADER, 0, 0,
     "Returns the token included in a http authorization header for usage "
     "with e.g. curl or httpie.",
     1},
    {"auth", OPT_AUTHHEADER, 0, OPTION_ALIAS, NULL, 1},

    {0, 0, 0, 0, "Advanced:", 2},
    {"scope", 's', "SCOPE", 0,
     "Scope to be requested for the requested access token. Multiple scopes "
     "can be provided as a space separated list or by using the option "
     "multiple times.",
     2},
    {"aud", OPT_AUDIENCE, "AUDIENCE", 0,
     "Audience for the requested access token. Multiple audiences can be "
     "provided as a space separated list",
     2},
    {"name", OPT_NAME, "NAME", 0,
     "This option is intended for other applications / scripts that call "
     "oidc-token to obtain an access token. NAME is the name of this "
     "application and might be displayed to the user.",
     2},
    {"id-token", OPT_IDTOKEN, 0, 0,
     "Returns an id-token instead of an access token. This option is meant as "
     "a "
     "development tool. ID-tokens should not be passed as authorization to "
     "resources.",
     2},
    {"mytoken", 'm', "PROFILE", OPTION_ARG_OPTIONAL,
     "Returns a mytoken instead of an access token. To specify the properties "
     "of the issued mytoken pass a mytoken profile. This option can only be "
     "used with account shortnames not with issuer urls.",
     2},
    {"MT", 'm', "PROFILE", OPTION_ARG_OPTIONAL | OPTION_ALIAS, NULL, 2},

    {0, 0, 0, 0, "Help:", -1},
    {0, 'h', 0, OPTION_HIDDEN, 0, -1},
    {0, 0, 0, 0, 0, 0}};

static unsigned char min_valid_period_set_from_arg = 0;

static error_t parse_opt(int key, char* arg, struct argp_state* state) {
  struct arguments* arguments = state->input;

  switch (key) {
    case 's': {
      if (arguments->scopes == NULL) {
        arguments->scopes = oidc_strcopy(arg);
        break;
      }
      char* tmp = oidc_sprintf("%s %s", arguments->scopes, arg);
      secFree(arguments->scopes);
      arguments->scopes = tmp;
      break;
    }
    case 't':
      if (!isdigit(*arg)) {
        return ARGP_ERR_UNKNOWN;
      }
      arguments->min_valid_period   = strToInt(arg);
      min_valid_period_set_from_arg = 1;
      break;
    case OPT_IDTOKEN: arguments->idtoken = 1; break;
    case OPT_NAME: arguments->application_name = arg; break;
    case OPT_AUDIENCE: arguments->audience = arg; break;
    case OPT_BEARER: arguments->bearer = 1; break;
    case OPT_AUTHHEADER: arguments->auth_header = 1; break;
    case 'i':
      arguments->issuer_env.str   = arg;
      arguments->issuer_env.useIt = 1;
      break;
    case 'o':
      arguments->token_env.str   = arg;
      arguments->token_env.useIt = 1;
      break;
    case 'e':
      arguments->expiration_env.str   = arg;
      arguments->expiration_env.useIt = 1;
      break;
    case 'm':
      arguments->mytoken.str =
          arg ?: getClientConfig()->default_mytoken_profile;
      arguments->mytoken.useIt = 1;
      break;
    case 'a': arguments->printAll = 1; break;
    case 'f': arguments->forceNewToken = 1; break;
    case 'c':
      arguments->issuer_env.useIt     = 1;
      arguments->token_env.useIt      = 1;
      arguments->expiration_env.useIt = 1;
      break;
    case 'h':
      argp_state_help(state, state->out_stream, ARGP_HELP_STD_HELP);
      break;
    case ARGP_KEY_ARG:
      if (state->arg_num >= 1) {
        argp_usage(state);
      }
      arguments->args[state->arg_num] = arg;
      break;
    case ARGP_KEY_END:
      if (state->arg_num < 1) {
        argp_usage(state);
      }
      break;
    default: return ARGP_ERR_UNKNOWN;
  }
  return 0;
}

static char args_doc[] = "ACCOUNT_SHORTNAME | ISSUER_URL";

static char doc[] =
    "oidc-token -- A client for oidc-agent for getting OIDC access tokens.";

struct argp argp = {options, parse_opt, args_doc, doc, 0, 0, 0};

void initArguments(struct arguments* arguments) {
  arguments->min_valid_period     = getClientConfig()->default_min_lifetime;
  arguments->args[0]              = NULL;
  arguments->scopes               = NULL;
  arguments->application_name     = NULL;
  arguments->audience             = NULL;
  arguments->expiration_env.str   = NULL;
  arguments->expiration_env.useIt = 0;
  arguments->token_env.str        = NULL;
  arguments->token_env.useIt      = 0;
  arguments->issuer_env.str       = NULL;
  arguments->issuer_env.useIt     = 0;
  arguments->mytoken.str          = NULL;
  arguments->mytoken.useIt        = 0;
  arguments->printAll             = 0;
  arguments->idtoken              = 0;
  arguments->forceNewToken        = 0;
  arguments->bearer               = 0;
  arguments->auth_header          = 0;
}
