package de.tobias.playwall.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import de.tobias.playwall.infrastructure.ArtifactoryConfigurationProperties;
import de.tobias.playwall.model.Plugin;
import de.tobias.playwall.model.PluginDescription;
import de.tobias.playwall.model.PluginManifest;
import de.tobias.playwall.model.artifactory.Folder;
import de.tobias.playwall.model.artifactory.Version;
import jakarta.annotation.PostConstruct;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.aot.hint.annotation.RegisterReflectionForBinding;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.util.Comparator;
import java.util.List;

@Service
@AllArgsConstructor
@RegisterReflectionForBinding({PluginDescription.class, PluginManifest.class, Folder.class, Folder.FolderItem.class,
		ArtifactoryClient.ArchiveViewSourceRequest.class, ArtifactoryClient.ArchiveViewSourceResponse.class})
public class ArtifactoryClient
{
	private final WebClient webClient;
	private final ArtifactoryConfigurationProperties configurationProperties;

	private final VersionTokenizer versionTokenizer;

	private ObjectMapper mapper;

	@PostConstruct
	private void init()
	{
		mapper = new ObjectMapper(new YAMLFactory());
		mapper.findAndRegisterModules();
	}

	public Version getLatestVersion(PluginDescription plugin)
	{
		final ResponseEntity<Folder> folderResponse = webClient.get()
				.uri("/artifactory/api/storage/%s/%s/%s".formatted(configurationProperties.getRepository(), configurationProperties.getGroupId().replace(".", "/"), plugin.getName()))
				.retrieve()
				.toEntity(Folder.class)
				.block();
		if(folderResponse == null || folderResponse.getBody() == null)
		{
			return null;
		}
		final List<Version> version = folderResponse.getBody().getChildren().stream()
				.filter(Folder.FolderItem::isFolder)
				.map(child -> versionTokenizer.getVersion(child.getUri()))
				.sorted(Comparator.comparing(Version::major).thenComparing(Version::minor).thenComparing(Version::patch))
				.toList();
		if (version.isEmpty()) {
			return null;
		}
		return version.get(version.size() - 1);
	}

	record ArchiveViewSourceRequest(String archivePath, String repoKey, String sourcePath)
	{
	}

	record ArchiveViewSourceResponse(String source)
	{
	}

	@SneakyThrows
	public PluginManifest getPluginManifest(PluginDescription description, Version version)
	{
		final ResponseEntity<ArchiveViewSourceResponse> response = webClient.post()
				.uri("/ui/api/v1/ui/archiveViewSource")
				.header("X-Requested-With", "XMLHttpRequest")
				.bodyValue(new ArchiveViewSourceRequest(
						"%s/%s/%s/%s-%s.jar".formatted(
								configurationProperties.getGroupId().replace(".", "/"),
								description.getName(),
								version.toVersionString(),
								description.getName(),
								version.toVersionString()
						),
						configurationProperties.getRepository(),
						"plugin.yml"
				))
				.retrieve()
				.toEntity(ArchiveViewSourceResponse.class)
				.block();

		if(response == null || response.getBody() == null)
		{
			return null;
		}
		return mapper.readValue(response.getBody().source(), PluginManifest.class);
	}

	public Mono<byte[]> downloadArtifact(Plugin plugin)
	{
		return webClient.get()
				.uri("/artifactory/%s/%s/%s/%s/%s-%s.jar".formatted(
						configurationProperties.getRepository(),
						configurationProperties.getGroupId().replace(".", "/"),
						plugin.getName(),
						plugin.getVersion(),
						plugin.getName(),
						plugin.getVersion()
				))
				.retrieve()
				.bodyToMono(byte[].class);
	}
}