package p.studio.workspaces.assets.details.bank;

import org.junit.jupiter.api.Test;
import p.packer.dtos.PackerCodecConfigurationFieldDTO;
import p.packer.messages.AssetReference;
import p.packer.messages.assets.AssetFamilyCatalog;
import p.packer.messages.assets.OutputCodecCatalog;
import p.packer.messages.assets.OutputFormatCatalog;
import p.packer.messages.assets.PackerCodecConfigurationFieldType;
import p.studio.workspaces.assets.metadata.AssetStudioGlyphSpecialization;
import p.studio.workspaces.assets.messages.*;

import java.nio.file.Path;
import java.util.List;
import java.util.Map;

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

final class AssetDetailsBankCompositionCoordinatorTest {
    @Test
    void tileFamilyBlocksSelectionAfterSlotCapacity() {
        final AssetDetailsBankCompositionCoordinator coordinator = new AssetDetailsBankCompositionCoordinator();
        coordinator.replaceDetails(tileDetails("32x32", 65));
        coordinator.beginEdit();

        coordinator.moveToSelected(coordinator.viewModel().availableFiles());

        assertEquals(64, coordinator.viewModel().selectedFiles().size());
        assertTrue(coordinator.viewModel().capacityState().blocked());
    }

    @Test
    void tileFamilyUsesAtlasCapacityFor8x8Tiles() {
        final AssetDetailsBankCompositionCoordinator coordinator = new AssetDetailsBankCompositionCoordinator();
        coordinator.replaceDetails(tileDetails("8x8", 1_100));
        coordinator.beginEdit();

        coordinator.moveToSelected(coordinator.viewModel().availableFiles());

        assertEquals(1_024, coordinator.viewModel().selectedFiles().size());
        assertEquals("1024 / 1024", coordinator.viewModel().capacityState().labelText());
        assertTrue(coordinator.viewModel().capacityState().blocked());
    }

    @Test
    void tileFamilyUsesAtlasCapacityFor16x16Tiles() {
        final AssetDetailsBankCompositionCoordinator coordinator = new AssetDetailsBankCompositionCoordinator();
        coordinator.replaceDetails(tileDetails("16x16", 300));
        coordinator.beginEdit();

        coordinator.moveToSelected(coordinator.viewModel().availableFiles());

        assertEquals(256, coordinator.viewModel().selectedFiles().size());
        assertEquals("256 / 256", coordinator.viewModel().capacityState().labelText());
        assertTrue(coordinator.viewModel().capacityState().blocked());
    }

    @Test
    void tileFamilyUsesAtlasCapacityFor32x32Tiles() {
        final AssetDetailsBankCompositionCoordinator coordinator = new AssetDetailsBankCompositionCoordinator();
        coordinator.replaceDetails(tileDetails("32x32", 100));
        coordinator.beginEdit();

        coordinator.moveToSelected(coordinator.viewModel().availableFiles());

        assertEquals(64, coordinator.viewModel().selectedFiles().size());
        assertEquals("64 / 64", coordinator.viewModel().capacityState().labelText());
        assertTrue(coordinator.viewModel().capacityState().blocked());
    }

    @Test
    void soundFamilyBlocksSelectionAfterByteCapacity() {
        final AssetDetailsBankCompositionCoordinator coordinator = new AssetDetailsBankCompositionCoordinator();
        coordinator.replaceDetails(soundDetails(600_000L, 600_000L));
        coordinator.beginEdit();

        coordinator.moveToSelected(coordinator.viewModel().availableFiles());

        assertEquals(1, coordinator.viewModel().selectedFiles().size());
        assertTrue(coordinator.viewModel().capacityState().progress() > 0.5d);
    }

    @Test
    void resetAndCancelFollowStudioFormSessionLifecycle() {
        final AssetDetailsBankCompositionCoordinator coordinator = new AssetDetailsBankCompositionCoordinator();
        coordinator.replaceDetails(tileDetails("16x16", 2));
        coordinator.beginEdit();

        final var firstFile = coordinator.viewModel().availableFiles().subList(0, 1);
        coordinator.moveToSelected(firstFile);
        assertTrue(coordinator.viewModel().dirty());

        coordinator.reset();
        assertFalse(coordinator.viewModel().dirty());

        coordinator.moveToSelected(firstFile);
        assertTrue(coordinator.viewModel().dirty());
        coordinator.cancel();
        assertFalse(coordinator.viewModel().editing());
        assertFalse(coordinator.viewModel().dirty());
    }

    private AssetWorkspaceAssetDetails tileDetails(String tileSize, int fileCount) {
        return new AssetWorkspaceAssetDetails(
                summary(AssetFamilyCatalog.GLYPH_BANK),
                List.of(),
                OutputFormatCatalog.GLYPH_INDEXED_V1,
                OutputCodecCatalog.NONE,
                List.of(OutputCodecCatalog.NONE),
                Map.of(OutputCodecCatalog.NONE, List.of()),
                List.of(new PackerCodecConfigurationFieldDTO("tile_size", "Tile Size", PackerCodecConfigurationFieldType.TEXT, tileSize, true, List.of())),
                Map.of(),
                new AssetWorkspaceBankCompositionDetails(
                        files("tile", fileCount, 1024L),
                        List.of(),
                        0L),
                List.of(),
                null,
                null,
                AssetWorkspaceSceneBankValidation.NOT_APPLICABLE,
                List.of());
    }

    private AssetWorkspaceAssetDetails soundDetails(long firstSize, long secondSize) {
        return new AssetWorkspaceAssetDetails(
                summary(AssetFamilyCatalog.SOUND_BANK),
                List.of(),
                OutputFormatCatalog.SOUND_V1,
                OutputCodecCatalog.NONE,
                List.of(OutputCodecCatalog.NONE),
                Map.of(OutputCodecCatalog.NONE, List.of()),
                List.of(),
                Map.of("samples", Map.of(
                        "1", Map.of("offset", 0L, "length", firstSize),
                        "2", Map.of("offset", firstSize, "length", secondSize))),
                new AssetWorkspaceBankCompositionDetails(
                        List.of(
                                new AssetWorkspaceBankCompositionFile("a.wav", "a.wav", firstSize, 1L, null, Map.of()),
                                new AssetWorkspaceBankCompositionFile("b.wav", "b.wav", secondSize, 1L, null, Map.of())),
                        List.of(),
                        0L),
                List.of(),
                null,
                null,
                AssetWorkspaceSceneBankValidation.NOT_APPLICABLE,
                List.of());
    }

    private List<AssetWorkspaceBankCompositionFile> files(String prefix, int count, long size) {
        return java.util.stream.IntStream.range(0, count)
                .mapToObj(index -> new AssetWorkspaceBankCompositionFile(
                        prefix + "-" + index + ".png",
                        prefix + "-" + index + ".png",
                        size,
                        1L,
                        null,
                        Map.of()))
                .toList();
    }

    private AssetWorkspaceAssetSummary summary(AssetFamilyCatalog family) {
        return new AssetWorkspaceAssetSummary(
                AssetReference.forAssetId(1),
                "bank",
                AssetWorkspaceAssetState.REGISTERED,
                AssetWorkspaceBuildParticipation.INCLUDED,
                1,
                family,
                AssetStudioGlyphSpecialization.NONE,
                Path.of("/tmp/bank"),
                false,
                false);
    }
}
