package p.packer.services;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import p.packer.messages.PackerProjectContext;
import p.packer.repositories.FileSystemPackerCacheRepository;
import p.packer.repositories.PackerAssetWalker;
import p.packer.repositories.PackerRuntimeAssetMaterializer;
import p.packer.repositories.PackerRuntimeLoader;
import p.packer.testing.PackerFixtureLocator;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.Optional;

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

final class PackerRuntimePatchServiceTest {
    @TempDir
    Path tempDir;

    @Test
    void afterUpdateAssetContractRefreshesOneAssetProjectionAndCacheState() throws Exception {
        final ObjectMapper mapper = new ObjectMapper();
        final Path projectRoot = copyFixture("workspaces/managed-basic", tempDir.resolve("managed"));
        writeTilePng(projectRoot.resolve("assets/ui/atlas/tile.png"));
        final var foundation = new PackerWorkspaceFoundation(mapper);
        final var parser = new PackerAssetDeclarationParser(mapper);
        final var assetWalker = new PackerAssetWalker(mapper);
        final var cacheRepository = new FileSystemPackerCacheRepository(mapper);
        final var assetMaterializer = new PackerRuntimeAssetMaterializer(assetWalker);
        final var loader = new PackerRuntimeLoader(foundation, parser, cacheRepository, assetMaterializer);
        final var patchService = new PackerRuntimePatchService(parser, assetMaterializer);
        final var project = new PackerProjectContext("main", projectRoot);

        final var initialSnapshot = loader.load(project, 1L);
        final var registryEntry = initialSnapshot.registry().assets().getFirst();
        final var updatedSnapshot = patchService.afterUpdateAssetContract(
                initialSnapshot,
                2L,
                projectRoot.resolve("assets/ui/atlas"),
                projectRoot.resolve("assets/ui/atlas/asset.json"),
                Optional.of(registryEntry));

        assertTrue(updatedSnapshot.cacheState().findAsset(registryEntry.assetId()).isPresent());
        assertEquals(1, updatedSnapshot.assets().getFirst().walkProjection().buildCandidateFiles().size());
        assertEquals("tile.png", updatedSnapshot.assets().getFirst().walkProjection().buildCandidateFiles().getFirst().relativePath());
    }

    private Path copyFixture(String relativePath, Path targetRoot) throws Exception {
        final Path sourceRoot = PackerFixtureLocator.fixtureRoot(relativePath);
        try (var stream = Files.walk(sourceRoot)) {
            for (Path source : stream.sorted(Comparator.naturalOrder()).toList()) {
                final Path target = targetRoot.resolve(sourceRoot.relativize(source).toString());
                if (Files.isDirectory(source)) {
                    Files.createDirectories(target);
                } else {
                    Files.createDirectories(target.getParent());
                    Files.copy(source, target);
                }
            }
        }
        return targetRoot;
    }

    private void writeTilePng(Path path) throws Exception {
        Files.createDirectories(path.getParent());
        final BufferedImage image = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);
        for (int y = 0; y < 16; y += 1) {
            for (int x = 0; x < 16; x += 1) {
                image.setRGB(x, y, 0xFFFF0000);
            }
        }
        ImageIO.write(image, "png", path.toFile());
    }
}
