/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.opentable.extension;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder;
import com.github.tomakehurst.wiremock.common.BinaryFile;
import com.github.tomakehurst.wiremock.common.FileSource;
import com.github.tomakehurst.wiremock.extension.ResponseTransformer;
import com.github.tomakehurst.wiremock.http.Request;
import com.github.tomakehurst.wiremock.http.ResponseDefinition;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class BodyTransformer extends ResponseTransformer {

    private final Pattern interpolationPattern = Pattern.compile("\\$\\(.*?\\)");
    private final Pattern randomIntegerPattern = Pattern.compile("!RandomInteger");
    private ObjectMapper jsonMapper = new ObjectMapper();
    private ObjectMapper xmlMapper = new XmlMapper();

    @Override
    public ResponseDefinition transform(Request request, ResponseDefinition responseDefinition, FileSource files) {
        Map object = null;
        try {
            object = jsonMapper.readValue(request.getBodyAsString(), Map.class);
        } catch (IOException e) {
            try
            {
                object = xmlMapper.readValue(request.getBodyAsString(), Map.class);
            }
            catch (IOException ex)
            {
                ex.printStackTrace();
            }
        }

        if (hasEmptyBody(responseDefinition)) {
			return responseDefinition;
		}

		String body = getBody(responseDefinition, files);

        return ResponseDefinitionBuilder
                .like(responseDefinition).but()
				.withBodyFile(null)
                .withBody(transformResponse(object, body))
                .build();
    }

    public String name() {
        return "body-transformer";
    }

    @Override
    public boolean applyGlobally() {
        return false;
    }

    private String transformResponse(Map requestObject, String response) {
        String modifiedResponse = response;

        Matcher matcher = interpolationPattern.matcher(response);
        while (matcher.find())
        {
            String group = matcher.group();
            modifiedResponse = modifiedResponse.replace(group, getValue(group, requestObject));

        }

        return modifiedResponse;
    }


    private CharSequence getValue(String group, Map requestObject) {
        if (randomIntegerPattern.matcher(group).find()) {
            return String.valueOf(new Random().nextInt(2147483647));
        }

        return getValueFromRequestObject(group, requestObject);
    }

	private CharSequence getValueFromRequestObject(String group, Map requestObject) {
		String fieldName = group.substring(2,group.length() -1);
		String[] fieldNames = fieldName.split("\\.");
		Object tempObject = requestObject;
		for (String field : fieldNames) {
			if (tempObject instanceof Map) {
				tempObject = ((Map) tempObject).get(field);
			}
		}
		return String.valueOf(tempObject);
	}

	private boolean hasEmptyBody(ResponseDefinition responseDefinition) {
		return responseDefinition.getBody() == null && responseDefinition.getBodyFileName() == null;
	}

	private String getBody(ResponseDefinition responseDefinition, FileSource files) {
		String body;
		if (responseDefinition.getBody() != null) {
			body = responseDefinition.getBody();
		} else {
			BinaryFile binaryFile = files.getBinaryFileNamed(responseDefinition.getBodyFileName());
			body = new String(binaryFile.readContents(), StandardCharsets.UTF_8);
		}
		return body;
	}
}


@@ -2165,19 +2165,28 @@
 ion)) %7B%0A
-%09%09%09
+            
 return r
@@ -2208,15 +2208,27 @@
 on;%0A
-%09%09%7D%0A%0A%09%09
+        %7D%0A%0A        
 Stri
@@ -2363,20 +2363,32 @@
 ).but()%0A
-%09%09%09%09
+                
 .withBod
@@ -3338,17 +3338,20 @@
 %0A    %7D%0A%0A
-%09
+    
 private 
@@ -3420,26 +3420,32 @@
 stObject) %7B%0A
-%09%09
+        
 String field
@@ -3489,18 +3489,24 @@
 () -1);%0A
-%09%09
+        
 String%5B%5D
@@ -3543,18 +3543,24 @@
 %22%5C%5C.%22);%0A
-%09%09
+        
 Object t
@@ -3586,18 +3586,24 @@
 Object;%0A
-%09%09
+        
 for (Str
@@ -3628,19 +3628,28 @@
 ames) %7B%0A
-%09%09%09
+            
 if (temp
@@ -3677,12 +3677,24 @@
 ) %7B%0A
-%09%09%09%09
+                
 temp
@@ -3737,19 +3737,40 @@
 d);%0A
-%09%09%09%7D%0A%09%09%7D%0A%09%09
+            %7D%0A        %7D%0A        
 retu
@@ -3800,21 +3800,27 @@
 bject);%0A
-%09%7D%0A%0A%09
+    %7D%0A%0A    
 private 
@@ -3881,18 +3881,24 @@
 tion) %7B%0A
-%09%09
+        
 return r
@@ -3986,13 +3986,19 @@
 ll;%0A
-%09%7D%0A%0A%09
+    %7D%0A%0A    
 priv
@@ -4075,18 +4075,24 @@
 iles) %7B%0A
-%09%09
+        
 String b
@@ -4096,18 +4096,24 @@
 g body;%0A
-%09%09
+        
 if (resp
@@ -4148,19 +4148,28 @@
 null) %7B%0A
-%09%09%09
+            
 body = r
@@ -4197,18 +4197,24 @@
 Body();%0A
-%09%09
+        
 %7D else %7B
@@ -4214,19 +4214,28 @@
  else %7B%0A
-%09%09%09
+            
 BinaryFi
@@ -4318,11 +4318,20 @@
 ));%0A
-%09%09%09
+            
 body
@@ -4400,14 +4400,26 @@
 8);%0A
-%09%09%7D%0A%09%09
+        %7D%0A        
 retu
@@ -4431,9 +4431,12 @@
 dy;%0A
-%09
+    
 %7D%0A%7D%0A

convert tabs to spaces