/*
 * Copyright 2013-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.cloud.internal;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.io.FileUtils;

import org.springframework.util.FileSystemUtils;

import static org.springframework.cloud.internal.Logger.error;
import static org.springframework.cloud.internal.Logger.info;

class ArtifactFetcher {

	private static final int CONNECT_TIMEOUT = 5000;

	private static final int READ_TIMEOUT = 5000;

	private final File outputFolder;

	private final String repoUrl;

	ArtifactFetcher(File outputFolder, String repoUrl) {
		this.outputFolder = outputFolder;
		this.repoUrl = repoUrl;
	}

	File unpackedDocs(Project project) {
		String projectName = project.name;
		String version = project.version;
		List<String> urls = urls(projectName, version);
		final File downloadedZipsFolder = new File(outputFolder, "downloaded-zips");
		downloadedZipsFolder.mkdirs();
		File outputZip = new File(downloadedZipsFolder, projectName + ".zip");
		for (String url : urls) {
			try {
				info(projectName + ": Fetching sources from [" + url + "]. Please wait...");
				FileUtils.copyURLToFile(new URL(url), outputZip, CONNECT_TIMEOUT, READ_TIMEOUT);
				info(projectName + ": Successfully fetched a zip from [" + url + "] to [" + outputZip + "]");
				break;
			}
			catch (IOException ex) {
				error(projectName + ": Failed to fetch a zip from [" + url + "]");
			}
		}
		if (!outputZip.exists()) {
			error(projectName + ": Exception occurred while trying to download an artifact with name [" + projectName
					+ "] and version [" + version + "]");
			return null;
		}
		return unpackDocs(projectName, outputZip);
	}

	private File unpackDocs(String projectName, File outputZip) {
		File unpackedDoc = unpackDoc(projectName, outputZip);
		info(projectName + ": Unpacked file to [" + unpackedDoc.getAbsolutePath() + "]");
		if (unpackedDoc.isDirectory()) {
			String[] subfolders = unpackedDoc.list();
			if (subfolders == null || subfolders.length != 1) {
				return unpackedDoc;
			}
			moveOneFolderUp(unpackedDoc, subfolders[0]);
		}
		return unpackedDoc;
	}

	private void moveOneFolderUp(File unpackedDoc, String subfolder) {
		File onlySubfolder = new File(unpackedDoc, subfolder);
		try {
			FileSystemUtils.copyRecursively(onlySubfolder, unpackedDoc);
		}
		catch (IOException e) {
			throw new IllegalStateException(e);
		}
		FileSystemUtils.deleteRecursively(onlySubfolder);
	}

	private List<String> urls(String projectName, String version) {
		ReleaseType releaseType = ReleaseType.fromVersion(version);
		String sourcesUrl = this.repoUrl + projectName + "/archive/";
		List<String> sourcesUrls = new LinkedList<>();
		if (releaseType == ReleaseType.SNAPSHOT) {
			// 2.0.0-SNAPSHOT or 2.0.0-BUILD-SNAPSHOT -> 2.0.x
			String[] splitVersion = version.split("\\.");
			sourcesUrls.add(sourcesUrl + splitVersion[0] + "." + splitVersion[1] + ".x.zip");
			// fallback
			sourcesUrls.add(sourcesUrl + "main.zip");
			sourcesUrls.add(sourcesUrl + "master.zip");
		}
		else {
			sourcesUrls.add(sourcesUrl + "v" + version + ".zip");
			sourcesUrls.add(sourcesUrl + version + ".zip");
		}
		return sourcesUrls;
	}

	private File unpackDoc(String artifactName, File zip) {
		File unzippedDocs = new File(outputFolder, artifactName);
		unzippedDocs.mkdirs();
		info(artifactName + ": Unpacking from [" + zip + "]");
		ZipCategory.unzipTo(zip, unzippedDocs);
		return unzippedDocs;
	}

}

enum ReleaseType {

	SNAPSHOT, NON_SNAPSHOT;

	static ReleaseType fromVersion(String version) {
		if (isSnapshot(version)) {
			return ReleaseType.SNAPSHOT;
		}
		return ReleaseType.NON_SNAPSHOT;
	}

	private static boolean isSnapshot(String version) {
		return version.contains("SNAPSHOT");
	}

}
