package fr.uge.code.camp;

import static org.junit.jupiter.api.Assertions.*;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.List;
import java.util.Set;

import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.io.TempDir;

import fr.uge.code.camp.Session4.Box;

class Session4Test {

	private static Path ensureInProjectOrShares(String file) throws IOException {
		var path = Paths.get(file);
		if (Files.exists(path)) {
			return path;
		}
		// Same relative path, but under folder "/mnt/shares/..." instead of "data"
		var relative = Paths.get("").relativize(path);
		var fallback = Paths.get("/mnt/shares/igm/prof/pivoteau/JCC/Session4").resolve(relative);

		if (!Files.exists(fallback)) {
			throw new NoSuchFileException("Missing file in both locations: " + path + " and " + fallback);
		}
		return fallback;
	}
	@Nested
	@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
	public final class Ex1FallingBricks {

		@Test
		@Order(24)
		void testFallingBricksFileSmall(@TempDir Path tempDir) throws IOException {
			Path dir = ensureInProjectOrShares("data/FallingBricks/Small");
			var files = Files.list(dir).filter(path -> path.toString().endsWith(".data")).sorted().toList();

			for (var dataPath : files) {
				var fileName = dataPath.getFileName().toString();
				if (fileName.startsWith("._")) {
					continue;
				}
				var outPath = dir.resolve(fileName.replace(".data", ".out"));

				int expected = Integer.parseInt(Files.readString(outPath).trim());
				var result = assertTimeoutPreemptively(Duration.ofMillis(1_000), () -> Session4.bricks(dataPath));
				assertEquals(expected, result, "problem with file " + dataPath.getFileName());
			}
		}
		
		@Test
		@Order(25)
		void testFallingBricksFileMedium(@TempDir Path tempDir) throws IOException {
			Path dir = ensureInProjectOrShares("data/FallingBricks/Medium");
			var files = Files.list(dir).filter(path -> path.toString().endsWith(".data")).sorted().toList();

			for (var dataPath : files) {
				var fileName = dataPath.getFileName().toString();
				if (fileName.startsWith("._")) {
					continue;
				}
				var outPath = dir.resolve(fileName.replace(".data", ".out"));

				int expected = Integer.parseInt(Files.readString(outPath).trim());
				var result = assertTimeoutPreemptively(Duration.ofMillis(1_000), () -> Session4.bricks(dataPath));
				assertEquals(expected, result, "problem with file " + dataPath.getFileName());
			}
		}
		
		@Test
		@Order(26)
		void testFallingBricksFileLarge(@TempDir Path tempDir) throws IOException {
			Path dir = ensureInProjectOrShares("data/FallingBricks/Large");
			var files = Files.list(dir).filter(path -> path.toString().endsWith(".data")).sorted().toList();

			for (var dataPath : files) {
				var fileName = dataPath.getFileName().toString();
				if (fileName.startsWith("._")) {
					continue;
				}
				var outPath = dir.resolve(fileName.replace(".data", ".out"));

				int expected = Integer.parseInt(Files.readString(outPath).trim());
				var result = assertTimeoutPreemptively(Duration.ofMillis(5_000), () -> Session4.bricks(dataPath));
				assertEquals(expected, result, "problem with file " + dataPath.getFileName());
			}
		}
	}
	


	@Nested
	@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
	public final class Ex2JunctionBoxes {
		@Test
		@Order(23)
		void testJunctionBoxesFileSmall(@TempDir Path tempDir) throws IOException {
			Path dir = ensureInProjectOrShares("data/JunctionBoxes/Small");
			var files = Files.list(dir).filter(path -> path.toString().endsWith(".data")).sorted().toList();

			for (var dataPath : files) {
				var fileName = dataPath.getFileName().toString();
				if (fileName.startsWith("._")) {
					continue;
				}
				var outPath = dir.resolve(fileName.replace(".data", ".out"));

				try (var readerOut = Files.newBufferedReader(outPath)) {
					var result = Set.of(Box.ofString(readerOut.readLine()), Box.ofString(readerOut.readLine()));
					var boxes = assertTimeoutPreemptively(Duration.ofMillis(10),
							() -> Session4.junctionBoxes(dataPath));
					assertEquals(result, boxes, "problem with file " + dataPath.getFileName());
				}
			}
		}
		
		@Test
		@Order(24)
		void testJunctionBoxesFileMedium(@TempDir Path tempDir) throws IOException {
			Path dir = ensureInProjectOrShares("data/JunctionBoxes/Medium");
			var files = Files.list(dir).filter(path -> path.toString().endsWith(".data")).sorted().toList();

			for (var dataPath : files) {
				var fileName = dataPath.getFileName().toString();
				if (fileName.startsWith("._")) {
					continue;
				}
				var outPath = dir.resolve(fileName.replace(".data", ".out"));

				try (var readerOut = Files.newBufferedReader(outPath)) {
					var result = Set.of(Box.ofString(readerOut.readLine()), Box.ofString(readerOut.readLine()));
					var boxes = assertTimeoutPreemptively(Duration.ofMillis(1_000),
							() -> Session4.junctionBoxes(dataPath));
					assertEquals(result, boxes, "problem with file " + dataPath.getFileName());
				}
			}
		}
		
		@Test
		@Order(25)
		void testJunctionBoxesFileLarge(@TempDir Path tempDir) throws IOException {
			Path dir = ensureInProjectOrShares("data/JunctionBoxes/Large");
			var files = Files.list(dir).filter(path -> path.toString().endsWith(".data")).sorted().toList();

			for (var dataPath : files) {
				var fileName = dataPath.getFileName().toString();
				if (fileName.startsWith("._")) {
					continue;
				}
				var outPath = dir.resolve(fileName.replace(".data", ".out"));

				try (var readerOut = Files.newBufferedReader(outPath)) {
					var result = Set.of(Box.ofString(readerOut.readLine()), Box.ofString(readerOut.readLine()));
					var boxes = assertTimeoutPreemptively(Duration.ofMillis(10_000),
							() -> Session4.junctionBoxes(dataPath));
					assertEquals(result, boxes, "problem with file " + dataPath.getFileName());
				}
			}
		}
	}
}
