package com.eactive.httpmockserver.api.controller;

import com.eactive.httpmockserver.api.dto.MockRequest;
import com.eactive.httpmockserver.api.entity.MockCondition;
import com.eactive.httpmockserver.api.entity.MockResponse;
import com.eactive.httpmockserver.api.entity.MockRoute;
import com.eactive.httpmockserver.api.repository.MockRouteRepository;
import com.eactive.httpmockserver.api.service.MockRouteService;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.PathNotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@RestController
public class MockApiController {

    @Autowired
    private MockRouteService mockRouteService;


    //logger
    private static final Logger logger = LoggerFactory.getLogger(MockApiController.class);


    @RequestMapping(value = "/{_:^(?!swagger-ui|mgmt|min-maps|plugins|dist|css|fonts|js|h2-console|img|pages|favicon.ico).*$}/**")
    public ResponseEntity<String> handleRequest(HttpServletRequest httpRequest) {
        MockRequest request = new MockRequest(httpRequest);
        AntPathMatcher pathMatcher = new AntPathMatcher();

        for (MockRoute mockRoute : mockRouteService.getAllRoutes()) {
            if (isRouteMatch(mockRoute, request)) {
                logger.debug("===========================");
                logger.debug(request.getBody());
                logger.debug("===========================");
                Map<String, String> pathVariables = pathMatcher.extractUriTemplateVariables(mockRoute.getPathPattern(), request.getUri());
                MockResponse matchingResponse = findMatchingResponse(mockRoute, request, pathVariables);
                if (matchingResponse != null) {
                    if (matchingResponse.getDelay() > 0) {
                        try {
                            Thread.sleep(matchingResponse.getDelay());
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    HttpHeaders responseHeaders = new HttpHeaders();
                    matchingResponse.getHeaders().forEach(responseHeaders::set);

                    request.getParams().putAll(pathVariables);

                    return new ResponseEntity<>(replaceParams(matchingResponse.getBody(), request.getParams()), responseHeaders, HttpStatus.valueOf(matchingResponse.getStatusCode()));
                }
            }
        }

        // If no matching route or response is found, return a 404 Not Found
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Not Found");
    }

    private String replaceParams(String responseBody, Map<String, String> params) {
        for (Map.Entry<String, String> entry : params.entrySet()) {
            responseBody = responseBody.replace("{" + entry.getKey() + "}", entry.getValue());
        }
        return responseBody;
    }

    private boolean isRouteMatch(MockRoute mockRoute, MockRequest request) {
        AntPathMatcher pathMatcher = new AntPathMatcher();
        return mockRoute.getMethod().equalsIgnoreCase(request.getMethod()) && pathMatcher.match(mockRoute.getPathPattern(), request.getUri());
    }

    private MockResponse findMatchingResponse(MockRoute mockRoute, MockRequest mockRequest, Map<String, String> pathVariables) {

        MockResponse defaultResponse = mockRoute.getResponses().stream().filter(MockResponse::isDefaultResponse).findFirst().orElse(null);
        List<MockResponse> responses = mockRoute.getResponses().stream().filter(response -> !response.isDefaultResponse()).collect(Collectors.toList());
        if(defaultResponse!=null){
            responses.add(defaultResponse);
        }

        for (MockResponse response : responses) {
            if (response.getConditions() == null || isRequestMatching(response.getConditions(), mockRequest, pathVariables)) {
                return response;
            }
        }
        return null;
    }

    private boolean isRequestMatching(List<MockCondition> conditions, MockRequest request, Map<String, String> pathVariables) {

        if (conditions == null || conditions.size() == 0) {
            return true;
        }

        for (MockCondition condition : conditions) {
            if (condition.getType().equalsIgnoreCase("query")) {
                if(!condition.getValue().equals(request.getParam(condition.getKey()))){
                    return false;
                }
            } else if (condition.getType().equalsIgnoreCase("header")) {
                if (!condition.getValue().equals(request.getHeader(condition.getKey()))){
                    return false;
                }
            } else if (condition.getType().equalsIgnoreCase("path")) {
                if (!condition.getValue().equals(pathVariables.get(condition.getKey()))){
                    return false;
                }
            } else if (condition.getType().equalsIgnoreCase("jsonpath")) {
                try{
                    Object value = JsonPath.read(request.getBody(), condition.getKey());
                    return value.toString().equals(condition.getValue());
                }catch(PathNotFoundException e) {
                    return false;
                }
            }

        }
        return true;
    }

}
