package com.github.kislayverma.rulette.rest.controller;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.kislayverma.rulette.RuleSystem;
import com.github.kislayverma.rulette.rest.Constants;
import com.github.kislayverma.rulette.core.rule.Rule;
import io.netty.handler.codec.http.HttpResponseStatus;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.restexpress.Request;
import org.restexpress.Response;

public class RuleSystemController {

    private final RuleSystemFactory ruleSystemFactory;

    public RuleSystemController() {
        this.ruleSystemFactory = new RuleSystemFactory();
    }

    public List<Rule> getAllRules(Request request, Response response) {
        String ruleSystemName = request.getHeader(Constants.Url.RULE_SYSTEM_NAME);
        return getRuleSystem(ruleSystemName).getAllRules();
    }

    public Rule getApplicableRule(Request request, Response response) throws Exception {
        try {
            Map<String, String> map = convertInputJsonToMap(request);
            String ruleSystemName = request.getHeader(Constants.Url.RULE_SYSTEM_NAME);

            return getRuleSystem(ruleSystemName).getRule(map);
        } catch (IOException ex) {
            ex.printStackTrace();
            response.setException(ex);
            response.setResponseStatus(HttpResponseStatus.BAD_REQUEST);
        }

        return null;
    }

    public Rule getNextApplicableRule(Request request, Response response) throws Exception {
        try {
            Map<String, String> map = convertInputJsonToMap(request);
            String ruleSystemName = request.getHeader(Constants.Url.RULE_SYSTEM_NAME);

            return getRuleSystem(ruleSystemName).getNextApplicableRule(map);
        } catch (IOException ex) {
            ex.printStackTrace();
            response.setException(ex);
            response.setResponseStatus(HttpResponseStatus.BAD_REQUEST);
        }

        return null;
    }

    public Rule getRule(Request request, Response response) {
        String ruleSystemName = request.getHeader(Constants.Url.RULE_SYSTEM_NAME);
        String ruleIdStr = request.getHeader(Constants.Url.RULE_ID);
        Integer ruleId = Integer.parseInt(ruleIdStr);

        return getRuleSystem(ruleSystemName).getRule(ruleId);
    }

    public Rule addRule(Request request, Response response) throws Exception {
        try {
            Map<String, String> map = convertInputJsonToMap(request);
            String ruleSystemName = request.getHeader(Constants.Url.RULE_SYSTEM_NAME);

            return getRuleSystem(ruleSystemName).addRule(map);
        } catch (IOException ex) {
            ex.printStackTrace();
            response.setException(ex);
            response.setResponseStatus(HttpResponseStatus.BAD_REQUEST);
        }

        return null;
    }

    public Rule updateRule(Request request, Response response) throws Exception {
        try {
            Map<String, String> map = convertInputJsonToMap(request);
            String ruleSystemName = request.getHeader(Constants.Url.RULE_SYSTEM_NAME);
            String ruleIdStr = request.getHeader(Constants.Url.RULE_ID);
            Integer ruleId = Integer.parseInt(ruleIdStr);

            RuleSystem rs = getRuleSystem(ruleSystemName);
            Rule oldRule = rs.getRule(ruleId);
            if (oldRule == null) {
                throw new RuntimeException("No existing rule found for the input");
            } else {
                Rule newRule = rs.createRuleObject(map);

                return rs.updateRule(oldRule, newRule);
            }
        } catch (IOException ex) {
            ex.printStackTrace();
            response.setException(ex);
            response.setResponseStatus(HttpResponseStatus.BAD_REQUEST);
        }

        return null;
    }

    public void deleteRule(Request request, Response response) throws Exception {
        try {
            Map<String, String> map = convertInputJsonToMap(request);
            String ruleSystemName = request.getHeader(Constants.Url.RULE_SYSTEM_NAME);
            String ruleIdStr = request.getHeader(Constants.Url.RULE_ID);
            Integer ruleId = Integer.parseInt(ruleIdStr);

            getRuleSystem(ruleSystemName).deleteRule(ruleId);
            response.setResponseNoContent();
        } catch (IOException ex) {
            ex.printStackTrace();
            response.setException(ex);
            response.setResponseStatus(HttpResponseStatus.BAD_REQUEST);
        }
    }

    public void reloadRuleSystem(Request request, Response response) throws Exception {
        String ruleSystemName = request.getHeader(Constants.Url.RULE_SYSTEM_NAME);
        ruleSystemFactory.reloadRuleSystem(ruleSystemName);
        response.setResponseNoContent();
    }

    private RuleSystem getRuleSystem(String ruleSystemName) {
        return ruleSystemFactory.getRuleSystem(ruleSystemName);
    }

    private Map<String, String> convertInputJsonToMap(Request request) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
//        System.out.println(request.getHeaderNames());
//        System.out.println(request.getBodyFromUrlFormEncoded(true));

        Map<String, String> map = new HashMap<>();
        Set<String> keySet = request.getBodyFromUrlFormEncoded(true).keySet();
        for (String payload : keySet) {
            Map<String, Object> objMap = mapper.readValue(payload, new TypeReference<Map<String, Object>>() {
            });
            for (Map.Entry<String, Object> entry : objMap.entrySet()) {
                map.put(entry.getKey(), (String) entry.getValue());
            }

            return map;
        }

        return null;
    }
}

@@ -555,16 +555,73 @@
 esponse;
+%0Aimport org.slf4j.Logger;%0Aimport org.slf4j.LoggerFactory;
 %0A%0Apublic
@@ -705,16 +705,110 @@
 Factory;
+%0A    private static final Logger LOGGER = LoggerFactory.getLogger(RuleSystemController.class);
 %0A%0A    pu
@@ -5320,26 +5320,24 @@
 pper();%0A
-//
         
 System.o
@@ -5328,35 +5328,51 @@
         
-System.out.println(
+LOGGER.info(%22Request headers : %22 + 
 request.
@@ -5394,18 +5394,16 @@
 ));%0A
-//
         
 Syst
@@ -5402,27 +5402,41 @@
     
-System.out.println(
+LOGGER.info(%22Request body  : %22 + 
 requ
@@ -5684,16 +5684,32 @@
 objMap =
+%0A               
  mapper.
@@ -5773,25 +5773,13 @@
 () %7B
-%0A            
 %7D);%0A
+%0A
     
@@ -5790,65 +5790,53 @@
     
-for (
+obj
 Map.
-E
+e
 ntry
-%3CString, Object%3E entry : objMap.entrySet())
+Set().stream().forEach((entry) -%3E
  %7B%0A 
@@ -5898,24 +5898,24 @@
 etValue());%0A
-
             
@@ -5907,32 +5907,34 @@
 );%0A            %7D
+);
 %0A%0A            re

Use logging and functional operations