package eu.dnetlib.openaire.usermanagement;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import eu.dnetlib.openaire.user.pojos.RegisteredService;
import eu.dnetlib.openaire.usermanagement.utils.RegisteredServicesUtils;
import eu.dnetlib.openaire.usermanagement.utils.TokenUtils;
import org.apache.http.HttpResponse;
import org.apache.log4j.Logger;
import org.mitre.openid.connect.model.OIDCAuthenticationToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class RegisteredServicesServlet extends HttpServlet {

    private Logger logger = Logger.getLogger(RegisteredServicesServlet.class);

    @Autowired
    private RegisteredServicesUtils registeredServicesUtils;

    @Autowired
    private TokenUtils tokenUtils;

    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this,
                config.getServletContext());
    }

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        request.getSession().setAttribute("authenticated",
                !SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString()
                        .equals("anonymousUser"));

        OIDCAuthenticationToken authentication = (OIDCAuthenticationToken) SecurityContextHolder.
                getContext().getAuthentication();

        String userId = authentication.getSub();

        List<RegisteredService> registeredServices = null;
        try {
            registeredServices = registeredServicesUtils.
                    getRegisteredServiceDao().fetchAllRegisteredServicesByOwner(userId);
            //System.out.println("LOAD REGISTERED SERVICES. " + registeredServices.size());

            if (registeredServices.isEmpty()) {
                request.getSession().setAttribute("showEmptyList", true);
            } else {
                Map<String, ServiceResponse> serviceResponses = new HashMap<>();
                Map<String, String> serviceKey = new HashMap<>();

                for (RegisteredService registeredService:registeredServices) {
                    ServiceResponse serviceResponse = tokenUtils.getRegisteredService(registeredService.getClientId(),registeredService.getRegistrationAccessToken());
                    serviceResponses.put(registeredService.getId(), serviceResponse);
                    serviceKey.put(registeredService.getId(), extractPublicKeySet(serviceResponse));
                }

                boolean reachedLimit = reachedMaximumNumberOfServices(registeredServices);
                StringBuilder name = new StringBuilder().append(authentication.getUserInfo().getGivenName().charAt(0));
                name.append(authentication.getUserInfo().getFamilyName().charAt(0));
                request.getSession().setAttribute("name", name.toString());
                request.getSession().setAttribute("reachedLimit", reachedLimit);
                //System.out.println("REACHED LIMIT??? " + reachedLimit);

                request.getSession().setAttribute("services", serviceResponses);
                request.getSession().setAttribute("keys", serviceKey);
            }
            request.getSession().setAttribute("registeredServices", registeredServices);

        } catch (SQLException sqle) {
            logger.error("Error fetching registered services for user " + userId , sqle);
            request.getSession().setAttribute("message", "Error fetching registered services. " +
                    "Please try again later.");
            request.getSession().setAttribute("showEmptyList", false);
            request.getRequestDispatcher("./registeredServices.jsp").include(request, response);
        }

        response.setContentType("text/html");
        request.getRequestDispatcher("./registeredServices.jsp").include(request, response);
    }

    private String extractPublicKeySet(ServiceResponse serviceResponse) {
        if (serviceResponse.getJwksUri()!=null && !serviceResponse.getJwksUri().isEmpty())
            return serviceResponse.getJwksUri();

        return extractJSONJwk(serviceResponse.getJwks());
    }

    private String extractJSONJwk(Jwks jwks) {
        Gson gson = new GsonBuilder().setPrettyPrinting().create();
        //System.out.println(gson.toJson(jwks));
        return gson.toJson(jwks);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        OIDCAuthenticationToken authentication = (OIDCAuthenticationToken) SecurityContextHolder.
                getContext().getAuthentication();
        String id = request.getParameter("id");

        //System.out.println("POST " +id);

        if (id!=null && !id.isEmpty()) {
            try {
                RegisteredService registeredService = registeredServicesUtils.getRegisteredServiceDao().fetchRegisteredServiceById(Integer.parseInt(id));

                if (!registeredService.getOwner().equals(authentication.getSub())) {
                    request.getSession().setAttribute("message", "You are not allowed to delete the service.");
                    //System.out.println("BLOCKED " + registeredService.getOwner() + " >> " + authentication.getSub());
                    response.sendRedirect("./registeredServices");
                    return;
                }

                HttpResponse resp = tokenUtils.deleteService(registeredService.getClientId(), registeredService.getRegistrationAccessToken());

                int statusCode = resp.getStatusLine().getStatusCode();
                //System.out.println("STATUS CODE " + statusCode);

                if (statusCode != 204) {
                    logger.error("Unable to delete the service. Status code was " + statusCode);
                    request.getSession().setAttribute("message", "Fail to delete the service. Status " + statusCode);
                    //System.out.println("AAI blocked");
                    response.sendRedirect("./registeredServices");
                    return;
                } else {
                    registeredServicesUtils.getRegisteredServiceDao().delete(Integer.parseInt(id));
                    request.getSession().setAttribute("success", "The service was successfully deleted.");
                    //System.out.println("HERE HERE");
                }

            } catch (SQLException sqle) {
                logger.error("Unable to contact db.", sqle);
                request.getSession().setAttribute("message", "Fail to delete the service. Please try again later.");
            }

        } else {
            request.getSession().setAttribute("message", "Error selecting service to delete. Please try again.");
        }

        response.sendRedirect("./registeredServices");
    }

    private boolean reachedMaximumNumberOfServices(List<RegisteredService> registeredServices) {
        return registeredServices.size() >= 5;
    }
}
