package p.packer.services;

import p.packer.models.*;

import java.nio.file.Path;
import java.util.*;

public final class PackerRuntimePatchService {
    private final PackerAssetDeclarationParser declarationParser;

    public PackerRuntimePatchService(PackerAssetDeclarationParser declarationParser) {
        this.declarationParser = Objects.requireNonNull(declarationParser, "declarationParser");
    }

    public PackerRuntimeSnapshot afterCreateAsset(
            PackerRuntimeSnapshot snapshot,
            long generation,
            PackerRegistryState updatedRegistry,
            PackerRegistryEntry entry,
            Path assetRoot,
            Path manifestPath) {
        final PackerAssetDeclarationParseResult parsed = declarationParser.parse(manifestPath);
        final List<PackerRuntimeAsset> updatedAssets = new ArrayList<>(snapshot.assets().stream()
                .filter(candidate -> !candidate.assetRoot().equals(assetRoot.toAbsolutePath().normalize()))
                .toList());
        updatedAssets.add(new PackerRuntimeAsset(
                assetRoot,
                manifestPath,
                Optional.of(entry),
                parsed));
        updatedAssets.sort(Comparator.comparing(asset -> asset.assetRoot().toString(), String.CASE_INSENSITIVE_ORDER));
        return new PackerRuntimeSnapshot(generation, updatedRegistry, updatedAssets);
    }

    public PackerRuntimeSnapshot afterRegisterAsset(
            PackerRuntimeSnapshot snapshot,
            long generation,
            PackerRegistryState updatedRegistry,
            PackerRegistryEntry entry,
            Path assetRoot) {
        final List<PackerRuntimeAsset> updatedAssets = new ArrayList<>();
        boolean patched = false;
        for (PackerRuntimeAsset asset : snapshot.assets()) {
            if (asset.assetRoot().equals(assetRoot.toAbsolutePath().normalize())) {
                updatedAssets.add(new PackerRuntimeAsset(
                        asset.assetRoot(),
                        asset.manifestPath(),
                        Optional.of(entry),
                        asset.parsedDeclaration()));
                patched = true;
            } else {
                updatedAssets.add(asset);
            }
        }
        if (!patched) {
            throw new IllegalStateException("Unable to patch runtime snapshot for unregistered asset: " + assetRoot);
        }
        return new PackerRuntimeSnapshot(generation, updatedRegistry, updatedAssets);
    }

    public PackerRuntimeSnapshot afterDeleteAsset(
            PackerRuntimeSnapshot snapshot,
            long generation,
            PackerRegistryState updatedRegistry,
            Path assetRoot) {
        final List<PackerRuntimeAsset> updatedAssets = snapshot.assets().stream()
                .filter(asset -> !asset.assetRoot().equals(assetRoot.toAbsolutePath().normalize()))
                .toList();
        return new PackerRuntimeSnapshot(generation, updatedRegistry, updatedAssets);
    }

    public PackerRuntimeSnapshot afterMoveAsset(
            PackerRuntimeSnapshot snapshot,
            long generation,
            PackerRegistryState updatedRegistry,
            Optional<PackerRegistryEntry> updatedRegistryEntry,
            Path sourceRoot,
            Path targetRoot,
            Path targetManifestPath) {
        final PackerAssetDeclarationParseResult parsed = declarationParser.parse(targetManifestPath);
        final List<PackerRuntimeAsset> updatedAssets = new ArrayList<>();
        boolean patched = false;
        for (PackerRuntimeAsset asset : snapshot.assets()) {
            if (asset.assetRoot().equals(sourceRoot.toAbsolutePath().normalize())) {
                updatedAssets.add(new PackerRuntimeAsset(
                        targetRoot,
                        targetManifestPath,
                        updatedRegistryEntry,
                        parsed));
                patched = true;
            } else {
                updatedAssets.add(asset);
            }
        }
        if (!patched) {
            throw new IllegalStateException("Unable to patch runtime snapshot for moved asset: " + sourceRoot);
        }
        updatedAssets.sort(Comparator.comparing(asset -> asset.assetRoot().toString(), String.CASE_INSENSITIVE_ORDER));
        return new PackerRuntimeSnapshot(generation, updatedRegistry, updatedAssets);
    }

    public PackerRuntimeSnapshot afterUpdateAssetContract(
            PackerRuntimeSnapshot snapshot,
            long generation,
            Path assetRoot,
            Path manifestPath,
            Optional<PackerRegistryEntry> registryEntry) {
        final PackerAssetDeclarationParseResult parsed = declarationParser.parse(manifestPath);
        final List<PackerRuntimeAsset> updatedAssets = new ArrayList<>();
        boolean patched = false;
        for (PackerRuntimeAsset asset : snapshot.assets()) {
            if (asset.assetRoot().equals(assetRoot.toAbsolutePath().normalize())) {
                updatedAssets.add(new PackerRuntimeAsset(
                        asset.assetRoot(),
                        asset.manifestPath(),
                        asset.registryEntry(),
                        parsed));
                patched = true;
            } else {
                updatedAssets.add(asset);
            }
        }
        if (!patched) {
            updatedAssets.add(new PackerRuntimeAsset(
                    assetRoot,
                    manifestPath,
                    registryEntry,
                    parsed));
            updatedAssets.sort(Comparator.comparing(asset -> asset.assetRoot().toString(), String.CASE_INSENSITIVE_ORDER));
        }
        return new PackerRuntimeSnapshot(generation, snapshot.registry(), updatedAssets);
    }
}
