package p.studio.compiler.utilities;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@FunctionalInterface
public interface SourceProviderFactory {
    SourceProvider create(Path path);

    static SourceProviderFactory filesystem() {
        return path -> new SourceProvider() {

            private volatile byte[] cachedBytes = null;

            @Override
            public byte[] read() throws IOException {
                if (Objects.isNull(cachedBytes)) {
                    cachedBytes = Files.readAllBytes(path);
                }
                return cachedBytes;
            }
        };
    }

    static SourceProviderFactory overlayUtf8(final Map<Path, String> overrides) {
        final Map<Path, byte[]> bytesByPath = new HashMap<>();
        for (final var entry : Objects.requireNonNull(overrides, "overrides").entrySet()) {
            bytesByPath.put(
                    normalize(entry.getKey()),
                    Objects.requireNonNull(entry.getValue(), "override text").getBytes(StandardCharsets.UTF_8));
        }
        return overlay(bytesByPath, filesystem());
    }

    static SourceProviderFactory overlay(
            final Map<Path, byte[]> overrides,
            final SourceProviderFactory fallback) {
        final Map<Path, byte[]> normalizedOverrides = new HashMap<>();
        for (final var entry : Objects.requireNonNull(overrides, "overrides").entrySet()) {
            normalizedOverrides.put(
                    normalize(entry.getKey()),
                    Objects.requireNonNull(entry.getValue(), "override bytes").clone());
        }
        final SourceProviderFactory safeFallback = Objects.requireNonNull(fallback, "fallback");
        return path -> {
            final Path normalizedPath = normalize(path);
            final byte[] override = normalizedOverrides.get(normalizedPath);
            if (override != null) {
                final byte[] immutableBytes = override.clone();
                return () -> immutableBytes;
            }
            return safeFallback.create(normalizedPath);
        };
    }

    private static Path normalize(final Path path) {
        return Objects.requireNonNull(path, "path").toAbsolutePath().normalize();
    }
}
