package krasa.console;

import krasa.actions.AutotestUtils;

import org.jetbrains.annotations.*;

import com.intellij.execution.filters.*;
import com.intellij.lang.xml.XMLLanguage;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diff.*;
import com.intellij.openapi.diff.ex.DiffContentFactory;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.search.PsiShortNamesCache;
import com.intellij.testFramework.LightVirtualFile;
import com.intellij.ui.awt.RelativePoint;

public class RequestComparatorFilter implements Filter {

	public static final String REQUEST_DOES_NOT_MATCH_EXPECTED_CONTENT_FOR = "Request does not match expected content for";

	Data data;

	@Nullable
	@Override
	public Result applyFilter(String line, int entireLength) {
		if (line == null) {
			return null;
		}
		if (line.substring(0, Math.min(line.length(), 100)).contains("RequestComparator")) {
			int path = line.indexOf(REQUEST_DOES_NOT_MATCH_EXPECTED_CONTENT_FOR);
			int actual = actualIndex(line);
			if (path > 0) {
				data = new Data();
				data.path = line.substring(path + REQUEST_DOES_NOT_MATCH_EXPECTED_CONTENT_FOR.length()).trim();
				data.hyperlinkInfoBase = new MyHyperlinkInfoBase(data);
			} else if (actual > 0 && data != null) {
				String substring = line.substring(actual + "Actual: ".length());
				data.actual = substring.trim();
				Result result = new Result(entireLength - line.length(), entireLength, data.hyperlinkInfoBase);
				data = null;
				return result;
			}
		}
		return null;
	}

	private int actualIndex(String line) {
		int i = line.indexOf("Actual: ");
		if (i <= 0) {
			i = line.indexOf("actual: ");
		}
		return i;
	}

	private static class MyHyperlinkInfoBase extends HyperlinkInfoBase {

		private Data data;

		public MyHyperlinkInfoBase(Data data) {
			this.data = data;
		}

		@Override
		public void navigate(@NotNull final Project project,
				@Nullable RelativePoint hyperlinkLocationPoint) {
			VirtualFile expected = findFile(project);

			if (expected == null) {
				ConsoleFilterProvider.notificationGroup.createNotification(
						"File not found " + data.path, MessageType.ERROR).notify(project);
				return;
			}

			final String actual = data.actual;
			String text = FormatUtils.reformat(project, actual);
			VirtualFile actualVF = new LightVirtualFile("Actual", XMLLanguage.INSTANCE, text);

			SimpleDiffRequest diffData = DiffContentFactory.compareVirtualFiles(project, actualVF, expected, data.path);
			// noinspection ConstantConditions
			diffData.setContentTitles("Actual", expected.getPath());
			final DiffContent[] contents = diffData.getContents();
			ApplicationManager.getApplication().runWriteAction(new Runnable() {

				public void run() {
					for (DiffContent content : contents) {
						Document document = content.getDocument();
						if (document != null) {
							FileDocumentManager.getInstance().saveDocument(document);
						}
					}
				}
			});
			DiffManager.getInstance().getDiffTool().show(diffData);
		}

		private VirtualFile findFile(Project project) {
			VirtualFile file = null;
			PsiShortNamesCache instance = PsiShortNamesCache.getInstance(project);
			String fileName = AutotestUtils.getFileName(data.path);
			PsiFile[] filesByName = getFilesByName(instance, fileName);
			String expectedPath = data.path;
			if (!expectedPath.startsWith("/")) {
				expectedPath = "/" + expectedPath;
			}
			for (PsiFile psiFile : filesByName) {
				VirtualFile virtualFile = psiFile.getVirtualFile();
				String path = virtualFile.getPath();
				if (path.contains(expectedPath) && path.contains("dummy-requests")) {
					file = virtualFile;
				}
			}
			return file;
		}

		private PsiFile[] getFilesByName(PsiShortNamesCache instance, String fileName) {
			PsiFile[] filesByName = instance.getFilesByName(fileName);
			if (filesByName.length == 0) {
				filesByName = instance.getFilesByName(fileName + ".xml");
			}
			if (filesByName.length == 0) {
				filesByName = instance.getFilesByName(fileName + ".json");
			}
			return filesByName;
		}
	}

	private static class Data {

		String path;
		String actual;
		HyperlinkInfoBase hyperlinkInfoBase;

		@Override
		public String toString() {
			final StringBuilder sb = new StringBuilder("Data{");
			sb.append("path='").append(path).append('\'');
			sb.append(", actual='").append(actual).append('\'');
			sb.append('}');
			return sb.toString();
		}
	}

}
