package p.studio.compiler.workspaces;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import p.studio.compiler.messages.Addressable;
import p.studio.compiler.messages.FESurfaceContext;
import p.studio.utilities.structures.ReadOnlyList;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public final class AssetSurfaceContextLoader {
    private static final Path REGISTRY_PATH = Path.of("assets", ".prometeu", "index.json");

    private final ObjectMapper mapper;

    public AssetSurfaceContextLoader() {
        this(new ObjectMapper());
    }

    AssetSurfaceContextLoader(final ObjectMapper mapper) {
        this.mapper = Objects.requireNonNull(mapper, "mapper");
    }

    public FESurfaceContext load(final Path projectRoot) {
        final var registryPath = Objects.requireNonNull(projectRoot, "projectRoot")
                .toAbsolutePath()
                .normalize()
                .resolve(REGISTRY_PATH);
        if (!Files.isRegularFile(registryPath)) {
            return FESurfaceContext.empty();
        }
        try {
            final var document = mapper.readValue(registryPath.toFile(), RegistryDocument.class);
            final var addressables = new ArrayList<Addressable>();
            if (document.assets != null) {
                for (final var asset : document.assets) {
                    if (asset == null || !isIncludedInBuild(asset)) {
                        continue;
                    }
                    addressables.add(new Addressable(toAddress(asset.root()), asset.assetId()));
                }
            }
            return new FESurfaceContext(ReadOnlyList.wrap(addressables));
        } catch (IOException exception) {
            throw new IllegalStateException("Unable to load asset surface registry: " + registryPath, exception);
        }
    }

    private boolean isIncludedInBuild(final RegistryAssetDocument asset) {
        return asset.includedInBuild() == null || asset.includedInBuild();
    }

    private String toAddress(final String root) {
        final var normalizedRoot = normalizeRoot(root);
        if (normalizedRoot.isBlank()) {
            throw new IllegalStateException("Asset root must not be blank in surface registry");
        }
        return "assets." + normalizedRoot.replace('/', '.');
    }

    private String normalizeRoot(final String root) {
        if (root == null) {
            return "";
        }
        final var trimmed = root.trim().replace('\\', '/');
        final List<String> segments = new ArrayList<>();
        for (final var rawSegment : trimmed.split("/")) {
            final var segment = rawSegment.trim();
            if (segment.isEmpty() || ".".equals(segment)) {
                continue;
            }
            if ("..".equals(segment)) {
                throw new IllegalStateException("Asset root escapes trusted boundary: " + root);
            }
            segments.add(segment);
        }
        return String.join("/", segments);
    }

    @JsonIgnoreProperties(ignoreUnknown = true)
    private record RegistryDocument(
            @JsonProperty("assets") List<RegistryAssetDocument> assets) {
    }

    @JsonIgnoreProperties(ignoreUnknown = true)
    private record RegistryAssetDocument(
            @JsonProperty("asset_id") int assetId,
            @JsonProperty("root") String root,
            @JsonProperty("included_in_build") Boolean includedInBuild) {
    }
}
