import {ShopifyError} from './error';
import {ConfigInterface, ConfigParams} from './base-types';
import {LogSeverity} from './types';
import {AuthScopes} from './auth/scopes';
import {logger as createLogger} from './logger';

export function validateConfig<Params extends ConfigParams>(
  params: Params,
): ConfigInterface<Params> {
  const config = {
    apiKey: '',
    apiSecretKey: '',
    hostName: '',
    hostScheme: 'https',
    isEmbeddedApp: true,
    isCustomStoreApp: false,
    logger: {
      log: defaultLogFunction,
      level: LogSeverity.Info,
      httpRequests: false,
      timestamps: false,
    },
    future: {},
    _logDisabledFutureFlags: true,
  } as ConfigInterface<Params>;

  // Make sure that the essential params actually have content in them
  const mandatory: (keyof Params)[] = [
    'apiSecretKey',
    'hostName',
    'apiVersion',
  ];
  if (!('isCustomStoreApp' in params) || !params.isCustomStoreApp) {
    mandatory.push('apiKey');
  }
  if ('isCustomStoreApp' in params && params.isCustomStoreApp) {
    if (
      !('adminApiAccessToken' in params) ||
      params.adminApiAccessToken?.length === 0
    ) {
      mandatory.push('adminApiAccessToken');
    }
  }

  const missing: (keyof Params)[] = [];
  mandatory.forEach((key) => {
    if (!notEmpty(params[key])) {
      missing.push(key);
    }
  });

  if (missing.length) {
    throw new ShopifyError(
      `Cannot initialize Shopify API Library. Missing values for: ${missing.join(
        ', ',
      )}. For apiVersion, please specify an explicit API version (e.g., ApiVersion.July25). See https://shopify.dev/docs/api/usage/versioning for more information.`,
    );
  }

  const {
    hostScheme,
    isCustomStoreApp,
    adminApiAccessToken,
    userAgentPrefix,
    logger,
    privateAppStorefrontAccessToken,
    billing,
    future,
    ...mandatoryParams
  } = params;

  let scopes;
  if (params.scopes === undefined) {
    scopes = undefined;
  } else if (params.scopes instanceof AuthScopes) {
    scopes = params.scopes;
  } else {
    scopes = new AuthScopes(params.scopes);
  }

  Object.assign(config, mandatoryParams, {
    hostName: params.hostName.replace(/\/$/, ''),
    scopes,
    hostScheme: hostScheme ?? config.hostScheme,
    isCustomStoreApp: isCustomStoreApp ?? config.isCustomStoreApp,
    adminApiAccessToken: adminApiAccessToken ?? config.adminApiAccessToken,
    userAgentPrefix: userAgentPrefix ?? config.userAgentPrefix,
    logger: {...config.logger, ...(logger || {})},
    privateAppStorefrontAccessToken:
      privateAppStorefrontAccessToken ?? config.privateAppStorefrontAccessToken,
    billing: billing ?? config.billing,
    future: future ?? config.future,
  });

  if (
    config.isCustomStoreApp &&
    params.adminApiAccessToken === params.apiSecretKey
  ) {
    createLogger(config).warning(
      "adminApiAccessToken is set to the same value as apiSecretKey. adminApiAccessToken should be set to the Admin API access token for custom store apps; apiSecretKey should be set to the custom store app's API secret key.",
    );
  }

  return config;
}

function notEmpty<T>(value: T): value is NonNullable<T> {
  if (value == null) {
    return false;
  }
  return typeof value === 'string' || Array.isArray(value)
    ? value.length > 0
    : true;
}

function defaultLogFunction(severity: LogSeverity, message: string): void {
  switch (severity) {
    case LogSeverity.Debug:
      console.debug(message);
      break;
    case LogSeverity.Info:
      console.log(message);
      break;
    case LogSeverity.Warning:
      console.warn(message);
      break;
    case LogSeverity.Error:
      console.error(message);
      break;
  }
}
