package p.packer.services;

import p.packer.PackerWorkspacePaths;
import p.packer.messages.AssetReference;
import p.packer.messages.PackerProjectContext;
import p.packer.messages.diagnostics.PackerDiagnosticCategory;
import p.packer.messages.diagnostics.PackerDiagnosticSeverity;
import p.packer.models.*;

import java.nio.file.Path;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

public final class PackerAssetReferenceResolver {
    private final PackerRegistryLookup lookup;

    public PackerAssetReferenceResolver() {
        this(new PackerRegistryLookup());
    }

    public PackerAssetReferenceResolver(PackerRegistryLookup lookup) {
        this.lookup = Objects.requireNonNull(lookup, "lookup");
    }

    public PackerResolvedAssetReference resolve(
            PackerProjectContext project,
            PackerRuntimeSnapshot snapshot,
            AssetReference assetReference) {
        final String reference = Objects.requireNonNull(assetReference, "assetReference").rawValue();
        final Optional<PackerRegistryEntry> byId = parseAssetId(reference)
                .flatMap(assetId -> lookup.findByAssetId(snapshot.registry(), assetId));
        if (byId.isPresent()) {
            final Path assetRoot = PackerWorkspacePaths.assetRoot(project, byId.get().root());
            return new PackerResolvedAssetReference(assetRoot, byId, findRuntimeAsset(snapshot, assetRoot), List.of());
        }

        final Optional<PackerRegistryEntry> byUuid = lookup.findByAssetUuid(snapshot.registry(), reference);
        if (byUuid.isPresent()) {
            final Path assetRoot = PackerWorkspacePaths.assetRoot(project, byUuid.get().root());
            return new PackerResolvedAssetReference(assetRoot, byUuid, findRuntimeAsset(snapshot, assetRoot), List.of());
        }

        final Path candidateRoot = PackerWorkspacePaths.assetRoot(project, reference);
        final Optional<PackerRuntimeAsset> runtimeAsset = findRuntimeAsset(snapshot, candidateRoot);
        if (runtimeAsset.isPresent()) {
            return new PackerResolvedAssetReference(
                    candidateRoot,
                    lookup.findByRoot(project, snapshot.registry(), candidateRoot),
                    runtimeAsset,
                    List.of());
        }

        final Optional<PackerRegistryEntry> registryEntry = lookup.findByRoot(project, snapshot.registry(), candidateRoot);
        if (registryEntry.isPresent()) {
            return new PackerResolvedAssetReference(candidateRoot, registryEntry, Optional.empty(), List.of());
        }

        return new PackerResolvedAssetReference(
                candidateRoot,
                Optional.empty(),
                Optional.empty(),
                List.of(new PackerDiagnostic(
                        PackerDiagnosticSeverity.ERROR,
                        PackerDiagnosticCategory.STRUCTURAL,
                        "Requested asset reference could not be resolved.",
                        candidateRoot,
                        true)));
    }

    private Optional<Integer> parseAssetId(String reference) {
        try {
            return Optional.of(Integer.parseInt(reference.trim()));
        } catch (NumberFormatException ignored) {
            return Optional.empty();
        }
    }

    private Optional<PackerRuntimeAsset> findRuntimeAsset(PackerRuntimeSnapshot snapshot, Path assetRoot) {
        final Path normalizedRoot = Objects.requireNonNull(assetRoot, "assetRoot").toAbsolutePath().normalize();
        return snapshot.assets().stream()
                .filter(candidate -> candidate.assetRoot().equals(normalizedRoot))
                .findFirst();
    }
}
