package com.github.dhaval_mehta.savetogoogledrive.uploader.drive;

import static com.github.dhaval_mehta.savetogoogledrive.utility.HttpUtilities.USER_AGENT;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.FilenameUtils;
import org.springframework.http.HttpStatus;

import com.github.dhaval_mehta.savetogoogledrive.exception.ApiException;
import com.github.dhaval_mehta.savetogoogledrive.model.DownloadFileInfo;
import com.github.dhaval_mehta.savetogoogledrive.model.User;
import com.github.dhaval_mehta.savetogoogledrive.utility.HttpUtilities;

public class DriveUploaderBuilder {

	private static final Pattern CONTENT_DISPOSITION_PATTERN = Pattern
			.compile("(?i)filename[^;\\n=]*=(?:utf-8'')?[\"]?([^;\"]*)");

	private User user;
	private DownloadFileInfo downloadFileInfo;

	public DriveUploaderBuilder() {
		downloadFileInfo = new DownloadFileInfo();
	}

	public DriveUploaderBuilder setFileName(String fileName) {
		downloadFileInfo.setFileName(fileName);
		return this;
	}

	public DriveUploaderBuilder setUser(User user) {
		this.user = user;
		return this;
	}

	public DriveUploaderBuilder setUploadUrl(URL uploadUrl) {
		downloadFileInfo.setUploadUrl(uploadUrl);
		return this;
	}

	public DriveUploader build() throws IOException {
		assert user != null;
		assert downloadFileInfo.getUploadUrl() != null;
		fetchFileInformation();

		if (downloadFileInfo.isResumeSupported())
			return new ResumableDriveUploader(downloadFileInfo, user);
		else
			return new NonResumableDriveUploader(downloadFileInfo, user);
	}

	private void fetchFileInformation() throws IOException {
		assert downloadFileInfo.getUploadUrl() != null;
		HttpURLConnection connection = (HttpURLConnection) downloadFileInfo.getUploadUrl().openConnection();
		connection.setRequestProperty("User-Agent", USER_AGENT);
		connection.setRequestMethod("HEAD");

		connection.connect();

		int statusCode = connection.getResponseCode();

		if (!HttpUtilities.success(statusCode))
			throw new ApiException(HttpStatus.UNPROCESSABLE_ENTITY, "Server can not download file");

		String mimeType = connection.getContentType().toLowerCase();

		if (mimeType.contains("application/xml") || mimeType.contains("text/xml")
				|| mimeType.contains("application/json") || mimeType.contains("text/html"))
			throw new ApiException(HttpStatus.UNPROCESSABLE_ENTITY, "Given URL does not point to a file.");

		downloadFileInfo.setContentLength(connection.getContentLengthLong());
		downloadFileInfo.setContentType(connection.getContentType());

		String acceptRangeHeader = connection.getHeaderField("Accept-Ranges");

		if (acceptRangeHeader != null)
			downloadFileInfo.setResumeSupported(acceptRangeHeader.startsWith("bytes"));
		else
			downloadFileInfo.setResumeSupported(checkResumeSupportUsingGetMethod());

		if (downloadFileInfo.getFileName() == null || downloadFileInfo.getFileName().equals("")) {
			String contentDisposition = connection.getHeaderField("Content-Disposition");
			downloadFileInfo.setFileName(findFileName(contentDisposition));
		}
	}

	private String findFileName(String contentDisposition) {

		if (contentDisposition != null)
			try {
				Matcher matcher = CONTENT_DISPOSITION_PATTERN.matcher(contentDisposition);

				if (matcher.find()) {
					String match = matcher.group(1);

					if (match != null && !match.equals("")) {
						match = URLDecoder.decode(match, "UTF-8");
						return match;
					}
				}

			} catch (IllegalStateException ex) {
				// cannot find filename using content disposition http header.
				// Use url to find filename;
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
		return FilenameUtils.getName(downloadFileInfo.getUploadUrl().getPath());
	}

	private boolean checkResumeSupportUsingGetMethod() throws IOException {
		HttpURLConnection connection = (HttpURLConnection) downloadFileInfo.getUploadUrl().openConnection();
		connection.setRequestMethod("GET");
		String rangeHeaderValue = "bytes=" + 0 + "-" + 0;
		connection.setRequestProperty("Range", rangeHeaderValue);
		connection.setRequestProperty("User-Agent", USER_AGENT);
		return HttpStatus.PARTIAL_CONTENT.value() == connection.getResponseCode();
	}
}