package p.studio.workspaces.assets.metadata;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import p.packer.messages.assets.AssetFamilyCatalog;

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

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

final class AssetStudioMetadataServiceTest {
    @TempDir
    Path tempDir;

    private final AssetStudioMetadataService service = new AssetStudioMetadataService();

    @Test
    void readsGlyphSpecializationFromStudioMetadataFile() throws Exception {
        final Path assetRoot = tempDir.resolve("tileset");
        Files.createDirectories(assetRoot);
        service.writeGlyphSpecialization(assetRoot, AssetStudioGlyphSpecialization.TILESET);

        final AssetStudioMetadataSnapshot snapshot = service.read(assetRoot, AssetFamilyCatalog.GLYPH_BANK);

        assertEquals(AssetStudioGlyphSpecialization.TILESET, snapshot.glyphSpecialization());
        assertNull(snapshot.sceneBankMetadata());
        assertNull(snapshot.glyphMaterialMetadata());
    }

    @Test
    void keepsGlyphBankWithoutStudioMetadataAsNone() throws Exception {
        final Path assetRoot = tempDir.resolve("legacy-glyph");
        Files.createDirectories(assetRoot);

        final AssetStudioMetadataSnapshot snapshot = service.read(assetRoot, AssetFamilyCatalog.GLYPH_BANK);

        assertEquals(AssetStudioGlyphSpecialization.NONE, snapshot.glyphSpecialization());
        assertNull(snapshot.sceneBankMetadata());
        assertNull(snapshot.glyphMaterialMetadata());
    }

    @Test
    void readsAndWritesGlyphMaterialSupportMetadata() throws Exception {
        final Path assetRoot = tempDir.resolve("glyph");
        Files.createDirectories(assetRoot);
        final AssetStudioGlyphMaterialMetadata metadata = new AssetStudioGlyphMaterialMetadata(
                List.of(new AssetStudioGlyphMaterialSchema(
                        "schema-1",
                        AssetStudioGlyphMaterialSchemaStatus.OK,
                        2,
                        Map.of(1, "#8B4513", 2, "#00AA00"),
                        List.of(
                                new AssetStudioGlyphMaterialArtifactBinding("artifact-1", "fp-1"),
                                new AssetStudioGlyphMaterialArtifactBinding("artifact-2", "fp-2")))),
                List.of(new AssetStudioGlyphMaterialPalette(
                        "palette-1",
                        "Forest",
                        "schema-1",
                        2,
                        List.of("#8B4513", "#00AA00"))),
                null);
        service.writeGlyphMaterialMetadata(assetRoot, metadata);

        final AssetStudioMetadataSnapshot snapshot = service.read(assetRoot, AssetFamilyCatalog.GLYPH_BANK);

        assertNotNull(snapshot.glyphMaterialMetadata());
        assertEquals(1, snapshot.glyphMaterialMetadata().schemas().size());
        assertEquals("schema-1", snapshot.glyphMaterialMetadata().schemas().getFirst().schemaId());
        assertEquals("#8B4513", snapshot.glyphMaterialMetadata().schemas().getFirst().materialIndices().get(1));
        assertEquals(1, snapshot.glyphMaterialMetadata().palettes().size());
        assertEquals("Forest", snapshot.glyphMaterialMetadata().palettes().getFirst().displayName());
        assertEquals(List.of("#8B4513", "#00AA00"), snapshot.glyphMaterialMetadata().palettes().getFirst().colors());
    }

    @Test
    void keepsHtmlRgbAsCanonicalGlyphMaterialColorAndDerivesRgb565() {
        final AssetStudioGlyphMaterialPalette palette = new AssetStudioGlyphMaterialPalette(
                "palette-1",
                "schema-1",
                1,
                List.of("#FF0000", "#00FF00", "#0000FF"));

        assertEquals(List.of("#FF0000", "#00FF00", "#0000FF"), palette.colors());
        assertEquals(List.of(0xF800, 0x07E0, 0x001F), palette.convertedRgb565Colors());
    }


    @Test
    void readsSceneBankSupportMetadataWhenContractIsValid() throws Exception {
        final Path assetRoot = tempDir.resolve("scene");
        Files.createDirectories(assetRoot);
        Files.writeString(assetRoot.resolve(AssetStudioMetadataService.SCENE_BANK_SUPPORT_FILE), """
                {
                  "schema_version": 1,
                  "map_width": 20,
                  "map_height": 12,
                  "layer_count": 4,
                  "layers": [
                    { "index": 1, "enabled": true, "name": "Ground", "width": 20, "height": 12, "tilemap": "ground.tmx", "tileset_asset_root": "tilesets/ground", "parallax_factor_x": 1.0, "parallax_factor_y": 1.0, "default_palette_id": 2 },
                    { "index": 2, "enabled": true, "name": "Collision", "width": 10, "height": 8, "tilemap": "collision.tmx", "tileset_asset_root": "tilesets/ground", "parallax_factor_x": 0.5, "parallax_factor_y": 0.75, "default_palette_id": 3 },
                    { "index": 3, "enabled": false, "name": "Layer 3", "width": 20, "height": 12, "tilemap": "layer-3.tmx", "tileset_asset_root": "", "default_palette_id": 0 },
                    { "index": 4, "enabled": false, "name": "Layer 4", "width": 20, "height": 12, "tilemap": "layer-4.tmx", "tileset_asset_root": "", "default_palette_id": 0 }
                  ]
                }
                """);

        final AssetStudioMetadataSnapshot snapshot = service.read(assetRoot, AssetFamilyCatalog.SCENE_BANK);

        assertNotNull(snapshot.sceneBankMetadata());
        assertEquals(20, snapshot.sceneBankMetadata().mapWidth());
        assertEquals(12, snapshot.sceneBankMetadata().mapHeight());
        assertEquals(4, snapshot.sceneBankMetadata().layerCount());
        assertEquals(10, snapshot.sceneBankMetadata().layerBindings().get(1).width());
        assertEquals(8, snapshot.sceneBankMetadata().layerBindings().get(1).height());
        assertEquals(0.5f, snapshot.sceneBankMetadata().layerBindings().get(1).parallaxFactorX());
        assertEquals(0.75f, snapshot.sceneBankMetadata().layerBindings().get(1).parallaxFactorY());
        assertEquals(3, snapshot.sceneBankMetadata().layerBindings().get(1).defaultPaletteId());
        assertEquals(20 * 12, snapshot.sceneBankMetadata().layerBindings().getFirst().tiles().size());
        assertFalse(snapshot.sceneBankMetadata().layerBindings().getFirst().tiles().getFirst().active());
        assertEquals(
                java.util.List.of("ground.tmx", "collision.tmx"),
                snapshot.sceneBankMetadata().layerBindings().stream()
                        .filter(AssetStudioSceneLayerBinding::enabled)
                        .map(AssetStudioSceneLayerBinding::tilemap)
                        .toList());
    }

    @Test
    void rejectsSceneBankSupportMetadataWhenLayerCountIsNotFixedAtFour() throws Exception {
        final Path assetRoot = tempDir.resolve("scene-invalid");
        Files.createDirectories(assetRoot);
        Files.writeString(assetRoot.resolve(AssetStudioMetadataService.SCENE_BANK_SUPPORT_FILE), """
                {
                  "schema_version": 1,
                  "map_width": 16,
                  "map_height": 16,
                  "layer_count": 3,
                  "layers": [
                    { "index": 1, "enabled": true, "name": "A", "width": 16, "height": 16, "tilemap": "a.tmx", "tileset_asset_root": "", "default_palette_id": 0 },
                    { "index": 2, "enabled": false, "name": "B", "width": 16, "height": 16, "tilemap": "b.tmx", "tileset_asset_root": "", "default_palette_id": 0 },
                    { "index": 3, "enabled": false, "name": "C", "width": 16, "height": 16, "tilemap": "c.tmx", "tileset_asset_root": "", "default_palette_id": 0 }
                  ]
                }
                """);

        final AssetStudioMetadataSnapshot snapshot = service.read(assetRoot, AssetFamilyCatalog.SCENE_BANK);

        assertNull(snapshot.sceneBankMetadata());
    }

    @Test
    void writesSceneBankSupportMetadataWithCanonicalLayerFields() throws Exception {
        final Path assetRoot = tempDir.resolve("scene-written");
        Files.createDirectories(assetRoot);
        final AssetStudioSceneBankMetadata metadata = new AssetStudioSceneBankMetadata(
                16,
                12,
                4,
                List.of(
                        new AssetStudioSceneLayerBinding(true, 1, "Ground", 16, 12, "ground.tmx", "tilesets/ground", 1.0f, 1.0f, 2),
                        new AssetStudioSceneLayerBinding(false, 2, "Layer 2", 16, 12, "layer-2.tmx", "", 1.0f, 1.0f, 0),
                        new AssetStudioSceneLayerBinding(false, 3, "Layer 3", 16, 12, "layer-3.tmx", "", 1.0f, 1.0f, 0),
                        new AssetStudioSceneLayerBinding(false, 4, "Layer 4", 16, 12, "layer-4.tmx", "", 1.0f, 1.0f, 0)),
                assetRoot.resolve(AssetStudioMetadataService.SCENE_BANK_SUPPORT_FILE));

        service.writeSceneBankMetadata(assetRoot, metadata);

        final String json = Files.readString(assetRoot.resolve(AssetStudioMetadataService.SCENE_BANK_SUPPORT_FILE));
        assertTrue(json.contains("\"parallax_factor_x\""));
        assertTrue(json.contains("\"parallax_factor_y\""));
        assertTrue(json.contains("\"default_palette_id\""));
    }

    @Test
    void writesSceneBankSupportMetadataWithCanonicalTilesWhenPresent() throws Exception {
        final Path assetRoot = tempDir.resolve("scene-tiles");
        Files.createDirectories(assetRoot);
        final AssetStudioSceneBankMetadata metadata = new AssetStudioSceneBankMetadata(
                2,
                2,
                4,
                List.of(
                        new AssetStudioSceneLayerBinding(
                                true,
                                1,
                                "Ground",
                                2,
                                2,
                                "ground.tmx",
                                "tilesets/ground",
                                1.0f,
                                1.0f,
                                2,
                                List.of(
                                        new AssetStudioSceneTile(true, 7, 2, false, false),
                                        AssetStudioSceneTile.inactive(2),
                                        AssetStudioSceneTile.inactive(2),
                                        AssetStudioSceneTile.inactive(2))),
                        new AssetStudioSceneLayerBinding(false, 2, "Layer 2", 2, 2, "layer-2.tmx", "", 1.0f, 1.0f, 0),
                        new AssetStudioSceneLayerBinding(false, 3, "Layer 3", 2, 2, "layer-3.tmx", "", 1.0f, 1.0f, 0),
                        new AssetStudioSceneLayerBinding(false, 4, "Layer 4", 2, 2, "layer-4.tmx", "", 1.0f, 1.0f, 0)),
                assetRoot.resolve(AssetStudioMetadataService.SCENE_BANK_SUPPORT_FILE));

        service.writeSceneBankMetadata(assetRoot, metadata);

        final String json = Files.readString(assetRoot.resolve(AssetStudioMetadataService.SCENE_BANK_SUPPORT_FILE));
        assertTrue(json.contains("\"tiles\""));
        assertTrue(json.contains("\"glyph_id\""));
    }

    @Test
    void providesDefaultSceneBankMetadataWhenSupportFileIsMissing() throws Exception {
        final Path assetRoot = tempDir.resolve("scene-missing");
        Files.createDirectories(assetRoot);

        final AssetStudioMetadataSnapshot snapshot = service.read(assetRoot, AssetFamilyCatalog.SCENE_BANK);

        assertNotNull(snapshot.sceneBankMetadata());
        assertEquals(16, snapshot.sceneBankMetadata().mapWidth());
        assertEquals("scene.tmx", snapshot.sceneBankMetadata().layerBindings().getFirst().tilemap());
    }
}
