package p.packer.services;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import p.packer.messages.assets.AssetFamilyCatalog;
import p.packer.messages.assets.OutputCodecCatalog;
import p.packer.messages.diagnostics.PackerDiagnosticCategory;
import p.packer.testing.PackerFixtureLocator;

import java.nio.file.Files;
import java.nio.file.Path;

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

final class PackerAssetDeclarationParserTest {
    private final PackerAssetDeclarationParser parser = new PackerAssetDeclarationParser();

    @TempDir
    Path tempDir;

    @Test
    void parsesValidDeclarationFixture() {
        final var result = parser.parse(PackerFixtureLocator.fixtureRoot("workspaces/managed-basic/assets/ui/atlas/asset.json"));

        assertTrue(result.valid());
        assertNotNull(result.declaration());
        assertEquals(1, result.declaration().schemaVersion());
        assertEquals("fixture-uuid-1", result.declaration().assetUuid());
        assertEquals("ui_atlas", result.declaration().name());
        assertEquals(AssetFamilyCatalog.IMAGE_BANK, result.declaration().assetFamily());
        assertEquals("TILES/indexed_v1", result.declaration().outputFormat());
        assertEquals(OutputCodecCatalog.NONE, result.declaration().outputCodec());
        assertTrue(result.declaration().preloadEnabled());
    }

    @Test
    void rejectsMalformedJsonWithStructuralDiagnostic() {
        final var result = parser.parse(PackerFixtureLocator.fixtureRoot("workspaces/invalid-malformed/assets/bad/asset.json"));

        assertFalse(result.valid());
        assertNull(result.declaration());
        assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.category() == PackerDiagnosticCategory.STRUCTURAL));
    }

    @Test
    void rejectsMissingRequiredFields() {
        final var result = parser.parse(PackerFixtureLocator.fixtureRoot("workspaces/invalid-missing-fields/assets/bad/asset.json"));

        assertFalse(result.valid());
        assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("name")));
        assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("asset_uuid")));
        assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("format")));
    }

    @Test
    void rejectsUnsupportedSchemaVersion() {
        final var result = parser.parse(PackerFixtureLocator.fixtureRoot("workspaces/invalid-version/assets/bad/asset.json"));

        assertFalse(result.valid());
        assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.category() == PackerDiagnosticCategory.VERSIONING));
    }

    @Test
    void rejectsUntrustedInputPaths() throws Exception {
        final Path manifest = tempDir.resolve("asset.json");
        Files.writeString(manifest, """
                {
                  "schema_version": 1,
                  "asset_uuid": "uuid-outside",
                  "name": "bad_asset",
                  "type": "image_bank",
                  "inputs": { "sprites": ["../outside.png"] },
                  "output": { "format": "TILES/indexed_v1", "codec": "NONE" },
                  "preload": { "enabled": true }
                }
                """);

        final var result = parser.parse(manifest);

        assertFalse(result.valid());
        assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("untrusted path")));
    }

    @Test
    void rejectsUnknownAssetFamily() throws Exception {
        final Path manifest = tempDir.resolve("asset.json");
        Files.writeString(manifest, """
                {
                  "schema_version": 1,
                  "asset_uuid": "uuid-video",
                  "name": "bad_asset",
                  "type": "video_bank",
                  "inputs": { "sprites": ["atlas.png"] },
                  "output": { "format": "TILES/indexed_v1", "codec": "NONE" },
                  "preload": { "enabled": true }
                }
                """);

        final var result = parser.parse(manifest);

        assertFalse(result.valid());
        assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("Field 'type' must be one of")));
    }
}
