package p.studio.workspaces.editor;

import p.studio.compiler.FrontendRegistryService;
import p.studio.projects.ProjectReference;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream;

public final class EditorProjectSnapshotService {

    public EditorProjectSnapshot createSnapshot(final ProjectReference projectReference) {
        final var project = Objects.requireNonNull(projectReference, "projectReference");
        final var projectRoot = project.rootPath().toAbsolutePath().normalize();
        final var taggedRoots = taggedSourceRoots(project);
        final var rootNode = buildNode(projectRoot, project.name(), taggedRoots);
        return new EditorProjectSnapshot(projectRoot, rootNode);
    }

    private Set<Path> taggedSourceRoots(final ProjectReference projectReference) {
        return FrontendRegistryService.getFrontendSpec(projectReference.languageId())
                .stream()
                .flatMap(frontendSpec -> frontendSpec.getSourceRoots().stream())
                .map(projectReference.rootPath()::resolve)
                .map(Path::toAbsolutePath)
                .map(Path::normalize)
                .collect(java.util.stream.Collectors.toSet());
    }

    private EditorProjectNode buildNode(
            final Path path,
            final String displayName,
            final Set<Path> taggedRoots) {
        if (!Files.isDirectory(path)) {
            return new EditorProjectNode(path, displayName, false, taggedRoots.contains(path), List.of());
        }

        final List<EditorProjectNode> children;
        try (Stream<Path> stream = Files.list(path)) {
            children = stream
                    .sorted(nodeComparator())
                    .map(child -> buildNode(child, child.getFileName().toString(), taggedRoots))
                    .toList();
        } catch (IOException ioException) {
            throw new UncheckedIOException(ioException);
        }

        return new EditorProjectNode(path, displayName, true, taggedRoots.contains(path), children);
    }

    private Comparator<Path> nodeComparator() {
        return Comparator
                .comparing((Path path) -> !Files.isDirectory(path))
                .thenComparing(path -> path.getFileName().toString(), String.CASE_INSENSITIVE_ORDER);
    }
}
