using System.IO.Compression;
using System.Security.Cryptography;
using System.Text;

class Program(string inputFile, string passwordPrompt, string outputFile)
{
	static void Main(string[] args)
	{
		Program program = new("input.txt", "Enter password: ", "encrypted.payload");
		program.CreateCompressedEncryptedPayload();
	}

	void CreateCompressedEncryptedPayload()
	{
		Console.Write(passwordPrompt);
		string? password = Console.ReadLine();
		if (string.IsNullOrEmpty(password)) return;

		byte[]? inputData = File.ReadAllBytes(inputFile);
		string fileName = Path.GetFileName(inputFile); // only store FileName and extension
		byte[]? fileNameBytes = Encoding.UTF8.GetBytes(fileName);
		byte[]? fileNameLength = BitConverter.GetBytes(fileNameBytes.Length);

		// Compress
		using MemoryStream? compressedStream = new();
		using (GZipStream? gzip = new(compressedStream, CompressionMode.Compress))
		{
			gzip.Write(inputData, 0, inputData.Length);
		}
		byte[]? compressedData = compressedStream.ToArray();

		// Encrypt
		byte[]? salt = new byte[16];
		RandomNumberGenerator.Fill(salt);

		using Rfc2898DeriveBytes? keyDeriver = new(password, salt, 100000, HashAlgorithmName.SHA256);
		byte[]? key = keyDeriver.GetBytes(32);
		byte[]? iv = keyDeriver.GetBytes(16);

		using Aes? aes = Aes.Create();
		aes.Key = key;
		aes.IV = iv;

		using MemoryStream? ms = new();
		ms.Write(fileNameLength, 0, fileNameLength.Length); // FileName Length
		ms.Write(fileNameBytes, 0, fileNameBytes.Length);  // FileName itself
		ms.Write(salt, 0, salt.Length);                    // Salt

		using (CryptoStream? cs = new(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
		{
			cs.Write(compressedData, 0, compressedData.Length);
			cs.FlushFinalBlock();
		}

		File.WriteAllBytes(outputFile, ms.ToArray());
		Console.WriteLine($"Compressed and encrypted payload saved to: {outputFile}");
	}
}
