package application;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter;

import de.lcag.common.HashTable;
import de.lcag.common.Table.TableColumn;
import routines.LcagFileTools;
import routines.LcagLogger;

public class CommentExtractor {
	private static final String BUGFIX_COMMAND = "BUGFIX_"; // Fix of a Agile Defect in Trackspace
	private static final String STORY_COMMAND = "BUGFIX_"; // User Story in Trackspace to implement
	private static final String HOW_TO_COMMAND = "HOWTO_";
	private static final String DOCUMENT_FLOW_COMMAND = "FLOW_";
	private static final String TODO_COMMAND = "TODO_";

	private static final String[] COMMAD_LIST = new String[] { HOW_TO_COMMAND, DOCUMENT_FLOW_COMMAND, TODO_COMMAND,
			BUGFIX_COMMAND, STORY_COMMAND };

	private static final String COL_TYPE = "Type";
	private static final String COL_ID = "Id";
	private static final String COL_KIND = "Kind";
	private static final String COL_STEP = "StepNo";
	private static final String COL_DESC = "Description;style=width: 85%";

	private static final String COL_FILE = "File";
	private static final String COL_LINE_NO = "LineNo";

	public static final String JAVA_CODE_BASE_PATH = "/workspace/ebx_lufthansa/ebx_lufthansa-lib/src/main/java/com/lufthansa/ebx";
	public static final String SQL_CODE_BASE_PATH = "/workspace/ebx_lufthansa/ebx_lufthansa-resources/src/main/sql";
	public static final String XSD_CODE_BASE_PATH = "/workspace/MDM-Datamodel";
	public static final String JAVA_CODE_BASE_FULLPATH = "V:/EBX" + JAVA_CODE_BASE_PATH;
	public static final String SQL_CODE_BASE_FULLPATH = "V:/EBX" + SQL_CODE_BASE_PATH;
	public static final String XSD_CODE_BASE_FULLPATH = "V:/EBX" + XSD_CODE_BASE_PATH;

	public static final String CODE_BASE_GITHUB_DIR = "https://dev.azure.com/LCAGDevOps/MDM/_git/EBX?path=%s&version=GB%s&line=%d&lineEnd=%d&lineStartColumn=1&lineEndColumn=1&lineStyle=plain&_a=contents";
	public static final String DOC_FILE = "V:/EBX/workspace/ebx_lufthansa/ebx_lufthansa-lib/HowToImplement";

	private static HashTable resultTable = new HashTable(COL_TYPE, COL_ID, COL_KIND, COL_STEP, COL_DESC, COL_FILE,
			COL_LINE_NO);
	private static String gitBranchName = "main";

	private static LcagLogger logger = LcagLogger.getLogger(CommentExtractor.class);

	public static void main(String[] args) {
		String rootDirPath;

		logger.setJavaLogLevel(LcagLogger.LOG_TRACE);
		if (args.length < 1) {
			rootDirPath = JAVA_CODE_BASE_FULLPATH;
			logger.info("Using default Root directory to scan code: %s", rootDirPath);
		} else {
			rootDirPath = args[0];
		}

		try {
			gitBranchName = getCurrentGitBranch();
		} catch (Exception e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		resultTable.setName("MDM EBX Coding HowTo");

		try {
			extractCodeExamples(rootDirPath);

			TableColumn sortedResults = resultTable.sort(COL_ID, COL_STEP);

			String txt = resultTable.asCSV(sortedResults);
			String fileName = DOC_FILE + ".csv";
			LcagFileTools.writeTextFile(fileName, txt);

			writeHtmlFile(sortedResults);

			System.out.println(String.format("Saved Code examples to file: %s", DOC_FILE));
		} catch (IOException e) {
			logger.error("Error reading file: %s", e.getMessage());
		}
	}

	private static void writeHtmlFile(TableColumn sortedResults) throws IOException {
		String fileName = DOC_FILE + ".html";

		for (int i = 0; i < resultTable.lenght(); i++) {
			int lineNo = (int) resultTable.getCellValue(i, COL_LINE_NO);

			String colId = (String) resultTable.getCellValue(i, COL_ID);
			String filePath = (String) resultTable.getCellValue(i, COL_FILE);
			String gitURL = String.format(CODE_BASE_GITHUB_DIR, filePath, gitBranchName, lineNo, lineNo + 1);
			String lineNoURL = String.format("<html><a href=\"%s\">%d</a></html>", gitURL, lineNo);

			if (filePath.endsWith(".java")) {
				filePath = filePath.substring(JAVA_CODE_BASE_PATH.length() + 1);
			} else if (filePath.endsWith(".sql")) {
				filePath = filePath.substring(SQL_CODE_BASE_PATH.length() + 1);
			} else if (filePath.endsWith(".xsd")) {
				filePath = filePath.substring(XSD_CODE_BASE_PATH.length() + 1);
			}
			resultTable.setCellValue(i, COL_FILE, filePath);
			resultTable.setCellValue(i, COL_LINE_NO, lineNoURL);

			String kind = (String) resultTable.getCellValue(i, COL_KIND);

			if (kind.equals("Title")) {
				String disc = String.format("<html><b>%s</b></html>", (String) resultTable.getCellValue(i, COL_DESC));
				resultTable.setCellValue(i, COL_DESC, disc);

				// For BUGFIX or STORY: Add link into trackspace:
				String digits = colId.replaceAll(".*[BS](\\d+).*", "$1"); // Extract digits after 'B'

				if (!Objects.equals(colId, digits)) {
					String htmlLink = "<html><a href=\"https://trackspace.lhsystems.com/browse/MDMGMT-" + digits
							+ "\" target=\"_blank\">" + colId + "</a></html>";

					resultTable.setCellValue(i, COL_ID, htmlLink);
				}
			}
		}

		String txt = resultTable.asHTML(sortedResults);

		LcagFileTools.writeTextFile(fileName, txt);
		String md = FlexmarkHtmlConverter.builder().build().convert(txt);
		String mdFileName = DOC_FILE + ".md";
		LcagFileTools.writeTextFile(mdFileName, md);

	}

	public static String getCurrentGitBranch() throws IOException, InterruptedException {
		Process process = Runtime.getRuntime().exec("git rev-parse --abbrev-ref HEAD");
		process.waitFor();

		BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));

		return reader.readLine();
	}

	private static void extractCodeExamples(String pRootDirPath) throws IOException {
		List<File> fileList = LcagFileTools.findFilesWithExtension(pRootDirPath, ".java");
		final List<File> sqlFileList = LcagFileTools.findFilesWithExtension(SQL_CODE_BASE_FULLPATH, ".sql");
		final List<File> xsdFileList = LcagFileTools.findFilesWithExtension(XSD_CODE_BASE_FULLPATH, ".xsd");

		fileList.addAll(xsdFileList);
		fileList.addAll(sqlFileList);

		for (File javaFile : fileList) {
			String filename = javaFile.getName();
			BufferedReader reader = new BufferedReader(new FileReader(javaFile));
			StringBuilder currentComment = new StringBuilder();
			boolean inMultilineComment = false;
			boolean inSingleLineComment = false;
			String line;
			int lineNo = 0;
			int commentStartLineNo = 0;

			logger.trace("Processing file %s...", filename);

			while ((line = reader.readLine()) != null) {
				lineNo += 1;
				line = line.trim();

				final boolean singleLineComment = line.startsWith("//") /* java */ || line.startsWith("--") /* SQL */;

				if (!inMultilineComment && singleLineComment) {
					final String comment = line.substring(2).trim();

					inSingleLineComment = true;
				} else if (line.startsWith("/*") /* java */ || line.startsWith("<!--") /* xml */) {
					final String comment = line.startsWith("/**") ? line.substring(3)
							: (line.startsWith("<!--") ? line.substring(4) : line.substring(2));

					commentStartLineNo = lineNo;
					inMultilineComment = true;
					currentComment.append(comment.trim());
				}

				final boolean isFirstLineInComment = lineNo == commentStartLineNo;

				if (singleLineComment && inSingleLineComment) {
					final String comment = line.substring(2).trim();

					currentComment.append(" ").append(comment.trim());
				} else if (!singleLineComment && inSingleLineComment) {
					String comment = currentComment.toString().trim();
					inSingleLineComment = false;

					parseComment(comment, javaFile, lineNo);
					currentComment.setLength(0);
				} else if (inMultilineComment && (line.endsWith("*/") /* java */ || line.endsWith("-->") /* xml */ )) {
					String comment = line.endsWith("*/") ? line.substring(0, line.length() - 2)
							: line.substring(0, line.length() - 3);

					if (isFirstLineInComment) {
						comment = currentComment.toString().trim();
						comment = comment.endsWith("*/") ? comment.substring(0, comment.length() - 2)
								: comment.substring(0, comment.length() - 3);
					} else {
						currentComment.append(" ").append(comment.trim());
						comment = currentComment.toString().trim();
					}

					parseComment(comment, javaFile, lineNo);

					inMultilineComment = false;
					commentStartLineNo = 0;
					currentComment.setLength(0);
				} else if (inMultilineComment && !isFirstLineInComment) {
					if (line.startsWith("*"))
						line = line.substring(1).trim();
					currentComment.append(" ").append(line);
				}
			}

			reader.close();
		}
	}

	private static void parseComment(String pComment, File pInFile, int pLineNo) {
		String command = null;

		String type = null;
		String regEx = String.join("|", COMMAD_LIST);
		Pattern pattern = Pattern.compile(regEx);
		Matcher matcher = pattern.matcher(pComment);

		List<String> cmdList = new ArrayList<>();
		List<Integer> startPositionList = new ArrayList<>();

		while (matcher.find()) {
			final int start = matcher.start();
			String cmd = matcher.group();

			cmdList.add(cmd);
			startPositionList.add(start);
		}

		if (cmdList.isEmpty())
			return;

		for (int n = 0; n < cmdList.size(); n++) {
			String cmd = cmdList.get(n);
			int start = startPositionList.get(n);
			int end = 0;

			if (n == cmdList.size() - 1) {
				end = pComment.length();
			} else {
				String nextCmd = (String) cmdList.get(n + 1);
				end = startPositionList.get(n + 1);
			}

			start += cmd.length();
			command = pComment.substring(start, end);

			// FIXME: Problem if comment text also includes "_"
			command = command.replace("_", ";");

			command = command.replaceFirst(":", ";");
			List<String> split = new ArrayList<>(Arrays.asList(command.split(";")));

			if (split.size() < 2) {
				logger.warn("Unexpected HowTo format '%s', should be '%s H<ID>: [Title:|<StepNo>.] <Description>",
						pComment, cmd);
				return;
			}

			type = cmd.substring(0, cmd.length() - 1);

			String id = split.remove(0);
			String kind = split.remove(0).trim();
			String descr = String.join(" ", split).trim();
			String absoluteSourceCodeFilePath = pInFile.getAbsolutePath();

			absoluteSourceCodeFilePath = absoluteSourceCodeFilePath.replace("\\", "/");
			absoluteSourceCodeFilePath = absoluteSourceCodeFilePath.replaceFirst("V:/EBX", "");

			String stepNoStr = "00";

			if (!"Title".equals(kind)) {
				stepNoStr = kind.replace("Step", "");
				kind = "Step";

			}

			int rowNo = resultTable.newRow();

			resultTable.setCellValue(rowNo, COL_TYPE, type);
			resultTable.setCellValue(rowNo, COL_ID, id);
			resultTable.setCellValue(rowNo, COL_KIND, kind);
			resultTable.setCellValue(rowNo, COL_STEP, stepNoStr);
			resultTable.setCellValue(rowNo, COL_FILE, absoluteSourceCodeFilePath);
			resultTable.setCellValue(rowNo, COL_LINE_NO, pLineNo);
			resultTable.setCellValue(rowNo, COL_DESC, descr);

		}

	}

}
