package com.eactive.httpmockserver.api.service;

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.common.exception.NotFoundException;
import org.apache.commons.beanutils.BeanUtils;
import org.hibernate.Hibernate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;

@Service
@Transactional
public class MockRouteService {

    private static final String NOT_FOUND_MESSAGE = "Route Not Found";

    @Autowired
    MockRouteRepository mockRouteRepository;

    private List<MockRoute> allRoutes = new ArrayList<>();

    public List<MockRoute> getAllRoutes() {
        return allRoutes;
    }

    public void setAllRoutes(List<MockRoute> allRoutes) {
        this.allRoutes = allRoutes;
    }

    public Page<MockRoute> findAll(Specification<MockRoute> mockRouteSpecification, Pageable pageable) {
        return mockRouteRepository.findAll(mockRouteSpecification, pageable);
    }

    public MockRoute findById(Long id) {
        return mockRouteRepository.findById(id).orElseThrow(() -> new NotFoundException(NOT_FOUND_MESSAGE));
    }

    public void create(MockRoute mockRoute) {

        if (!mockRoute.getPathPattern().startsWith("/")) {
            mockRoute.setPathPattern("/" + mockRoute.getPathPattern());
        }

        for (MockResponse response : mockRoute.getResponses()) {
            response.setRoute(mockRoute);

            if (response.getConditions() != null) {
                for (MockCondition condition : response.getConditions()) {
                    condition.setMockResponse(response);
                }
            }
        }
        mockRouteRepository.save(mockRoute);
    }

    public void deleteById(long id) {
        mockRouteRepository.deleteById(id);
    }

    public void update(Long id, MockRoute updated) {
        MockRoute route = mockRouteRepository.findById(id).orElseThrow(() -> new NotFoundException(NOT_FOUND_MESSAGE));

        route.getResponses().clear();

        route.setName(updated.getName());
        route.setDescription(updated.getDescription());
//        route.setMethod(updated.getMethod());
//        route.setPathPattern(updated.getPathPattern());
        if (!route.getPathPattern().startsWith("/")) {
            route.setPathPattern("/" + route.getPathPattern());
        }

        route.getResponses().addAll(updated.getResponses());
        for (MockResponse response : route.getResponses()) {
            response.setRoute(route);

            if (response.getConditions() != null) {
                for (MockCondition condition : response.getConditions()) {
                    condition.setMockResponse(response);
                }
            }

        }
        mockRouteRepository.save(route);

    }

    public void initAllRoutes() {
        allRoutes.clear();
        List<MockRoute> routes = mockRouteRepository.findAll();
        for (MockRoute route : routes) {
            for (MockResponse response : route.getResponses()) {
                if (response.getConditions() != null) {
                    for (MockCondition condition : response.getConditions()) {
                        Hibernate.initialize(condition.getMockResponse());
                    }
                }
                if (response.getHeaders() != null) {
                    Hibernate.initialize(response.getHeaders());
                }
                Hibernate.initialize(response);
            }
            try {
                MockRoute cloned = (MockRoute) BeanUtils.cloneBean(route);
                allRoutes.add(cloned);
            } catch (IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
