package p.packer.services;

import p.packer.messages.InitWorkspaceRequest;
import p.packer.messages.PackerProjectContext;
import p.packer.models.PackerRegistryEntry;
import p.packer.models.PackerRegistryState;
import p.packer.models.PackerRuntimeAsset;
import p.packer.models.PackerRuntimeSnapshot;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.stream.Collectors;

public final class PackerRuntimeLoader implements PackerRuntimeSnapshotLoader {
    private final PackerWorkspaceFoundation workspaceFoundation;
    private final PackerAssetDeclarationParser parser;

    public PackerRuntimeLoader(
            PackerWorkspaceFoundation workspaceFoundation,
            PackerAssetDeclarationParser parser) {
        this.workspaceFoundation = Objects.requireNonNull(workspaceFoundation, "workspaceFoundation");
        this.parser = Objects.requireNonNull(parser, "parser");
    }

    private boolean isAssetJson(Path path, BasicFileAttributes attrs) {
        return attrs.isRegularFile() && path.getFileName().toString().equals("asset.json");
    }

    @Override
    public PackerRuntimeSnapshot load(PackerProjectContext project, long generation) {
        final PackerProjectContext safeProject = Objects.requireNonNull(project, "project");
        workspaceFoundation.initWorkspace(new InitWorkspaceRequest(safeProject));

        final PackerRegistryState registry = workspaceFoundation.loadRegistry(safeProject);
        final Map<Path, PackerRegistryEntry> registryByRoot = registry.assets().stream()
                .collect(Collectors.toMap(
                        entry -> PackerWorkspacePaths.assetRoot(safeProject, entry.root()),
                        entry -> entry));

        final List<PackerRuntimeAsset> assets = new ArrayList<>();
        final var assetsRoot = PackerWorkspacePaths.assetsRoot(safeProject);
        if (Files.isDirectory(assetsRoot)) {
            try (final var paths = Files.find(assetsRoot, Integer.MAX_VALUE, this::isAssetJson)) {
                final var manifests = paths
                        .map(path -> path.toAbsolutePath().normalize())
                        .sorted(Comparator.naturalOrder())
                        .toList();
                for (final var manifestPath : manifests) {
                    final var assetRoot = manifestPath.getParent();
                    final var registryEntry = Optional.ofNullable(registryByRoot.get(assetRoot));
                    assets.add(new PackerRuntimeAsset(
                            assetRoot,
                            manifestPath,
                            registryEntry,
                            parser.parse(manifestPath)));
                }
            } catch (IOException exception) {
                throw new p.packer.exceptions.PackerRegistryException(
                        "Unable to build runtime snapshot for " + safeProject.projectId(),
                        exception);
            }
        }

        return new PackerRuntimeSnapshot(generation, registry, assets);
    }
}
