using KarleyLibrary.Serialization;
using Microsoft.Win32;
using System;
using System.Runtime.Versioning;
#nullable disable

namespace KarleyServiceLibrary
{
    /// <summary>
    /// Stellt eine Klasse zum arbeiten mit der Registry und Objekten dar
    /// </summary>
    [SupportedOSPlatform("windows")]
    public class RegistryData
    {
        /// <summary>
        /// Der Key unter dem alle Operationen stattfinden sollen. Default = <see cref="Registry.LocalMachine"/>
        /// </summary>
        public static RegistryKey BaseKey { get; set; } = Registry.LocalMachine;

        /// <summary>
        /// Liest ein komplexes Objekt T aus dem Pfad und dem Subkey aus.
        /// </summary>
        /// <typeparam name="T">Der Typ des zu lesenden Objekt</typeparam>
        /// <param name="path">Der Registry Pfad des Keys</param>
        /// <param name="key">Der zu lesende Key</param>
        /// <param name="type">Der Typ mit dem das Objekt serialisiert wurde. Default = <see cref="RegistrySettingsType.XML"/></param>
        /// <returns>Gibt ein Objekt des Typ T zurück, was die deserialisierten Werte des Registry Keys enthält</returns>
        public static (bool success, T element, string message) Read<T>(string path, string key, RegistrySettingsType type) where T : new()
        {
            var (success, element, message) = Read(path, key);

            if (!success)
            {
                return (false, default, message);
            }

            if (element == null)
            {
                return (false, default, "Die gelesenen Daten waren null");
            }

            string data = element as string;

            return type switch
            {
                RegistrySettingsType.XML => XMLWriter.Deserialize<T>(data),
                RegistrySettingsType.JSON => JSONWriter.Deserialize<T>(data),
                _ => XMLWriter.Deserialize<T>(data),
            };
        }

        /// <summary>
        /// Liest ein Objekt aus der Registry aus dem Pfad und dem Key aus
        /// </summary>
        /// <param name="path">Der Pfad unter dem sich der zu lesende Key befindet</param>
        /// <param name="key">Der zu lesende Key</param>
        /// <returns>Gibt ein <see cref="object"/> zurück das den Wert des Keys darstellt</returns>
        public static (bool success, object element, string message) Read(string path, string key)
        {
            if (!Exists(path))
            {
                return (false, null, "Der Registry Pfad konnte nicht gefunden werden");
            }

            if (!Exists(path, key))
            {
                return (false, null, "Der Registry Key konnte nicht gefunden werden");
            }

            return ReadKey(path, key);
        }

        /// <summary>
        /// Serialisiert und schreibt ein komplexes Objekt in einen Registry Key
        /// </summary>
        /// <typeparam name="T">Der Typ des zu schreibenden Objekt</typeparam>
        /// <param name="element">Das Objekt das in der Registry gespeichert werden soll</param>
        /// <param name="path">Der Registry Pfad zu dem zu schreibenden Registry Key</param>
        /// <param name="key">Der zu schreibende Registry Key</param>
        /// <param name="type">Der Serialisierungs Typ mit dem das Objekt serialisiert werden soll. Default = <see cref="RegistrySettingsType.XML"/></param>
        public static (bool success, string message) Write<T>(T element, string path, string key, RegistrySettingsType type) where T : new()
        {
            var (success, data, message) = type switch
            {
                RegistrySettingsType.XML => XMLWriter.Serialize<T>(element),
                RegistrySettingsType.JSON => JSONWriter.Serialize<T>(element),
                _ => XMLWriter.Serialize<T>(element),
            };

            if (success)
            {
                return Write(data, path, key);
            }
            else
            {
                return (false, message);
            }
        }

        /// <summary>
        /// Schreibt ein <see cref="object"/> in die Registry in den Key der dem Pfad untergeordnet ist
        /// </summary>
        /// <param name="element">Das zu schreibende Objekt</param>
        /// <param name="path">Der Pfad unter dem sich der zu schreibende Key befindet</param>
        /// <param name="key">Der zu schreibende Key</param>

        public static (bool success, string message) Write(object element, string path, string key)
        {
            if (!Exists(path))
            {
                var wert = CreatePath(path);
                if (!wert.success)
                {
                    return (false, wert.message);
                }
            }


            /*if (!Exists(path, key))
            {
                var wert = CreateKey(path, key);
                if (!wert.success)
                {
                    return (false,wert.message);
                }
            }*/


            return WriteKey(path, key, element);

        }

        /// <summary>
        /// Prüft ob ein Registry Pfad existiert
        /// </summary>
        /// <param name="path">Der zu prüfende Pfad</param>
        /// <returns>Gibt einen <see cref="bool"/> zurück der angibt ob der Pfad existiert oder nicht</returns>

        public static bool Exists(string path)
        {
            return BaseKey.OpenSubKey(path) != null;
        }

        /// <summary>
        /// Prüft ob ein Registry Key unter einem angegebenen Pfad existiert
        /// </summary>
        /// <param name="path">Der Pfad unter dem der Registry Key angegeben ist</param>
        /// <param name="key">Der zu prüfende Registry Key</param>
        /// <returns>Gibt einen <see cref="bool"/> zurück der angibt ob der Registry Key existiert oder nicht</returns>

        public static bool Exists(string path, string key)
        {
            if (!Exists(path))
            {
                return false;
            }

            return BaseKey.OpenSubKey(path).GetValue(key) != null;
        }

        /// <summary>
        /// Erstellt einen Registry Pfad
        /// </summary>
        /// <param name="path">Der zu erstellende Registry Pfad</param>
        /// <returns>Gibt einen <see cref="RegistryKey"/> zurück der den erstellten Pfad darstellt</returns>

        private static (bool success, RegistryKey key, string message) CreatePath(string path)
        {
            try
            {
                RegistryKey key = BaseKey.CreateSubKey(path, RegistryKeyPermissionCheck.ReadWriteSubTree);
                return (true, key, "Erfolgreich Key erstellt");
            }
            catch (Exception ex)
            {
                return (false, null, ex.Message);
            }
        }



        /// <summary>
        /// Liest den Wert eines Registry Keys aus einem angegebenen Pfad aus
        /// </summary>
        /// <param name="path">Der Pfad unter dem der Registry Key angegeben ist</param>
        /// <param name="key">Der zu lesende Registry Key</param>
        /// <returns>Gibt ein Objekt zurück das den Wert des Registry Key darstellt</returns>

        private static (bool success, object element, string message) ReadKey(string path, string key)
        {
            try
            {
                RegistryKey regKey = BaseKey.OpenSubKey(path);
                return (true, regKey.GetValue(key), "Registry Key erfolgreich gelesen");
            }
            catch (Exception ex)
            {
                return (false, null, ex.Message);
            }
        }
        /// <summary>
        /// Schreibt ein Objekt in einen Registry Key unter dem angegebenen Pfad
        /// </summary>
        /// <param name="path">Der Pfad unter dem sich der Registry Key befindet</param>
        /// <param name="key">Der zu schreibende Registry Key</param>
        /// <param name="obj">Der Wert der in den Registry Key geschrieben werden soll</param>

        private static (bool succes, string message) WriteKey(string path, string key, object obj)
        {
            try
            {
                RegistryKey regkey = BaseKey.OpenSubKey(path, true);
                regkey.SetValue(key, obj);
                return (true, "Erfolgreich geschrieben");
            }
            catch (Exception ex)
            {
                return (false, ex.Message);
            }


        }
    }

    /// <summary>
    /// Die Art mit der Komplexe Objekte in der Registry Serialisiert werden sollen
    /// </summary>
    public enum RegistrySettingsType
    {
        /// <summary>
        /// Gibt an das ein Komplexes Objekt in eine XML Serialisiert werden soll
        /// </summary>
        XML,
        /// <summary>
        /// Gibt an das ein Komplexes Objekt in einen JSON Serialisiert werden soll
        /// </summary>
        JSON
    }
}
