import btoa from '../../helpers/btoa.node.js';

// This function runs after the common function,
// `src/execute/index.js#buildRequest`
export default function buildRequest(options, req) {
  const {
    spec,
    operation,
    securities,
    requestContentType,
    responseContentType,
    attachContentTypeForEmptyPayload
  } = options;
  // Add securities, which are applicable
  req = applySecurities({
    request: req,
    securities,
    operation,
    spec
  });
  if (req.body || req.form || attachContentTypeForEmptyPayload) {
    // all following conditionals are Swagger2 only
    if (requestContentType) {
      req.headers['Content-Type'] = requestContentType;
    } else if (Array.isArray(operation.consumes)) {
      [req.headers['Content-Type']] = operation.consumes;
    } else if (Array.isArray(spec.consumes)) {
      [req.headers['Content-Type']] = spec.consumes;
    } else if (operation.parameters && operation.parameters.filter(p => p.type === 'file').length) {
      req.headers['Content-Type'] = 'multipart/form-data';
    } else if (operation.parameters && operation.parameters.filter(p => p.in === 'formData').length) {
      req.headers['Content-Type'] = 'application/x-www-form-urlencoded';
    }
  } else if (requestContentType) {
    const isBodyParamPresent = operation.parameters && operation.parameters.filter(p => p.in === 'body').length > 0;
    const isFormDataParamPresent = operation.parameters && operation.parameters.filter(p => p.in === 'formData').length > 0;
    if (isBodyParamPresent || isFormDataParamPresent) {
      req.headers['Content-Type'] = requestContentType;
    }
  }
  if (!responseContentType && Array.isArray(operation.produces) && operation.produces.length > 0) {
    req.headers.accept = operation.produces.join(', ');
  }
  return req;
}

// Add security values, to operations - that declare their need on them
export function applySecurities({
  request,
  securities = {},
  operation = {},
  spec
}) {
  const result = {
    ...request
  };
  const {
    authorized = {},
    specSecurity = []
  } = securities;
  const security = operation.security || specSecurity;
  const isAuthorized = authorized && !!Object.keys(authorized).length;
  const securityDef = spec.securityDefinitions;
  result.headers = result.headers || {};
  result.query = result.query || {};
  if (!Object.keys(securities).length || !isAuthorized || !security || Array.isArray(operation.security) && !operation.security.length) {
    return request;
  }
  security.forEach(securityObj => {
    Object.keys(securityObj).forEach(key => {
      const auth = authorized[key];
      if (!auth) {
        return;
      }
      const {
        token
      } = auth;
      const value = auth.value || auth;
      const schema = securityDef[key];
      const {
        type
      } = schema;
      const tokenName = schema['x-tokenName'] || 'access_token';
      const oauthToken = token && token[tokenName];
      let tokenType = token && token.token_type;
      if (auth) {
        if (type === 'apiKey') {
          const inType = schema.in === 'query' ? 'query' : 'headers';
          result[inType] = result[inType] || {};
          result[inType][schema.name] = value;
        } else if (type === 'basic') {
          if (value.header) {
            result.headers.authorization = value.header;
          } else {
            const username = value.username || '';
            const password = value.password || '';
            value.base64 = btoa(`${username}:${password}`);
            result.headers.authorization = `Basic ${value.base64}`;
          }
        } else if (type === 'oauth2' && oauthToken) {
          tokenType = !tokenType || tokenType.toLowerCase() === 'bearer' ? 'Bearer' : tokenType;
          result.headers.authorization = `${tokenType} ${oauthToken}`;
        }
      }
    });
  });
  return result;
}