using System;
using System.Runtime.InteropServices;
using System.Text;
using AxCopilot.Services;

namespace AxCopilot.Handlers;

public static class CredentialManager
{
	private struct FILETIME
	{
		public uint dwLowDateTime;

		public uint dwHighDateTime;
	}

	[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
	private struct CREDENTIAL
	{
		public uint Flags;

		public uint Type;

		public nint TargetName;

		public nint Comment;

		public FILETIME LastWritten;

		public uint CredentialBlobSize;

		public nint CredentialBlob;

		public uint Persist;

		public uint AttributeCount;

		public nint Attributes;

		public nint TargetAlias;

		public nint UserName;
	}

	private const uint CRED_TYPE_GENERIC = 1u;

	private const uint CRED_PERSIST_LOCAL_MACHINE = 2u;

	[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
	private static extern bool CredRead(string target, uint type, uint flags, out nint credential);

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

	[DllImport("advapi32.dll", SetLastError = true)]
	private static extern void CredFree(nint cred);

	[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
	private static extern bool CredDelete(string target, uint type, uint flags);

	public static string? GetToken(string key)
	{
		if (string.IsNullOrEmpty(key))
		{
			return null;
		}
		try
		{
			if (CredRead(key, 1u, 0u, out var credential))
			{
				try
				{
					CREDENTIAL cREDENTIAL = Marshal.PtrToStructure<CREDENTIAL>(credential);
					if (cREDENTIAL.CredentialBlobSize != 0 && cREDENTIAL.CredentialBlob != IntPtr.Zero)
					{
						return Marshal.PtrToStringUni(cREDENTIAL.CredentialBlob, (int)cREDENTIAL.CredentialBlobSize / 2);
					}
				}
				finally
				{
					CredFree(credential);
				}
			}
		}
		catch (Exception ex)
		{
			LogService.Warn("Windows Credential Manager 읽기 실패 (" + key + "): " + ex.Message);
		}
		return Environment.GetEnvironmentVariable(key.ToUpperInvariant());
	}

	public static void SetToken(string key, string token)
	{
		if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(token))
		{
			return;
		}
		byte[] bytes = Encoding.Unicode.GetBytes(token);
		nint num = Marshal.AllocHGlobal(bytes.Length);
		nint num2 = Marshal.StringToCoTaskMemUni(key);
		nint num3 = Marshal.StringToCoTaskMemUni(Environment.UserName);
		try
		{
			Marshal.Copy(bytes, 0, num, bytes.Length);
			CREDENTIAL userCredential = new CREDENTIAL
			{
				Type = 1u,
				TargetName = num2,
				UserName = num3,
				CredentialBlobSize = (uint)bytes.Length,
				CredentialBlob = num,
				Persist = 2u
			};
			if (!CredWrite(ref userCredential, 0u))
			{
				LogService.Error($"토큰 저장 실패: {key}, 오류 코드: {Marshal.GetLastWin32Error()}");
			}
			else
			{
				LogService.Info("토큰 저장 완료: " + key);
			}
		}
		finally
		{
			Marshal.FreeHGlobal(num);
			Marshal.FreeCoTaskMem(num2);
			Marshal.FreeCoTaskMem(num3);
		}
	}

	public static bool DeleteToken(string key)
	{
		return !string.IsNullOrEmpty(key) && CredDelete(key, 1u, 0u);
	}
}
