package be.jeffcheasey88.peeratcode.routes;

import static be.jeffcheasey88.peeratcode.framework.RequestType.POST;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;

import org.json.simple.JSONObject;

import be.jeffcheasey88.peeratcode.bonus.extract.RouteDoc;
import be.jeffcheasey88.peeratcode.framework.HttpReader;
import be.jeffcheasey88.peeratcode.framework.HttpWriter;
import be.jeffcheasey88.peeratcode.framework.Locker;
import be.jeffcheasey88.peeratcode.framework.Response;
import be.jeffcheasey88.peeratcode.framework.Route;
import be.jeffcheasey88.peeratcode.framework.User;
import be.jeffcheasey88.peeratcode.model.Chapter;
import be.jeffcheasey88.peeratcode.model.Completion;
import be.jeffcheasey88.peeratcode.model.Group;
import be.jeffcheasey88.peeratcode.model.Player;
import be.jeffcheasey88.peeratcode.model.Puzzle;
import be.jeffcheasey88.peeratcode.repository.DatabaseRepository;

public class PuzzleResponse implements Response {
	private final DatabaseRepository databaseRepo;
	private final String usersFilesPath;
	
	private final Locker<Completion> leaderboard;
	
	public PuzzleResponse(DatabaseRepository databaseRepo, String initUsersFilesPath, Locker<Completion> locker){
		this.databaseRepo = databaseRepo;
		usersFilesPath = initUsersFilesPath;
		this.leaderboard = locker;
	}
	
	@RouteDoc(path = "/puzzleResponse/<id>", responseCode = 200, responseDescription = "Bonne réponse, json contenant les points + tries")
	@RouteDoc(responseCode = 400, responseDescription = "Pas de réponse")
	@RouteDoc(responseCode = 403, responseDescription = "Déjà répondu")
	@RouteDoc(responseCode = 406, responseDescription = "Mauvaise réponse")
	@RouteDoc(responseCode = 423, responseDescription = "L'utilisateur essaye de répondre en dehors de l'event")

	@Route(path = "^\\/puzzleResponse\\/([0-9]+)$", type = POST, needLogin = true)
	public void exec(Matcher matcher, User user, HttpReader reader, HttpWriter writer) throws Exception{
		ReceivedResponse received = new ReceivedResponse(matcher, reader);
		if (received.getResponse() == null){
			writer.response(400, "Access-Control-Allow-Origin: *");
			return;
		}
		//saveSourceCode(received, databaseRepo.getPlayer(user.getId()));
		JSONObject responseJSON = new JSONObject();
		if(this.databaseRepo.getScore(user.getId(), received.getPuzzleId()) > 0){
			writer.response(403, "Access-Control-Allow-Origin: *");
			return;
		}

		Puzzle currentPuzzle = databaseRepo.getPuzzle(received.getPuzzleId());
		Chapter chapter = this.databaseRepo.getChapter(currentPuzzle);
		if(chapter.getStartDate() != null){
			if(LocalDateTime.now().isBefore(chapter.getStartDate().toLocalDateTime())){
				writer.response(423, "Access-Control-Allow-Origin: *");
				return;
			}
		}
		if(chapter.getEndDate() != null){
			if(LocalDateTime.now().isAfter(chapter.getEndDate().toLocalDateTime())){
				if(Arrays.equals(currentPuzzle.getSoluce(), received.getResponse())){
					writer.response(200, "Access-Control-Allow-Origin: *", "Content-Type: application/json");
					JSONObject theoSaisPasJusteRecevoir200 = new JSONObject();
					theoSaisPasJusteRecevoir200.put("success", true);
					writer.write(theoSaisPasJusteRecevoir200.toJSONString());
					return;
				}
				writer.response(423, "Access-Control-Allow-Origin: *");
				return;
			}
			Group group = this.databaseRepo.getPlayerGroup(user.getId(), chapter.getId());
			if(group == null){
				writer.response(423, "Access-Control-Allow-Origin: *");
				return;
			}
		}
		
		
		Completion completion = databaseRepo.insertOrUpdatePuzzleResponse(received.getPuzzleId(), user.getId(),
				received.getFileName(), received.getSourceCode(), received.getResponse(), currentPuzzle);
		if(completion == null){
			writer.response(400, "Access-Control-Allow-Origin: *");
			return;
		}
		if(completion.getScore() > 0){
			writer.response(200, "Access-Control-Allow-Origin: *", "Content-Type: application/json");
			responseJSON.put("score", completion.getScore());
			responseJSON.put("tries", completion.getTries());

		}else{
			writer.response(406, "Access-Control-Allow-Origin: *", "Content-Type: application/json");
			responseJSON.put("tries", completion.getTries());
		}
		writer.write(responseJSON.toJSONString());
		writer.flush();
		
		leaderboard.setValue(completion);
	}

	private void saveSourceCode(ReceivedResponse received, Player player){
		try{
			Path path = Paths.get(String.format("%s/%s/puz%04d-%s", usersFilesPath, player.getPseudo(),
					received.getPuzzleId(), received.getFileName()));
			Files.write(path, received.getSourceCode());
		}catch (IOException e){
			System.err.println("ERREUR D'ENREGISTREMENT DU CODE POUR PUZZLE_RESPONSE : " + e.toString());
		}

	}

}

class ReceivedResponse {

	private int puzzleId;
	private byte[] response;
	private String fileName;
	private byte[] sourceCode;

	public ReceivedResponse(Matcher matcher, HttpReader reader) throws Exception {
		puzzleId = Integer.parseInt(matcher.group(1));
		
		List<String> multiPartData = reader.readMultiPartData();
		if (multiPartData != null && multiPartData.size() > 0) {
			this.response = multiPartData.get(0).getBytes();
			if (multiPartData.size() == 3) {
				this.fileName = multiPartData.get(1);
				this.sourceCode = multiPartData.get(2).getBytes();
			}
		}
	}

	public int getPuzzleId() {
		return puzzleId;
	}

	public byte[] getResponse() {
		return response;
	}

	public String getFileName() {
		return fileName;
	}

	public byte[] getSourceCode() {
		return sourceCode;
	}
}
