package routines;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;

/*
 * user specification: the function's comment should contain keys as follows: 1. write about the function's comment.but
 * it must be before the "{talendTypes}" key.
 * 
 * 2. {talendTypes} 's value must be talend Type, it is required . its value should be one of: String, char | Character,
 * long | Long, int | Integer, boolean | Boolean, byte | Byte, Date, double | Double, float | Float, Object, short |
 * Short
 * 
 * 3. {Category} define a category for the Function. it is required. its value is user-defined .
 * 
 * 4. {param} 's format is: {param} <type>[(<default value or closed list values>)] <name>[ : <comment>]
 * 
 * <type> 's value should be one of: string, int, list, double, object, boolean, long, char, date. <name>'s value is the
 * Function's parameter name. the {param} is optional. so if you the Function without the parameters. the {param} don't
 * added. you can have many parameters for the Function.
 * 
 * 5. {example} gives a example for the Function. it is optional.
 */
public class LcagFileTools {

	private static LcagLogger log = LcagLogger.getLogger(LcagFileTools.class);

	/**
	 * getName: Get only the name of the given file path (name with extension)
	 *
	 * {talendTypes} String
	 *
	 * {Category} LCAG-File
	 *
	 * {param} string filePath: File path to use
	 *
	 * {example} getName("a/b/HelloWorld.txt") = "HelloWorld.txt"
	 */
	public static String getName(String filePath) {
		String result = FilenameUtils.getName(filePath);

		return result;
	}

	/**
	 * getBaseName: Get the base name of the given file path (without extension)
	 *
	 * {talendTypes} String
	 *
	 * {Category} LCAG-File
	 *
	 * {param} string filePath: File path to use
	 *
	 * {example} getName("a/b/HelloWorld.txt") = "HelloWorld"
	 */
	public static String getBaseName(String filePath) {
		String result = FilenameUtils.getBaseName(filePath);

		return result;
	}

	/**
	 * getBaseName: Get the extension of the given file path.
	 *
	 * {talendTypes} String
	 *
	 * {Category} LCAG-File
	 *
	 * {param} string filePath: File path to use
	 *
	 * {example} getName("a/b/HelloWorld.txt") = "txt"
	 */
	public static String getExtension(String filePath) {
		String result = FilenameUtils.getExtension(filePath);

		return result;
	}

	/**
	 * getBaseName: Get the path of the given file path without the file name.
	 *
	 * {talendTypes} String
	 *
	 * {Category} LCAG-File
	 *
	 * {param} string filePath: File path to use
	 *
	 * {example} getName("a/b/HelloWorld.txt") = "a/b";
	 */
	public static String getPath(String filePath) {
		String result = FilenameUtils.getFullPath(filePath);

		return result;
	}

	/**
	 * readTextFile: Read a file into a String. First it tries to lcate the given
	 * file path directly. If not found, it tries to locate the file path as a
	 * resource.
	 *
	 *
	 * {talendTypes} String
	 *
	 * {Category} LCAG-File
	 *
	 * {param} string fileName: File path to read
	 *
	 * {example} readTextFile("HelloWorld.txt")
	 */
	public static String readTextFile(String fileName) throws java.io.IOException {
		String result = null;
		File file = new File(fileName);
		String resourceilePath = getResourceFilePath(fileName);

		if (file.canRead()) {
			String fPath = file.getAbsolutePath();
			java.nio.file.Path filePath = java.nio.file.Paths.get(fPath);

			byte[] fileContent = java.nio.file.Files.readAllBytes(filePath);

			result = new String(fileContent);
		} else {
			result = readResourceFile(fileName);
		}

		return result;
	}

	public static void writeTextFile(String pFilePath, String pText) throws java.io.IOException {
		final File file = new File(pFilePath);

		FileUtils.writeStringToFile(file, pText, Charset.forName("UTF-8"));
	}

	public static String getResourceFilePath(String fileName) throws IOException {
		String result = fileName;

		if (!fileName.contains("/") && !fileName.contains("\\")) {
			ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

			URL url = classLoader.getResource(fileName);
			result = url.getPath();

			if (result.startsWith("/")) {
				// Drop staring / from paths like /C:/folder: Cuase file not found error when
				// opening
				result = result.substring(1);
			}
		}

		return result;
	}

	public static String readResourceFile(String fileName) throws IOException {
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		String result = null;

		InputStream is = classLoader.getResourceAsStream(fileName);

		if (is == null) {
			throw new IOException(String.format("readResourceFile: File %s not found", fileName));
		}

		InputStreamReader isr = new InputStreamReader(is);
		BufferedReader reader = new BufferedReader(isr);

		result = reader.lines().collect(Collectors.joining(System.lineSeparator()));

		return result;
	}

	/**
	 * createSameJobFileName: For a context.env_inputDirectory/fileName.ext create a
	 * new file name for the same job. The target in relative (see rePath) to the
	 * input directory: {targetDir}/fileName{preExtension}.ext
	 * 
	 * {talendTypes} String
	 * 
	 * {Category} LCAG
	 * 
	 * {param} String("srcFileName") input: source file name {param}
	 * String("targetDir") input: Target directory. use context.env_outputDir,
	 * context.env_tempDir, ... {param} String("preExtension") input: file name part
	 * to prepend before the file name extension {param} String("ext") input: use
	 * this extension instead of original one
	 * 
	 * 
	 */
	public static String createSameJobFileName(String srcFileName, String targetDir, String preExtension, String ext) {
		if (srcFileName == null)
			return null;
		if (preExtension == null)
			preExtension = "";
		if (ext == null)
			ext = FilenameUtils.getExtension(srcFileName);

		// get basename without extension
		String baseName = FilenameUtils.getBaseName(srcFileName);

		String result = String.format("%s/%s%s.%s", targetDir, baseName, preExtension, ext);
		result = FilenameUtils.normalize(result);

		return result;
	}

	/**
	 * createTimestampedFileName: Used for receive jobs and export from DB Jobs,
	 * only.
	 * 
	 * Creates a file name starting with a UTC timestamp for a given date. Drops
	 * leading directory path.
	 * 
	 * {talendTypes} String
	 * 
	 * {Category} LCAG-File
	 * 
	 * {param} String("aDate") input: Date to use for timestamp {param}
	 * String("srcFileName") input: source file name
	 * 
	 * 
	 */
	public static String createTimestampedFileName(Date aDate, String srcFileName) {
		if (aDate == null) {
			aDate = TalendDate.getCurrentDate();
		}
		String srcFileBaseName = FilenameUtils.getName((String) srcFileName);
		String tsString = TalendDate.formatDateInUTC(LcagDateTime.MDM_FILE_TIMESTAMP_FORMAT_STR, aDate);

		String result = String.format("%s_%s", tsString, srcFileBaseName);

		return result;
	}

	/**
	 * createTimestampedFileName: Used for receive jobs and export from DB Jobs,
	 * only.
	 * 
	 * Creates a file name starting with a current UTC timestamp.
	 * 
	 * {talendTypes} String
	 * 
	 * {Category} LCAG
	 * 
	 * {param} String("srcFileName") input: source file name
	 * 
	 * 
	 */
	public static String createTimestampedFileName(String srcFileName) {

		String result = createTimestampedFileName(TalendDate.getCurrentDate(), srcFileName);

		return result;
	}

	/**
	 * getFileTimestampAsString: retrieves the MDM timestamp prefix of a given file
	 * name.
	 * 
	 * {talendTypes} Object
	 * 
	 * {Category} LCAG
	 * 
	 * {param} String("srcFileName") input: file name with timestamp prefix
	 * 
	 * 
	 */
	public static String getFileTimestampAsString(Object srcFileName) {
		String result = null;

		String srcFileBaseName = FilenameUtils.getBaseName((String) srcFileName);
		int fnLength = srcFileBaseName.length();
		int tsLength = LcagDateTime.MDM_FILE_TIMESTAMP_FORMAT_STR.replaceAll("'", "").length();

		if (fnLength >= tsLength) {
			result = srcFileBaseName.substring(0, tsLength);
		}

		return result;
	}

	/**
	 * getFileTimestamp: retrieves the MDM data denoted by the MDM timestamp prefix
	 * of a given file name.
	 * 
	 * {talendTypes} Date
	 * 
	 * {Category} LCAG
	 * 
	 * {param} String("srcFileName") input: file name with timestamp prefix
	 * 
	 * 
	 */
	public static Date getFileTimestamp(String srcFileName) {

		if (srcFileName == null)
			throw new IllegalArgumentException("Filename argument may not be null");

		Date timestamp = null;
		int len = (srcFileName == null) ? 0 : srcFileName.length();

		if (len > 0) {
			String timestampString = getFileTimestampAsString(srcFileName);
			timestamp = LcagDateTime.secureParseDate(LcagConstant.FILE_TIMESTAMP_FORMAT, timestampString);
		}

		return timestamp;
	}

	/**
	 * createPrefixedFileName: Used for jobs, only.
	 * 
	 * Creates a file name starting with a current UTC timestamp and a number.
	 * Create for receiving multiple messages: The file name should start with the
	 * context.env_jobTimestamp followed by a number n that defines the n-th message
	 * received
	 * 
	 * Note: The prefix should identify the data flow of an incoming message
	 * throughout its entire processing by multiple jobs
	 * 
	 * {talendTypes} String
	 * 
	 * {Category} LCAG-File
	 * 
	 * {param} String("srcFileName") input: source file name {param} Date("d")
	 * input: Date to use, recommended: context.env_jobTimestamp {param} int("n")
	 * input: Number to make the prefix unique throughout all jobs
	 * 
	 * 
	 */
	public static String createPrefixedFileName(String srcFileName, Date d, int n) {
		String prefix = LcagDateTime.MDM_FILE_TIMESTAMP_FORMAT.format(d) + String.format("-x%05d_", n);
		String result = prefix + srcFileName;

		return result;
	}

	public static Path appendStringToFilePath(Path pSrcFilePath, String pEndFileNameWith) {
		String filePath = pSrcFilePath.toString();
		String ext = "." + getExtension(filePath);

		filePath = filePath.replace(ext, pEndFileNameWith + ext);

		return Path.of(filePath);
	}

	/**
	 * getFileNamePrefix: Used to fetch the file name prefix created by function
	 * createPrefixedFileName. This prefix should identify the data flow of an
	 * incoming message throughout its entire processing by multiple jobs
	 * 
	 * Note: This version supports the new prefix type (Timestamp prefix with
	 * number) as well as the old format (only Timestamp)
	 * 
	 * {talendTypes} String
	 * 
	 * {Category} LCAG-File
	 * 
	 * {param} String("srcFileName") input: source file name
	 */
	public static String getFileNamePrefix(String srcFileName) {
		String result = "";

		if (srcFileName == null)
			throw new IllegalArgumentException("File name argument must not be null");

		String srcFileBaseName = FilenameUtils.getBaseName((String) srcFileName);

		if (srcFileBaseName.indexOf("-x") < 0) {
			result = getFileTimestampAsString(srcFileName);

			if (result == null)
				result = "";
		} else {
			// replace needed to correctly handle formats including 'T' or 'Z':
			int prefixLength = LcagDateTime.MDM_FILE_TIMESTAMP_FORMAT_STR.replace("'", "").length()
					+ String.format("-x%05d_", 0).length();

			if (srcFileBaseName.length() >= prefixLength) {
				result = srcFileBaseName.substring(0, prefixLength);
			}
		}

		return result;
	}

	public static List<File> findFilesWithExtension(String pDirectoryPath, String... pExtList) {
		List<File> fileList = new ArrayList<>();
		File directory = new File(pDirectoryPath);

		findFilesWithExensionRecursive(directory, fileList, pExtList);
		return fileList;
	}

	private static void findFilesWithExensionRecursive(File pDirectory, List<File> pFileList, String... pExtList) {
		File[] files = pDirectory.listFiles();
		if (files != null) {
			for (File file : files) {
				if (file.isDirectory()) {
					findFilesWithExensionRecursive(file, pFileList, pExtList); // Recurse into sub directories
				} else {
					for (String ext : pExtList) {
						if (file.getName().endsWith(ext)) {
							pFileList.add(file);
						}
					}
				}
			}
		}
	}

}
