package eu.nets.sis.eident.demoapp.controller;

import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import eu.nets.sis.eident.demoapp.service.ClaimsService;

/**
 * Controller class, responsible for mapping  application requests to appropriate methods and views
 *
 */
@Controller
@PropertySource("classpath:application.properties")
public class ApplicationController {
	private static Logger logger = Logger.getLogger(ApplicationController.class);

	private String mid;
	private String nonce;

	@Value("${oidc.scope}")
	private String scope;

	@Value("${its.url}")
	private String itsURL;

	@Value("${oidc.redirecturi}")
	private String oidcRedirectUri;
	
	@Value("${oidc.redirecturipopup}")
	private String oidcRedirectUriPopup;

	@Value("${oidc.discoveryuri}")
	private String oidcDiscoveryUri;

	@Value("${oidc.truststorepath}")
	private String truststorePath;

	@Value("${oidc.truststorepwd}")
	private String truststorePassword;
	
	@Value("${server.port}")
	private String port;

	@Value("${idtoken.decrypt.private.keystore}")
	private String decryptionKeystore;

	@Value("${idtoken.decrypt.private.keystorepassword}")
	private String decryptionKeystorePassword;

	@Value("${idtoken.decrypt.private.keyalias}")
	private String decryptionKeyAlias;

	@Autowired
	private ApplicationArguments applicationArguments;
	
	private boolean isPopUISelected = false;
	
	/**
	 * Displays landing page with content
	 * 
	 * @param model Form data in key value pair
	 * @return String value that represents the target HTML page
	 */
	@RequestMapping(value = "/eident", method = RequestMethod.GET)
	public String index(ModelMap model) {
		//set mid
		this.mid = applicationArguments.getSourceArgs().length > 0 ? applicationArguments.getSourceArgs()[0] : "";
		// nonce generation is for demo purpose.  Merchants should handle this appropriately.
		nonce = new BigInteger(50, new SecureRandom()).toString(16);
		
		ClaimsService claimsService = new ClaimsService();
		String eIdentUrl = claimsService.generateEIdentUrl(mid, scope, itsURL);

		// Merchant end point that will be responsible for retrieving ID Token
		model.put("eIdentUrl", eIdentUrl + "&redirect_uri="+oidcRedirectUri); 
		model.put("eIdentUrlForPopup", eIdentUrl + "&redirect_uri="+oidcRedirectUriPopup);

		logger.info("Loading home page");
		return "index";
	}
	
	@RequestMapping(value = "/eident/popupreturn", method = RequestMethod.GET)
	public String closePopupAndGetClaims(HttpServletRequest request, ModelMap model) throws Exception {
		isPopUISelected = true;
		model.put("oidcRedirectUri", oidcRedirectUri);
		return "popupCloser";
	}

	/**
	 * Retrieves and displays claims (Success page)
	 * 
	 * @param request HTTP request object
	 * @param model Form data in key value pair
	 * @return String value that represents the target HTML page
	 * @throws Exception in case of any error
	 */
	@RequestMapping(value = "/eident/return", method = RequestMethod.GET)
	public String claims(HttpServletRequest request, ModelMap model) throws Exception {
		
		String redirectUri = isPopUISelected ? oidcRedirectUriPopup : oidcRedirectUri;
		isPopUISelected = false;
		
		Map<String, String> claims = new HashMap<>();
		String responseURL = request.getRequestURI() + "?" + request.getQueryString();

		String secretCode = applicationArguments.getSourceArgs().length > 1 ? applicationArguments.getSourceArgs()[1] : "";

		//override the default decryption keystore by merchants own keystore.
		decryptionKeystore = applicationArguments.getSourceArgs().length > 2 ? applicationArguments.getSourceArgs()[2] : decryptionKeystore;
		decryptionKeystorePassword = applicationArguments.getSourceArgs().length > 3 ? applicationArguments.getSourceArgs()[3] : decryptionKeystorePassword;
		decryptionKeyAlias = applicationArguments.getSourceArgs().length > 4 ? applicationArguments.getSourceArgs()[4] : decryptionKeyAlias;

		ClaimsService claimsService = new ClaimsService();
		Map<String, String> result = claimsService.getClaims(responseURL, redirectUri, oidcDiscoveryUri, mid,
				secretCode, nonce, truststorePath, truststorePassword, decryptionKeystore, decryptionKeystorePassword, decryptionKeyAlias);
		
		if(null == result) {
			model.put("errorDesc", claimsService.getErrorDesc());
			return "canceltimeout";
		}
		
		if (!result.isEmpty()) {
			claims.putAll(result);
		}
		model.put("oidc", claims);
		logger.info("Identification is completed");

		return "status";
	}

	/**
	 * Responsible for showing error page on error
	 * 
	 * @return String value that represents the target HTML page
	 */
	@RequestMapping(value = "/error", method = RequestMethod.GET)
	@ExceptionHandler(Exception.class)
	public String defaultErrorHandler() {
		logger.info("Moving to error page");
		return "error";
	}
	
	/**
	 * @return String value as a modal attribute, that represents eident home page
	 */
	@ModelAttribute("homeUrl")
	public String getHomeUrl() {
		return String.format("http://localhost:%s/eident", port);
	}
}
