// Install package System.Runtime.InteropServices

using System.Net;
using System.Runtime.InteropServices;
using System.Security;

public static class WindowsCredentialVault
{
	public static bool WriteCredential(string credentialName, string username, SecureString password)
	{
		// Encrypt the password
		password.MakeReadOnly();
		var credentialBlob = new byte[password.Length * 2];
		Marshal.Copy(Marshal.SecureStringToGlobalAllocUnicode(password), credentialBlob, 0,
			credentialBlob.Length);

		// Create the Credential struct
		var credential = new Credential
		{
			Flags = 0,
			Type = CredentialType.Generic,
			TargetName = Marshal.StringToCoTaskMemUni(credentialName),
			Comment = nint.Zero,
			LastWritten = new System.Runtime.InteropServices.ComTypes.FILETIME(),
			CredentialBlobSize = (uint)credentialBlob.Length,
			CredentialBlob = Marshal.AllocHGlobal(credentialBlob.Length),
			Persist = (uint)CredentialPersist.LocalMachine,
			AttributeCount = 0,
			Attributes = nint.Zero,
			TargetAlias = nint.Zero,
			UserName = Marshal.StringToCoTaskMemUni(username),
		};
		
		Marshal.Copy(credentialBlob, 0, credential.CredentialBlob, credentialBlob.Length);

		// Write the credential to the Windows Credential Manager
		var success = CredWrite(ref credential, 0);

		// Free the allocated memory
		Marshal.ZeroFreeGlobalAllocUnicode(credential.TargetName);
		Marshal.ZeroFreeGlobalAllocUnicode(credential.UserName);
		Marshal.FreeHGlobal(credential.CredentialBlob);

		return success;
	}

	public static NetworkCredential ReadCredential(string credentialName)
	{
		// Read the credential from the Windows Credential Manager
		var success = CredRead(credentialName, CredentialType.Generic, 0, out var credentialPtr);

		if (success)
		{
			var credential = (Credential)Marshal.PtrToStructure(credentialPtr, typeof(Credential));
			var username = Marshal.PtrToStringUni(credential.UserName);
			var password = Marshal.PtrToStringUni(credential.CredentialBlob, (int)credential.CredentialBlobSize / 2);

			// Free the allocated memory
			CredFree(credentialPtr);

			return new NetworkCredential(username, password);
		}

		var errorCode = Marshal.GetLastWin32Error();
		if (errorCode == 1168)
			throw new InvalidOperationException("The credential was not found.");

		throw new InvalidOperationException($"Failed to read credential: Error code {errorCode}");
	}

	[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
	public struct Credential
	{
		public uint Flags;
		public CredentialType Type;
		public nint TargetName;
		public nint Comment;
		public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
		public uint CredentialBlobSize;
		public nint CredentialBlob;
		public uint Persist;
		public uint AttributeCount;
		public nint Attributes;
		public nint TargetAlias;
		public nint UserName;
	}

	public enum CredentialPersist : uint
	{
		Session = 1,
		LocalMachine = 2,
		Enterprise = 3,
	}

	public enum CredentialType : uint
	{
		Generic = 1,
		DomainPassword = 2,
		DomainCertificate = 3,
		DomainVisiblePassword = 4,
		GenericCertificate = 5,
		DomainExtended = 6,
		Maximum = 7,
		MaximumEx = (Maximum + 1000),
	}

	[DllImport("Advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
	private static extern bool CredRead(string target, CredentialType type, int reservedFlag, out nint credential);

	[DllImport("Advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
	private static extern bool CredWrite([In] ref Credential userCredential, [In] uint flags);

	[DllImport("Advapi32.dll", SetLastError = true)]
	private static extern void CredFree(nint credential);
}