using NUnit.Framework;
using System;
using System.Linq;
using Vanara.Extensions;
using Vanara.InteropServices;
using static Vanara.PInvoke.AdvApi32;

namespace Vanara.PInvoke.Tests
{
	[TestFixture()]
	public class WinCredTests
	{
		public static readonly CREDENTIAL_MGD genCred = CredEnumerate().First(f => f.Type == CRED_TYPE.CRED_TYPE_GENERIC);

		[Test]
		public void CredEnumerateTest()
		{
			var a = CredEnumerate();
			TestContext.WriteLine(string.Join("\r\n", a.Select(c => $"{c.UserName} ({c.Type}) => {c.TargetName}")));
			Assert.That(a, Is.Not.Empty);
		}

		[Test]
		public void CredFindBestCredentialTest()
		{
			Assert.That(CredFindBestCredential(genCred.TargetName, genCred.Type, out var cred), ResultIs.Successful);
			Assert.That(cred.UserName, Is.EqualTo(genCred.UserName));
		}

		[Test]
		public void CredGetSessionTypesTest()
		{
			var cp = new CRED_PERSIST[(int)CRED_TYPE.CRED_TYPE_MAXIMUM];
			Assert.That(CredGetSessionTypes((uint)cp.Length, cp), ResultIs.Successful);
			cp.WriteValues();
		}

		[Test]
		public void CredGetTargetInfoTest()
		{
			// TODO: I have no idea how to get this to not return ERROR_NOT_FOUND
			//Assert.That(CredGetTargetInfo(@"https://github.com/", 0, out var mem), ResultIs.Successful);
		}

		[Test]
		public void CredMarshalCredentialTest()
		{
			const string un = "dahall@github.com";
			var utc = new USERNAME_TARGET_CREDENTIAL_INFO { UserName = un };
			Assert.That(CredMarshalCredential(utc, out var cred), ResultIs.Successful);
			Assert.That(cred, Is.Not.Null);
			TestContext.Write(cred);

			Assert.That(CredIsMarshaledCredential(cred), Is.True);

			Assert.That(CredUnmarshalCredential(cred, out var type, out var mem), ResultIs.Successful);
			Assert.That(type, Is.EqualTo(CRED_MARSHAL_TYPE.UsernameTargetCredential));
			Assert.That(mem.DangerousGetHandle().ToStructure<USERNAME_TARGET_CREDENTIAL_INFO>().UserName, Is.EqualTo(un));
			mem.Dispose();
		}

		[Test]
		public void CredReadTest()
		{
			Assert.That(CredRead(genCred.TargetName, genCred.Type, out var cred), ResultIs.Successful);
			Assert.That(cred.UserName, Is.EqualTo(genCred.UserName));
		}

		[Test]
		public void CredWriteTest()
		{
			const string targetName = "my.urn.pri";
			using (var target = new SafeCoTaskMemString(targetName, System.Runtime.InteropServices.CharSet.Auto))
			using (var user = new SafeCoTaskMemString("dahall@github.com", System.Runtime.InteropServices.CharSet.Auto))
			using (var pwd = new SafeCoTaskMemString("asldfjua(#)$#$Jdf-0934390".ToSecureString(), System.Runtime.InteropServices.CharSet.Auto))
			{
				var cred = new CREDENTIAL
				{
					Type = CRED_TYPE.CRED_TYPE_GENERIC,
					Persist = CRED_PERSIST.CRED_PERSIST_LOCAL_MACHINE,
					TargetName = (IntPtr)target,
					UserName = (IntPtr)user,
					CredentialBlob = (IntPtr)pwd,
					CredentialBlobSize = pwd.Size,
				};
				Assert.That(CredWrite(cred, 0), ResultIs.Successful);

				try
				{
					//Assert.That(CredGetTargetInfo(target, 1, out var mem), ResultIs.Successful);
					const string credToProtect = targetName;
					var sb = new System.Text.StringBuilder(256);
					var sz = (uint)sb.Capacity;
					Assert.That(CredProtect(true, credToProtect, (uint)credToProtect.Length, sb, ref sz, out var type), ResultIs.Successful);
					TestContext.Write(sb);
					Assert.That(CredIsProtected(sb.ToString(), out var type2), Is.True);
					Assert.That(type, Is.EqualTo(type2));
				}
				finally
				{
					Assert.That(CredDelete(targetName, CRED_TYPE.CRED_TYPE_GENERIC), ResultIs.Successful);
				}
			}
		}
	}
}