/*
** Module   : HttpRequestBody.java
** Abstract : The configuration for the HTTP request body.
**
** Copyright (c) 2021-2022, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
** 001 CA  20210827 Created initial version.
** 002 CA  20210907 Variables need to be uppercased for substitution.
** 003 CA  20220118 Fixed body encoding for the request (set the bytes, not the string).
*/

/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU Affero General Public License for more details.
**
** You should have received a copy of the GNU Affero General Public License
** along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package com.goldencode.harness.test;

import java.util.*;

import org.apache.commons.lang3.text.*;
import org.apache.http.Header;
import org.apache.http.client.entity.*;
import org.apache.http.client.methods.*;
import org.apache.http.entity.*;

/**
 * Configuration of the HTTP body request.  Substitutions are allowed via <code>${varname}</code> variables.
 */
public class HttpRequestBody
{
   /** The HTTP body request. */
   private String body;

   /** The substitutions to be applied to the {@link #body}.   The order is the one defined in the test. */
   private List<Substitution> substitutions = new ArrayList<>();

   /** The web service configuration. */
   private WebService service;
   
   /**
    * Create a new HTTP body configuration.
    * 
    * @param    body
    *           HTTP body template.
    * @param    service
    *           The service configuration.
    */
   public HttpRequestBody(String body, WebService service)
   {
      this.body = body;
      this.service = service;
   }

   /**
    * Add a substitution, which will either replace the path or variable, with the specified value.
    * 
    * @param    path
    *           A path in the body (can be used only in cases when the body is a JSON or XML content-type).
    * @param    var
    *           A variable name.
    * @param    value
    *           The value.
    */
   public void addSubstitution(String path, String var, String value)
   {
      if (path != null && var != null)
      {
         throw new IllegalStateException("Can't specify both path " + path + " and variable " + var + "!");
      }
      
      substitutions.add(new Substitution(path, var, value));
   }

   /**
    * Build the HTTP body and pass it to the request.
    * 
    * @param    requestBuilder
    *           The request builder.
    */
   public void build(RequestBuilder requestBuilder)
   {
      if (body == null)
      {
         return;
      }
      
      for (Substitution subst : substitutions)
      {
         body = subst.apply(body);
      }
      
      String contentType = service.getDefaultContentType();
      Header ct = requestBuilder.getFirstHeader("Content-Type");
      if (ct != null)
      {
         contentType = ct.getValue();
      }
      requestBuilder.setEntity(EntityBuilder.create()
                                            .setContentType(ContentType.create(contentType))
                                            .setBinary(body.getBytes())
                                            .build());
   }

   /**
    * Get the {@link #body request body}.
    * 
    * @return   See above.
    */
   public String getBody()
   {
      return body;
   }
   
   /**
    * The configuration of a substitution in the {@link HttpRequestBody#body HTTP body}.
    */
   private class Substitution
   {
      /** The path to replace (only for JSON or XML requests). */
      private String path;
      
      /** The variable to replace. */
      private String var;
      
      /** The value. */
      private String value;

      /**
       * Create a new instance.
       * 
       * @param    path
       *           The path to replace (only for JSON or XML requests).
       * @param    var
       *           The variable to replace.
       * @param    value
       *           The value.
       */
      public Substitution(String path, String var, String value)
      {
         this.path = path;
         this.var = var;
         this.value = value;
      }

      /**
       * Apply the substitution and replace the {@link #path} or {@link #var variable } with the {@link #value}.
       * 
       * @param    body
       *           The body string.
       *           
       * @return   The processed body.
       */
      public String apply(String body)
      {
         // replace all variables in the value
         String val = service.getResourcePool().setVariables(value);
         
         // replace the explicit variable reference (in the body) with its value
         if (var != null)
         {
            Map<String, String> vals = new HashMap<>();
            vals.put(var.toUpperCase(), val);
            body = StrSubstitutor.replace(body, vals);
         }
         
         // replace the explicit path with the value
         if (path != null)
         {
            body = service.replacePath(body, path, val);
         }
         
         return body;
      }
   }
}
