package application;

import java.awt.BorderLayout;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.naming.ConfigurationException;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileNameExtensionFilter;

import org.apache.commons.io.FilenameUtils;

import de.lcag.common.ExcelWorkbook;
import de.lcag.common.LcagProperties;
import de.lcag.common.MappingTable;
import de.lcag.common.Table;
import de.lcag.common.WorkbookComparisonResult;
import routines.LcagFileTools;
import routines.LcagStringTools;

public class FileActions extends JFrame {
	private static final int FRAME_HEIGHT = 150;
	private static String MASTER_FILE_LABEL = "Main Excel File";
	private static String CONFIG_FILE_LABEL = "Configuration File";

	private JLabel masterFileLabel, clientFileLabel, configFileLabel;
	private JButton selectMasterFile, selectClientFile, selectConfigFile;
	private Path configFile;
	private String masterFile, clientFile;
	private JMenuBar menuBar;
	private JMenu actionsMenu;
	private JMenuItem mergeAction;
	private JPopupMenu contextMenuMasterFile, contextMenuClientFile;
	private File configFileDirectoryPath = null;
	private LcagProperties config = null;
	private File masterDirecory = null;
	private File clientDirecory = null;
	private static List<String> SUPPORTED_FILE_TYPES = List.of("xls", "xlsx");

	public FileActions() {
		setTitle("Compare 2 Excel Files");
		setSize(800, FRAME_HEIGHT);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		JPanel mainPanel = new JPanel(new BorderLayout());
		JPanel leftButtonPanel = new JPanel(new GridLayout(3, 1, 5, 5));
		JPanel rightTextPanel = new JPanel(new GridLayout(3, 1, 5, 5));
		// setLayout(new GridLayout(4, 2));

		leftButtonPanel.setPreferredSize(new Dimension(200, FRAME_HEIGHT)); // Fixed width

		// Add an empty border to create a gap on the right side of leftPanel
		leftButtonPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10)); // right gap: 10px

		rightTextPanel.setPreferredSize(new Dimension(1000, FRAME_HEIGHT));
		mainPanel.add(leftButtonPanel, BorderLayout.WEST);
		mainPanel.add(rightTextPanel, BorderLayout.EAST);

		configFileLabel = new JLabel(String.format(" %s: Not selected", CONFIG_FILE_LABEL));

		selectConfigFile = new JButton("Select " + CONFIG_FILE_LABEL);
		leftButtonPanel.add(selectConfigFile);
		rightTextPanel.add(configFileLabel);

		// File Chooser for config file
		selectConfigFile.setEnabled(true);
		selectConfigFile.addActionListener(e -> {
			if (!configFileDirectoryPath.isDirectory()) {
				System.err.println(String.format("ERROR: Config directory %s does not exist",
						configFileDirectoryPath.getAbsolutePath()));
				System.exit(1);
			}

			JFileChooser fileChooser = new JFileChooser(configFileDirectoryPath);
			FileNameExtensionFilter filter = new FileNameExtensionFilter("Config Files", "properties");
			fileChooser.setFileFilter(filter);
			if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
				String clientDirString = null;

				configFile = Path.of(selectFile(fileChooser));
				config = new LcagProperties(configFile.toString());

				configFileLabel.setText(String.format(" %s: %s", CONFIG_FILE_LABEL, configFile));

				masterFile = config.getOptionalProperty("main_file");

				if (masterFile != null) {
					String masterFilePath = LcagFileTools.getPath(masterFile);
					masterFile = LcagFileTools.getName(masterFile);
					masterDirecory = new File(masterFilePath);
					masterFileLabel.setText(String.format("<html>Selected: %s<br>in: %s</html>", masterFile,
							masterDirecory.getAbsolutePath()));
					selectMasterFile.setEnabled(true);
				}

				clientFile = config.getOptionalProperty("compare_file");
				if (clientFile != null) {
					String clientFilePath = LcagFileTools.getPath(clientFile);
					clientFile = LcagFileTools.getName(clientFile);
					clientDirecory = new File(clientFilePath);

					if (clientDirecory.isAbsolute()) {
						clientDirString = clientFilePath;
					} else {
						clientDirString = "same directory as 1st file";
					}
					clientFileLabel.setText(
							String.format("<html>Selected: %s<br>in: %s </html>", clientFile, clientDirString));
					selectClientFile.setEnabled(true);
				}
				// After user selected a config file enable buttons to set files to compare (can
				// overwrite files listed in config file):

			}
		});

		// Kontextmenüs erstellen
		contextMenuMasterFile = new JPopupMenu();
		contextMenuClientFile = new JPopupMenu();
		JMenuItem renameFile1 = new JMenuItem("Rename File");
		JMenuItem renameFile2 = new JMenuItem("Rename File");
		contextMenuMasterFile.add(renameFile1);
		contextMenuClientFile.add(renameFile2);

		masterFileLabel = new JLabel(
				String.format(" %s: %s", MASTER_FILE_LABEL, masterFile == null ? "Not selected" : masterFile));

		selectMasterFile = new JButton("Select " + MASTER_FILE_LABEL);
		leftButtonPanel.add(selectMasterFile);
		rightTextPanel.add(masterFileLabel);

		clientFileLabel = new JLabel(
				String.format(" %s: %s", "Excel to compare", clientFile == null ? "Not selected" : clientFile));

		selectClientFile = new JButton("Select Excel to Compare");

		// MouseListener für Kontextmenü auf den Labels
		masterFileLabel.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				if (SwingUtilities.isRightMouseButton(e) && masterFile != null) {
					contextMenuMasterFile.show(masterFileLabel, e.getX(), e.getY());
				}
			}
		});
		clientFileLabel.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				if (SwingUtilities.isRightMouseButton(e) && clientFile != null) {
					contextMenuClientFile.show(clientFileLabel, e.getX(), e.getY());
				}
			}
		});

		leftButtonPanel.add(selectClientFile);
		rightTextPanel.add(clientFileLabel);

		renameFile1.addActionListener(e -> renameFile(masterFile, masterFileLabel, "Datei 1"));
		renameFile2.addActionListener(e -> renameFile(clientFile, clientFileLabel, "Datei 2"));

		// File Chooser für Datei 1 (nur Excel-Dateien)
		selectMasterFile.setEnabled(false);
		selectMasterFile.addActionListener(e -> {
			JFileChooser fileChooser = new JFileChooser();
			FileNameExtensionFilter filter = new FileNameExtensionFilter("Excel-Files",
					SUPPORTED_FILE_TYPES.toArray(new String[0]));
			fileChooser.setFileFilter(filter);

			if (masterFile != null) {
				Path masterFilePath = Path.of(masterFile);
				masterDirecory = new File(masterFilePath.getParent().toString());
				fileChooser.setCurrentDirectory(masterDirecory);

				masterDirecory = new File(masterFilePath.getParent().toString());
				masterFileLabel.setText(String.format("<html>Selected: %s<br>in: %s</html>", masterFile,
						masterFilePath.getParent().toString()));
				selectClientFile.setEnabled(true);
			}

			if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
				Path masterFilePath = Path.of(masterFile);
				masterFile = selectFile(fileChooser);
				masterDirecory = new File(masterFilePath.getParent().toString());
				masterFileLabel.setText(String.format(" %s: %s", MASTER_FILE_LABEL, masterFilePath.getFileName()));
				selectClientFile.setEnabled(true);
			}
		});

		// File Chooser für Datei 2 (im selben Verzeichnis wie Datei 1, nur
		// Excel-Dateien)
		selectClientFile.setEnabled(false);
		selectClientFile.addActionListener(e -> {
			if (masterDirecory != null) {
				JFileChooser fileChooser = new JFileChooser(masterDirecory);
				FileNameExtensionFilter filter = new FileNameExtensionFilter("Excel-Dateien", "xls", "xlsx");
				fileChooser.setFileFilter(filter);
				if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
					clientFile = selectFile(fileChooser);
					clientFileLabel.setText("Datei 2: " + clientFile);
				}
			}
		});

		// Menü erstellen
		menuBar = new JMenuBar();
		actionsMenu = new JMenu("Actions");
		mergeAction = new JMenuItem("Merge 2nd into Main file");

		mergeAction.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M, ActionEvent.CTRL_MASK));
		mergeAction.addActionListener(e -> {
			System.out.println("Ctrl-C pressed on menu");
		});

		actionsMenu.add(mergeAction);
		menuBar.add(actionsMenu);
		setJMenuBar(menuBar);

		// Aktion: Erste Datei in Excel öffnen
		mergeAction.addActionListener(e -> {
			mergeFiles(config, masterFile, clientFile);
		});

		add(mainPanel);
		pack();
		setVisible(true);
	}

	private String selectFile(JFileChooser fileChooser) {
		File result = fileChooser.getSelectedFile();
		return FilenameUtils.separatorsToUnix(result.getAbsolutePath());
	}

	private void mergeFiles(LcagProperties pConfig, String pMasterFilePathPattern, String pClientFilePathPattern) {
		Pattern masterPattern = Pattern.compile(pMasterFilePathPattern);
		List<Path> masterFiles = new ArrayList<>();
		String masterDirectoryPathStr = masterDirecory.getAbsolutePath();

		try {
			Path masterDirectoryPath = Path.of(masterDirectoryPathStr);
			masterFiles = Files.walk(masterDirectoryPath).filter(p -> {
				String filePath = FilenameUtils.separatorsToUnix(p.toString());
				String fileName = LcagFileTools.getName(p.toString());

				Matcher matcher = masterPattern.matcher(fileName);
				boolean matches = matcher.matches();
				return matches;
			}).toList();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		if (pConfig == null) {
			JOptionPane.showMessageDialog(this, "Pls select at least a config file!", "Error",
					JOptionPane.ERROR_MESSAGE);

			return;
		}

		for (Path aMasterFilePath : masterFiles) {
			String masterFileName = aMasterFilePath.getFileName().toString();
			String fileType = LcagFileTools.getExtension(masterFileName);

			if (!SUPPORTED_FILE_TYPES.contains(fileType)) {
				System.err.printf("Comparision of file type %s not supported for %s\n", fileType, masterFileName);
				continue;
			}

			String s = FilenameUtils.separatorsToUnix(aMasterFilePath.normalize().toString());

			String clientFilePathStr = clientDirecory.getAbsolutePath() + "/"
					+ masterFileName.replaceFirst(pMasterFilePathPattern, pClientFilePathPattern);
			Path clientFilePath = Path.of(clientFilePathStr);
			String clientDirString = null;

			if (clientFilePath.isAbsolute()) {
				clientDirString = clientFilePath.getParent().toString();
			} else {
				// client file is relative expect it is same directory as master
				Path dirPath = aMasterFilePath.getParent();

				clientDirString = "same directory as 1st file";
				clientFilePath = dirPath.resolve(clientFilePath);
			}

			if (clientFilePath != null) {
				String fileName = clientFilePath.getFileName().toString();
				clientFileLabel
						.setText(String.format("<html>Selected: %s<br>in: %s</html>", fileName, clientDirString));
				selectClientFile.setEnabled(true);
			}

			String keyFieldString = pConfig.getProperty("list_of_keys");

			List<String> keyFields = LcagStringTools.asList(keyFieldString, ",\\s*");

			if (aMasterFilePath != null && clientFilePath != null) {

				try {
					Path resultFilePath = LcagFileTools.appendStringToFilePath(aMasterFilePath, "-merged");
					String fieldMapping = pConfig.getProperty("column.mapping.main2compare");

					Files.copy(aMasterFilePath, resultFilePath, StandardCopyOption.REPLACE_EXISTING);

					ExcelWorkbook excel1 = new ExcelWorkbook(resultFilePath.toString());
					ExcelWorkbook excel2 = new ExcelWorkbook(clientFilePath.toString());

					excel1.setKeyColumns(keyFields);
					excel1.read();
					excel2.setKeyColumns(keyFields);
					excel2.read();

					excel1.setOption(keyFieldString, FRAME_HEIGHT);

					if (fieldMapping != null) {
						MappingTable mappingTable = new MappingTable(fieldMapping, ";", "->");

						excel2.setOption(Table.OPTION_USE_MAPPING, mappingTable);
					}

					WorkbookComparisonResult compareResult = excel1.compareTo(excel2);

					if (compareResult.tablesAreEquals()) {
						System.out.printf("RESULT: Tables %s and %s are equal.\n", resultFilePath, clientFilePath);
					} else {
						ExcelWorkbook integratedTable = (ExcelWorkbook) compareResult.integrateDifferencesIntoMaster();

						integratedTable.save();

						Desktop.getDesktop().open(resultFilePath.toFile());
					}
				} catch (IOException ex) {
					JOptionPane.showMessageDialog(this, "Cannot open comparison result file.", "Error",
							JOptionPane.ERROR_MESSAGE);
				} catch (ConfigurationException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

	private Path getPath(LcagProperties config, String pProperty, File pMasterFile) {
		Path result = null;
		String filePathString = (pMasterFile == null) ? config.getProperty(pProperty) : pMasterFile.getAbsolutePath();

		if (!LcagStringTools.isEmpty(filePathString)) {
			result = Path.of(filePathString);
		}

		return result;
	}

	private Path renameFile(String pFile, JLabel label, String prefix) {
		Path newPath = null;
		if (pFile == null)
			return newPath;

		File file = new File(pFile.toString());
		String newName = JOptionPane.showInputDialog(this, "Enter new name:", file.getName());

		if (!LcagStringTools.isEmpty(newName)) {
			Path filePath = Path.of(pFile);
			newPath = filePath.getParent().resolve(newName);

			File newFile = new File(newPath.toString());
			if (file.renameTo(newFile)) {
				file = newFile;
				label.setText(prefix + ": " + newFile.getName());
				if (prefix.equals("Datei 1"))
					this.masterFile = newPath.toString();
				else
					this.clientFile = newPath.toString();
			} else {
				JOptionPane.showMessageDialog(this, "Umbenennung fehlgeschlagen!", "Fehler", JOptionPane.ERROR_MESSAGE);
			}
		}

		return newPath;
	}

	public static void main(String[] args) {
		String cfgFileDirectory = args.length < 1 ? System.getenv("_MY_DATA") + "/Tools" : args[0];

		SwingUtilities.invokeLater(() -> {
			FileActions gui = new FileActions();

			gui.configFileDirectoryPath = new File(cfgFileDirectory);
			gui.setVisible(true);
		});
	}
}
