package com.kiwigrid.helm.maven.plugin;

import java.io.File;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;

import com.kiwigrid.helm.maven.plugin.junit.MojoExtension;
import com.kiwigrid.helm.maven.plugin.junit.MojoProperty;
import com.kiwigrid.helm.maven.plugin.junit.SystemPropertyExtension;
import org.apache.maven.plugin.MojoExecutionException;
import org.codehaus.plexus.util.Os;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.ArgumentCaptor;

import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;

@ExtendWith({ SystemPropertyExtension.class, MojoExtension.class })
@MojoProperty(name = "helmDownloadUrl", value = "https://get.helm.sh/helm-v3.0.0-linux-amd64.tar.gz")
@MojoProperty(name = "chartDirectory", value = "junit-helm")
@MojoProperty(name = "chartVersion", value = "0.0.1")
public class InitMojoTest {

	@DisplayName("Init helm with different download urls.")
	@ParameterizedTest
	@ValueSource(strings = { "darwin", "linux", "windows" })
	public void initMojoHappyPathWhenDownloadHelm(String os, InitMojo mojo) throws Exception {

		// prepare execution
		doNothing().when(mojo).callCli(contains("helm "), anyString(), anyBoolean());
		// getHelmExecuteablePath is system-depending and has to be mocked for that reason
		// as SystemUtils.IS_OS_WINDOWS will always return false on a *NIX system
		doReturn(Paths.get("dummy/path/to/helm").toAbsolutePath()).when(mojo).getHelmExecuteablePath();
		mojo.setHelmDownloadUrl(getOsSpecificDownloadURL(os));

		// run init
		mojo.execute();

		// check helm file
		Path helm = Paths.get(mojo.getHelmExecutableDirectory(), "windows".equals(os) ? "helm.exe" : "helm")
				.toAbsolutePath();
		assertTrue(Files.exists(helm), "Helm executable not found at: " + helm);
	}

	@DisplayName("Init helm with a automatically detected URL")
	@Test
	public void autoDownloadHelm(InitMojo mojo) throws Exception {

		// prepare execution
		doNothing().when(mojo).callCli(contains("helm "), anyString(), anyBoolean());
		// getHelmExecuteablePath is system-depending and has to be mocked for that reason
		// as SystemUtils.IS_OS_WINDOWS will always return false on a *NIX system
		doReturn(Paths.get("dummy/path/to/helm").toAbsolutePath()).when(mojo).getHelmExecuteablePath();
		mojo.setHelmDownloadUrl(null);
		mojo.setHelmVersion("3.2.0");

		// run init
		mojo.execute();

		// check helm file
		Path helm = Paths.get(mojo.getHelmExecutableDirectory(), "windows".equals(Os.OS_FAMILY) ? "helm.exe" : "helm")
				.toAbsolutePath();
		assertTrue(Files.exists(helm), "Helm executable not found at: " + helm);
	}

	@Test
	public void verifyAddingStableByDefault(InitMojo mojo) throws Exception {

		// prepare execution
		ArgumentCaptor<String> helmCommandCaptor = ArgumentCaptor.forClass(String.class);
		doNothing().when(mojo).callCli(helmCommandCaptor.capture(), anyString(), anyBoolean());
		mojo.setHelmDownloadUrl(getOsSpecificDownloadURL());
		mojo.setAddDefaultRepo(true);

		// run init
		mojo.execute();

		// check captured argument
		String helmDefaultCommand = helmCommandCaptor.getAllValues()
				.stream()
				.filter(cmd -> cmd.contains(Os.OS_FAMILY == Os.FAMILY_WINDOWS ? "helm.exe repo" : "helm repo"))
				.findAny().orElseThrow(() -> new IllegalArgumentException("Only one helm repo command expected"));

		assertTrue(helmDefaultCommand.contains("repo add stable https://kubernetes-charts.storage.googleapis.com"), "Adding stable repo by default expected");
	}

	@Test
	public void verifyCustomConfigOptions(InitMojo mojo) throws Exception {

		// prepare execution
		ArgumentCaptor<String> helmCommandCaptor = ArgumentCaptor.forClass(String.class);
		doNothing().when(mojo).callCli(helmCommandCaptor.capture(), anyString(), anyBoolean());
		mojo.setHelmDownloadUrl(getOsSpecificDownloadURL());
		mojo.setRegistryConfig("/path/to/my/registry.json");
		mojo.setRepositoryCache("/path/to/my/repository/cache");
		mojo.setRepositoryConfig("/path/to/my/repositories.yaml");
		mojo.setAddDefaultRepo(true);

		// run init
		mojo.execute();

		// check captured argument
		List<String> helmCommands = helmCommandCaptor.getAllValues()
				.stream()
				.filter(cmd -> cmd.contains(Os.OS_FAMILY == Os.FAMILY_WINDOWS ? "helm.exe " : "helm "))
				.collect(Collectors.toList());
		assertEquals(1, helmCommands.size(), "Only helm init command expected");
		String helmDefaultCommand = helmCommands.get(0);
		assertTrue(helmDefaultCommand.contains("--registry-config /path/to/my/registry.json"), "Option 'registry-config' not set");
		assertTrue(helmDefaultCommand.contains("--repository-cache /path/to/my/repository/cache"), "Option 'repository-cache' not set");
		assertTrue(helmDefaultCommand.contains("--repository-config /path/to/my/repositories.yaml"), "Option 'repository-config' not set");
	}

	@Test
	public void verifyLocalHelmBinaryUsage(InitMojo mojo) throws MojoExecutionException {
		// Because the download URL is hardcoded to linux, only proceed if the OS is indeed linux.
		assumeTrue(isOSUnix());

		final URL resource = this.getClass().getResource("helm.tar.gz");
		final String helmExecutableDir = new File(resource.getFile()).getParent();
		mojo.callCli("tar -xf "
				+ helmExecutableDir
				+ File.separator
				// flatten directory structure using --strip to get helm executeable on basedir, see https://www.systutorials.com/docs/linux/man/1-tar/#lbAS
				+ "helm.tar.gz --strip=1 --directory="
				+ helmExecutableDir, "Unable to unpack helm to " + helmExecutableDir, false);

		// configure mojo
		mojo.setUseLocalHelmBinary(true);
		mojo.setHelmExecutableDirectory(helmExecutableDir);

		// execute
		mojo.execute();
	}

	private boolean isOSUnix() {
		return System.getProperty("os.name").matches(".*n[i|u]x.*");
	}

	private String getOsSpecificDownloadURL() {
		String osForDownload;
		switch (Os.OS_FAMILY) {
		case Os.FAMILY_UNIX:
			osForDownload = "linux";
			break;
		case Os.FAMILY_MAC:
			osForDownload = "darwin";
			break;
		default:
			osForDownload = Os.OS_FAMILY;
		}

		return getOsSpecificDownloadURL(osForDownload);
	}

	private String getOsSpecificDownloadURL(final String os) {
		return "https://get.helm.sh/helm-v2.15.2-" + os + "-amd64." + ("windows".equals(os) ? "zip" : "tar.gz");
	}
}