/**
 * Shell Card Management APIsLib
 *
 * This file was automatically generated by APIMATIC v3.0 ( https://www.apimatic.io ).
 */

import { HttpClientOptions } from './clientAdapter.js';
import { ClientCredentialsAuthManager } from './clientCredentialsAuthManager.js';
import { OAuthToken, oAuthTokenSchema } from './models/oAuthToken.js';
import {
  anyOf,
  array,
  boolean,
  literal,
  number,
  object,
  optional,
  Schema,
  string,
  stringEnum,
  validateAndMap,
} from './schema.js';

/** An interface for all configuration parameters required by the SDK. */
export interface Configuration {
  timeout: number;
  environment: Environment;
  /** @deprecated use clientCredentialsAuthCredentials field instead */
  oAuthClientId?: string;
  /** @deprecated use clientCredentialsAuthCredentials field instead */
  oAuthClientSecret?: string;
  /** @deprecated use clientCredentialsAuthCredentials field instead */
  oAuthToken?: OAuthToken;
  clientCredentialsAuthCredentials?: {
    oAuthClientId: string;
    oAuthClientSecret: string;
    oAuthToken?: OAuthToken;
    oAuthTokenProvider?: (
      lastOAuthToken: OAuthToken | undefined,
      authManager: ClientCredentialsAuthManager
    ) => Promise<OAuthToken>;
    oAuthOnTokenUpdate?: (token: OAuthToken) => void;
    oAuthClockSkew?: number;
  };
  httpClientOptions?: Partial<HttpClientOptions>;
  unstable_httpClientOptions?: any;
}

/** Environments available for API */
export enum Environment {
  SIT = 'SIT',
  Production = 'Production',
}

export namespace Configuration {
  export function fromJsonConfig(jsonConfig: string): Partial<Configuration> {
    const configurationObject = JSON.parse(jsonConfig);
    const result = validateAndMap(jsonConfig, configurationObject);

    if (result.errors) {
      throw new Error(
        'Invalid configuration provided. Please check the following errors:\n' +
          result.errors.map((e: any) => e.message).join('\n')
      );
    }

    return result.result;
  }

  export function fromEnvironment(
    envVariables: Record<string, string | undefined>
  ): Partial<Configuration> {
    const config: any = {};

    config.timeout = envVariables.TIMEOUT;
    config.environment = envVariables.ENVIRONMENT;

    if (envVariables.O_AUTH_CLIENT_ID && envVariables.O_AUTH_CLIENT_SECRET) {
      config.clientCredentialsAuthCredentials = {
        oAuthClientId: envVariables.O_AUTH_CLIENT_ID,
        oAuthClientSecret: envVariables.O_AUTH_CLIENT_SECRET,
        oAuthToken: envVariables.O_AUTH_TOKEN,
        oAuthClockSkew: envVariables.O_AUTH_CLOCK_SKEW,
      };
    }

    config.httpClientOptions = {
      timeout: envVariables.TIMEOUT,
      retryConfig: {
        retryOnTimeout: envVariables.RETRY_ON_TIMEOUT,
        retryInterval: envVariables.RETRY_INTERVAL,
        maxNumberOfRetries: envVariables.MAX_NUMBER_OF_RETRIES,
        maximumRetryWaitTime: envVariables.MAX_RETRY_WAIT_TIME,
        backoffFactor: envVariables.RETRY_BACKOFF_FACTOR,
        httpStatusCodesToRetry: envVariables.HTTP_STATUS_CODES_TO_RETRY?.split(
          ','
        ).map((s) => s.trim()),
        httpMethodsToRetry: envVariables.HTTP_METHODS_TO_RETRY?.split(
          ','
        ).map((s) => s.trim()),
      },
    };

    if (envVariables.PROXY_ADDRESS) {
      config.httpClientOptions.proxySettings = {
        address: envVariables.PROXY_ADDRESS,
        port: envVariables.PROXY_PORT,
      };

      if (
        envVariables.PROXY_AUTH_USERNAME &&
        envVariables.PROXY_AUTH_PASSWORD
      ) {
        config.httpClientOptions.proxySettings.auth = {
          username: envVariables.PROXY_AUTH_USERNAME,
          password: envVariables.PROXY_AUTH_PASSWORD,
        };
      }
    }

    const result = validateAndMap(config, configurationSchema);

    if (result.errors) {
      throw new Error(
        'Invalid configuration provided. Please check the following errors:\n' +
          result.errors.map((e: any) => e.message).join('\n')
      );
    }

    return result.result;
  }
}

const configurationSchema: Schema<Partial<Configuration>> = object({
  timeout: ['timeout', optional(number())],
  environment: ['environment', optional(stringEnum(Environment))],
  oAuthClientId: ['oAuthClientId', optional(string())],
  oAuthClientSecret: ['oAuthClientSecret', optional(string())],
  oAuthToken: ['oAuthToken', optional(optional(oAuthTokenSchema))],
  clientCredentialsAuthCredentials: [
    'clientCredentialsAuthCredentials',
    optional(
      object({
        oAuthClientId: ['oAuthClientId', string()],
        oAuthClientSecret: ['oAuthClientSecret', string()],
        oAuthClockSkew: ['oAuthClockSkew', optional(number())],
      })
    ),
  ],
  httpClientOptions: [
    'httpClientOptions',
    optional(
      object({
        timeout: ['timeout', optional(number())],
        retryConfig: [
          'retryConfig',
          optional(
            object({
              maxNumberOfRetries: ['maxNumberOfRetries', optional(number())],
              retryOnTimeout: ['retryOnTimeout', optional(boolean())],
              retryInterval: ['retryInterval', optional(number())],
              maximumRetryWaitTime: [
                'maximumRetryWaitTime',
                optional(number()),
              ],
              backoffFactor: ['backoffFactor', optional(number())],
              httpStatusCodesToRetry: [
                'httpStatusCodesToRetry',
                optional(array(number())),
              ],
              httpMethodsToRetry: [
                'httpMethodsToRetry',
                optional(
                  array(
                    anyOf([
                      literal('GET'),
                      literal('DELETE'),
                      literal('HEAD'),
                      literal('OPTIONS'),
                      literal('POST'),
                      literal('PUT'),
                      literal('PATCH'),
                      literal('LINK'),
                      literal('UNLINK'),
                    ])
                  )
                ),
              ],
            })
          ),
        ],
        proxySettings: [
          'proxySettings',
          optional(
            object({
              address: ['address', string()],
              port: ['port', optional(number())],
              auth: [
                'auth',
                optional(
                  object({
                    username: ['username', string()],
                    password: ['password', string()],
                  })
                ),
              ],
            })
          ),
        ],
      })
    ),
  ],
});
