package code;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;

import java.util.List;

import javax.net.ssl.HttpsURLConnection;

import org.apache.commons.codec.binary.Base64;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;

public class ObtainToken implements HttpHandler{
	
	private KeyCloak kcs;
	private String state;
	private String codeVerifier;
	
	public ObtainToken(KeyCloak kcs) {
		this.kcs = kcs;
	}

	@Override
    public void handle(HttpExchange exchange) throws IOException {
        URI requestURI = exchange.getRequestURI();
        String stringURI = requestURI.toString();
        boolean wantsRedirectPage = Helper.compareText(stringURI,URI.create("/").toString());
        boolean wantsToken = Helper.compareText(stringURI,URI.create("/secured").toString()); 
        if(wantsToken)
        System.out.println("URI  = "+exchange.getRequestURI().getPath());
        
        if(!wantsRedirectPage && !wantsToken) {
            String error = "Invalid URI";
            OutputStream os = exchange.getResponseBody();
            exchange.sendResponseHeaders(400, error.getBytes().length);
            os.write(error.getBytes());
            os.close();
            return;
        }

        String requestMethod = exchange.getRequestMethod(); 
        if (Helper.compareText(requestMethod, "GET")) {
        	if(wantsRedirectPage) {
	        	codeVerifier = createRandomString();
	        	try {
					String codeChallenge = createCodeChallenge(codeVerifier);
					
					state = createRandomString(); //An opaque arbitrary alphanumeric string your app adds to the initial request that Auth0 includes when redirecting back to your application. 
					String nonce = "a81e1a84-8885-4702-b8d1-f6c5a0d1fc4d";
//					System.out.println("CODE VERIFIER = "+codeVerifier);
					// get the html page
		            List<String> strlist = new ArrayList<>();
		            String response = null;
		            response = getRedirectPage();
		            strlist.add("text/html");
		            
		            if(response != null && !Helper.compareText(response, "fail")){
		            	response = response.replace("$DOMAIN", kcs.authServer())
		            			.replace("$REALM", kcs.realm())
		            			.replace("$MY_CODE_CHALLENGE", codeChallenge)
		            			.replace("$MY_CLIENT_ID", kcs.clientId())
		            			.replace("$MY_REDIRECT_URI", kcs.redirectUri())
		            			.replace("$MY_NONCE",nonce)
		            			.replace("$MY_STATE", state);	            	
		        
		                exchange.getResponseHeaders().put("content-type", strlist);
		                exchange.sendResponseHeaders(200, response.getBytes().length);
		                OutputStream os = exchange.getResponseBody();
		                os.write(response.getBytes());
		                os.close();
		            } else {
		                exchange.sendResponseHeaders(500, response.getBytes().length);
		                OutputStream os = exchange.getResponseBody();
		                os.write(response.getBytes());
		                os.close();
		            }
				} catch (UnsupportedEncodingException | NoSuchAlgorithmException e) {
					System.out.println("Error during creation of code challenge");
				}  
        	}
        	if(wantsToken) { 
        		// NON FUNZIONA PERCHE' LA 
        		String[] arr = stringURI.split("/secured");
        		for(int i=0; i<arr.length; i++)
        			System.out.println(arr[i]);
        		System.out.println("lunghezza = "+arr.length);
        		
        		String allParamsString = stringURI.split("/secured")[1];
        		System.out.println("allParamsString = "+allParamsString);
        		String[] allParamsArray = allParamsString.split("&");
        		String state = allParamsArray[0];
        		if(!this.state.equals(state)) {
        			Helper.badRequest(exchange);
        			return;
        		}
        		String authCode = allParamsArray[2];
        		
        		// request token  
        		String httpsURL = "http://"+kcs.authServer()+"/realms/"+kcs.realm()+"/protocol/openid-connect/token";
        		URL myUrl = new URL(httpsURL);
//                SSLUtilities.trustAllHttpsCertificates();
//                SSLUtilities.trustAllHostnames();
                HttpsURLConnection conn = (HttpsURLConnection)myUrl.openConnection();

                conn.setReadTimeout(7000);
                conn.setConnectTimeout(7000);
                conn.setRequestMethod("POST");
                conn.setDoOutput(true);
                conn.setDoInput(true);
                
                conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
             
                String body = "grant_type=authorization_code"
                		+ "&client_id="+kcs.clientId()
                		+ "&code_verifier="+codeVerifier
                		+ "&code="+authCode
                		+ "&redirect_uri=https://localhost:3000/secured";
                OutputStream outputStream = conn.getOutputStream();
                outputStream.write(body.getBytes("UTF-8"));
                outputStream.close();

                String inputLine;
                InputStream is = conn.getInputStream();
                InputStreamReader isr = new InputStreamReader(is);
                BufferedReader br = new BufferedReader(isr);
                String response = "";
                while ((inputLine = br.readLine()) != null) {
                	response += inputLine;
                }

                br.close();
                System.out.println(response);
//        	String answer = response.replace(remoteHOST,localHOST);
        		
        	}
        } else {
            Helper.methodNotAllowed(exchange);
        }
    }

    

	private static String getRedirectPage() {
        String line;
        String page = Server.CLIENT_PATH+"/redirect.html";

        StringBuilder answer = new StringBuilder();
        if (getExtension(page).length() == 0)
            page += ".html";

        BufferedReader bufferedReader = null;
        try {
            FileReader fileReader = new FileReader(page);

            bufferedReader = new BufferedReader(fileReader);
            boolean isComment = false;
            while ((line = bufferedReader.readLine()) != null) {
            	line = line.trim();

            	if(line.startsWith("<!--") && line.endsWith("-->")) {
            		continue;
            	}
            	if(line.startsWith("<!--")) {
            		isComment = true;
            		continue;
            	}
            	if(line.endsWith("-->")) {
            		isComment = false;
            		continue;
            	}

            	if(!isComment && line.length()>0)
                	answer.append(line).append("\n");
            }
        } catch (FileNotFoundException ex) {
            System.out.println("Unable to open file '" + page + "'");
            return "fail";
        } catch (IOException ex) {
            System.out.println("Error reading file '" + page + "'");
            return "fail";
        } finally {
            try{
                if(bufferedReader != null)
                    bufferedReader.close();
            } catch (IOException ex){
                System.out.println("Error closing bufferedReader");
            }
        }
        return answer.toString();
    }


    private static String getExtension(String file) {
        int i = file.length() - 1;
        while (i > 0 && file.charAt(i) != '.' && file.charAt(i) != '/')
            i--;
        if (file.charAt(i) == '.')
            return file.substring(i + 1);
        else
            return "";
    }
    
    
    private String createRandomString() {
    	SecureRandom sr = new SecureRandom();
    	byte[] code = new byte[32];
    	sr.nextBytes(code); 
    	return java.util.Base64.getUrlEncoder().withoutPadding().encodeToString(code);
    }
    
    
    private String createCodeChallenge(String verifier) throws UnsupportedEncodingException, NoSuchAlgorithmException {
    	byte[] bytes = verifier.getBytes("US-ASCII");
    	MessageDigest md = MessageDigest.getInstance("SHA-256");
    	md.update(bytes, 0, bytes.length);
    	byte[] digest = md.digest();
    	return Base64.encodeBase64URLSafeString(digest);
	}

}