package ar.cpfw.main;

import ar.cpfw.signing.UsbToken;

import java.io.*;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

public class SignMain {
	public static void main(String[] args) {
		Properties properties = new Properties();

		// Load config
		try (InputStream input = new FileInputStream("./resources/config.cnf")) {
			properties.load(input);
		} catch (IOException e) {
			System.err.println("Failed to load config.cnf: " + e.getMessage());
			return;
		}

		// Config values
		String tokenLibPath = properties.getProperty("tokenLibPath");
		String tokenPassword = properties.getProperty("tokenPassword");
		String inputDirPath = properties.getProperty("inputDir", "pdfs");
		String outputDirPath = properties.getProperty("outputDir", "signed_pdfs");

		UsbToken token = new UsbToken(tokenLibPath);

		try {
			if (args.length == 2 && !args[0].equals("-dir")) {
				// === Single file mode ===
				String inputFile = args[0];
				String outputFile = args[1];
				token.signPdf(inputFile, outputFile, tokenPassword, "Semnat", "Drobeta Turnu Severin");
				System.out.println("Signed file: " + outputFile);

			} else if (args.length == 3 && args[0].equals("-dir")) {
				// === Directory mode via CLI ===
				inputDirPath = args[1];
				outputDirPath = args[2];
				signDirectory(token, tokenPassword, inputDirPath, outputDirPath);

			} else {
				// === Fallback to config.cnf directory mode ===
				signDirectory(token, tokenPassword, inputDirPath, outputDirPath);
			}

		} catch (Exception e) {
			System.err.println("Signing error: " + e.getMessage());
		}
	}

	private static void signDirectory(UsbToken token, String password, String inputDirPath, String outputDirPath) {
		File inputDir = new File(inputDirPath);
		if (!inputDir.exists() || !inputDir.isDirectory()) {
			System.err.println("Input directory does not exist or is not a directory: " + inputDirPath);
			return;
		}

		File outputDir = new File(outputDirPath);
		if (!outputDir.exists()) outputDir.mkdirs();

		// === Sign PDFs ===
		File[] pdfFiles = inputDir.listFiles((dir, name) -> name.toLowerCase().endsWith(".pdf"));
		if (pdfFiles != null && pdfFiles.length > 0) {
			int total = pdfFiles.length;
			for (int i = 0; i < total; i++) {
				File pdfFile = pdfFiles[i];
				String outputPath = new File(outputDir, pdfFile.getName().replace(".pdf", "_signed.pdf")).getPath();

				int progress = (int) (((i + 1) / (double) total) * 100);
				if ((i + 1) % 10 == 0 || i == total - 1) {
					System.out.printf("Signing file %d of %d (%d%%): %s%n", i + 1, total, progress, pdfFile.getName());
				}

				try {
					token.signPdf(pdfFile.getPath(), outputPath, password, "Semnat", "Drobeta Turnu Severin");
				} catch (Exception e) {
					System.err.println("Failed to sign " + pdfFile.getName() + ": " + e.getMessage());
				}
			}
		}

		// === Process ZIP files ===
		File[] zipFiles = inputDir.listFiles((dir, name) -> name.toLowerCase().endsWith(".zip"));
		if (zipFiles != null && zipFiles.length > 0) {
			for (File zipFile : zipFiles) {
				System.out.println("Found ZIP file: " + zipFile.getName());
				try {
					signZipFile(zipFile, outputDir, token, password);
				} catch (Exception e) {
					System.err.println("Failed to extract and sign PDFs from ZIP: " + zipFile.getName() + " - " + e.getMessage());
				}
			}
		}
	}

	private static void signZipFile(File zipFile, File outputDir, UsbToken token, String password) throws IOException {
		String zipBaseName = zipFile.getName().replaceFirst("[.][^.]+$", ""); // removes .zip
		File signedZipFile = new File(outputDir, zipBaseName + ".zip");

		try (
				ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(zipFile));
				FileOutputStream fos = new FileOutputStream(signedZipFile);
				ZipOutputStream signedZipOut = new ZipOutputStream(fos)
		) {
			ZipEntry entry;
			while ((entry = zipInputStream.getNextEntry()) != null) {
				if (!entry.getName().toLowerCase().endsWith(".pdf")) continue;

				System.out.println("Found PDF inside ZIP: " + entry.getName());

				// Create a temporary file to extract the original PDF
				File tempOriginalPdf = File.createTempFile("original_", ".pdf");
				try (FileOutputStream tempOut = new FileOutputStream(tempOriginalPdf)) {
					byte[] buffer = new byte[1024];
					int bytesRead;
					while ((bytesRead = zipInputStream.read(buffer)) != -1) {
						tempOut.write(buffer, 0, bytesRead);
					}
				}

				// Create a temporary file to hold the signed version
				File tempSignedPdf = File.createTempFile("signed_", ".pdf");
				try {
					token.signPdf(tempOriginalPdf.getAbsolutePath(), tempSignedPdf.getAbsolutePath(), password, "Semnat", "Drobeta Turnu Severin");

					// Add signed PDF to the output ZIP
					try (FileInputStream signedInput = new FileInputStream(tempSignedPdf)) {
						String signedEntryName = entry.getName().replace(".pdf", ".pdf");
						signedZipOut.putNextEntry(new ZipEntry(signedEntryName));

						byte[] buffer = new byte[1024];
						int len;
						while ((len = signedInput.read(buffer)) != -1) {
							signedZipOut.write(buffer, 0, len);
						}
						signedZipOut.closeEntry();
					}
				} finally {
					tempOriginalPdf.delete();
					tempSignedPdf.delete();
				}
			}
		}

		System.out.println("Signed ZIP created: " + signedZipFile.getAbsolutePath());
	}
}
