package dev.peerat.backend.routes.users;

import static dev.peerat.framework.RequestType.POST;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Map;
import java.util.Base64.Encoder;
import java.util.regex.Matcher;

import javax.net.ssl.HttpsURLConnection;

import org.json.simple.JSONAware;
import org.json.simple.JSONObject;

import dev.peerat.backend.bonus.extract.RouteDoc;
import dev.peerat.backend.model.PeerAtUser;
import dev.peerat.backend.repository.DatabaseRepository;
import dev.peerat.backend.utils.FormResponse;
import dev.peerat.framework.Context;
import dev.peerat.framework.HttpReader;
import dev.peerat.framework.HttpWriter;
import dev.peerat.framework.Route;
import dev.peerat.framework.Router;

public class MailConfirmation extends FormResponse {

	private DatabaseRepository databaseRepo;
	private Router<PeerAtUser> router;
	private String usersFilesPath;
	private KeyPairGenerator generator;
	private Encoder encoder;
	private String gitToken;
	private Map<String, Integer> playersWaiting;

	public MailConfirmation(
			DatabaseRepository databaseRepo,
			Router<PeerAtUser> router,
			String initUsersFilesPath,
			String gitToken,
			Map<String, Integer> playersWaiting){
		this.databaseRepo = databaseRepo;
		this.router = router;
		usersFilesPath = initUsersFilesPath;
		this.gitToken = gitToken;
		try {
			generator = KeyPairGenerator.getInstance("RSA");
			generator.initialize(2048); //a changer ?
		} catch (NoSuchAlgorithmException e){
			e.printStackTrace();
		}
		encoder = Base64.getEncoder();
	}

	@RouteDoc(path = "/confirmation", responseCode = 200, responseDescription = "L'utilisateur est inscrit")
	@RouteDoc(responseCode = 403, responseDescription = "L'utilisateur est connecté")
	@RouteDoc(responseCode = 400, responseDescription = "Aucune données fournie / données invalide")

	@Route(path = "^\\/confirmation$", type = POST)
	public void exec(Matcher matcher, Context context, HttpReader reader, HttpWriter writer) throws Exception {
		if (context.isLogged()){
			context.response(403);
			return;
		}
		JSONObject json = json(reader);
		if(!areValids("email","code","pseudo","firstname","lastname","passwd")){
			context.response(400);
			return;
		}
		String email = (String) json.get("email");
		int code = (int) json.get("code");
		String pseudo = (String) json.get("pseudo");
		String firstname = (String) json.get("firstname");
		String lastname = (String) json.get("lastnae");
		String password = (String) json.get("passwd");
		
		Integer checkCode = playersWaiting.get(email);
		if(checkCode == null){
			context.response(400);
			return;
		}
		if(code == checkCode.intValue()){
			playersWaiting.remove(email);
			int id = databaseRepo.register(pseudo, email, password, firstname, lastname, "", "", "");
			if(id >= 0){
				createFolderToSaveSourceCode(pseudo);
				generateGitKey(email, pseudo, password);
				context.response(200,
						"Access-Control-Expose-Headers: Authorization",
						"Authorization: Bearer " + this.router.createAuthUser(new PeerAtUser(id)));
			}else{
				context.response(400);
				JSONObject error = new JSONObject();
				error.put("username_valid", pseudo);
				error.put("email_valid", email);
				writer.write(error.toJSONString());
			}
		}else{
			context.response(400);
		}
	}

	private void createFolderToSaveSourceCode(String pseudo) throws IOException {

		Files.createDirectories(Paths.get(String.format("%s/%s", usersFilesPath, pseudo)));
	}
	
	private String generateGitKey(String email, String pseudo, String password) throws Exception{
		KeyPair pair = generator.generateKeyPair(); //doit être unique ???
		
		JSONObject createUser = new JSONObject();
		createUser.put("email", email);
		createUser.put("username", pseudo);
		createUser.put("password", password);
		post("https://git-users.peerat.dev/api/v1/admin/users/", createUser);
		
		JSONObject sendKey = new JSONObject();
		sendKey.put("key", new String(encoder.encode(pair.getPrivate().getEncoded()))); //add ssh-rsa au début ?
		sendKey.put("read_only", false);
		sendKey.put("title", "peer_at_code_auto_push_key_"+pseudo);
		post("https://git-users.peerat.dev/api/v1/admin/users/"+pseudo+"/keys", sendKey);
		
		return new String(encoder.encode(pair.getPrivate().getEncoded()));
	}
	
	private void post(String url, JSONAware json) throws Exception{
		HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();
		connection.setRequestMethod("POST");
		connection.setRequestProperty("Content-Type","application/json");
		connection.setRequestProperty("Authorization","Bearer "+this.gitToken);
		connection.setDoInput(true);
		connection.setDoOutput(true);
		
		BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(connection.getOutputStream()));
		writer.write(json.toJSONString());
		writer.flush();
		writer.close();
		
		int response = connection.getResponseCode();
		if(response != 201) throw new Exception("Call to "+url+" failed with response code "+response);
	}
}
