using System.Security.Cryptography;
using System.Text;
using Microsoft.Extensions.Configuration;

namespace PhotoTkutimes.Models
{
    /// <summary>
    /// 提供 DES 字串加解密功能。
    /// </summary>
    public class Security
    {
        private static string? _configuredKey;
        private static string? _configuredIV;

        private string _Key;
        private string _IV;

        /// <summary>
        /// 加密金鑰（8 個 ASCII 字元）。
        /// </summary>
        public string Key
        {
            set => _Key = ValidateEightByteValue(value, nameof(Key));
        }

        /// <summary>
        /// 初始化向量（8 個 ASCII 字元）。
        /// </summary>
        public string IV
        {
            set => _IV = ValidateEightByteValue(value, nameof(IV));
        }

        /// <summary>
        /// 從應用程式設定初始化預設金鑰及向量。
        /// </summary>
        public static void Configure(IConfiguration configuration)
        {
            ArgumentNullException.ThrowIfNull(configuration);

            _configuredKey = configuration["SecurityKey"];
            _configuredIV = configuration["SecurityIV"];
        }

        /// <summary>
        /// 使用應用程式設定中的金鑰及向量建立執行個體。
        /// </summary>
        public Security()
        {
            if (_configuredKey is null || _configuredIV is null)
            {
                throw new InvalidOperationException("Security 尚未初始化，請先呼叫 Security.Configure。");
            }

            _Key = ValidateEightByteValue(_configuredKey, "SecurityKey");
            _IV = ValidateEightByteValue(_configuredIV, "SecurityIV");
        }

        /// <summary>
        /// 使用指定的金鑰及向量建立執行個體。
        /// </summary>
        public Security(string newKey, string newIV)
        {
            _Key = ValidateEightByteValue(newKey, nameof(newKey));
            _IV = ValidateEightByteValue(newIV, nameof(newIV));
        }

        public string Encrypt(string value)
        {
            return Encrypt(value, _Key, _IV);
        }

        public string Decrypt(string value)
        {
            return Decrypt(value, _Key, _IV);
        }

        private static string Encrypt(string value, string key, string iv)
        {
            StringBuilder result = new StringBuilder();

            using DES des = DES.Create();
            byte[] inputBytes = Encoding.Default.GetBytes(value);
            des.Key = Encoding.ASCII.GetBytes(key);
            des.IV = Encoding.ASCII.GetBytes(iv);

            using MemoryStream memoryStream = new MemoryStream();
            using (CryptoStream cryptoStream = new CryptoStream(
                memoryStream,
                des.CreateEncryptor(),
                CryptoStreamMode.Write))
            {
                cryptoStream.Write(inputBytes, 0, inputBytes.Length);
                cryptoStream.FlushFinalBlock();
            }

            foreach (byte valueByte in memoryStream.ToArray())
            {
                result.AppendFormat("{0:X2}", valueByte);
            }

            return result.ToString();
        }

        private static string Decrypt(string value, string key, string iv)
        {
            using DES des = DES.Create();

            byte[] inputBytes = new byte[value.Length / 2];
            for (int index = 0; index < inputBytes.Length; index++)
            {
                inputBytes[index] = Convert.ToByte(value.Substring(index * 2, 2), 16);
            }

            des.Key = Encoding.ASCII.GetBytes(key);
            des.IV = Encoding.ASCII.GetBytes(iv);

            using MemoryStream memoryStream = new MemoryStream();
            using CryptoStream cryptoStream = new CryptoStream(
                memoryStream,
                des.CreateDecryptor(),
                CryptoStreamMode.Write);

            try
            {
                cryptoStream.Write(inputBytes, 0, inputBytes.Length);
                cryptoStream.FlushFinalBlock();
                return Encoding.Default.GetString(memoryStream.ToArray());
            }
            catch (CryptographicException)
            {
                return "N/A";
            }
        }

        public bool ValidateString(string encryptedValue, string originalValue)
        {
            return Decrypt(encryptedValue, _Key, _IV) == originalValue;
        }

        private static string ValidateEightByteValue(string? value, string settingName)
        {
            if (string.IsNullOrEmpty(value) ||
                value.Length != 8 ||
                value.Any(character => character > 127))
            {
                throw new InvalidOperationException($"{settingName} 必須是 8 個 ASCII 字元。");
            }

            return value;
        }
    }
}
