// Copyright (C) 2026, The Duplicati Team
// https://duplicati.com, hello@duplicati.com
// 
// Permission is hereby granted, free of charge, to any person obtaining a 
// copy of this software and associated documentation files (the "Software"), 
// to deal in the Software without restriction, including without limitation 
// the rights to use, copy, modify, merge, publish, distribute, sublicense, 
// and/or sell copies of the Software, and to permit persons to whom the 
// Software is furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in 
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
// DEALINGS IN THE SOFTWARE.

using System.Security.Cryptography.X509Certificates;
using Duplicati.Library.Logging;

namespace Duplicati.Library.Certificates;

/// <summary>
/// Result of a CA trust installation operation.
/// </summary>
public enum CATrustInstallationStatus
{
    /// <summary>Installation was successful.</summary>
    Success,
    /// <summary>Certificate was already installed.</summary>
    AlreadyInstalled,
    /// <summary>Installation requires elevated privileges.</summary>
    RequiresElevation,
    /// <summary>Installation failed.</summary>
    Failed,
    /// <summary>No trust installer available for this platform.</summary>
    NotSupported
}

/// <summary>
/// Result of a CA trust installation attempt.
/// </summary>
public class CATrustInstallationResult
{
    /// <summary>
    /// The status of the installation.
    /// </summary>
    public CATrustInstallationStatus Status { get; set; }

    /// <summary>
    /// An optional error message if installation failed.
    /// </summary>
    public string? ErrorMessage { get; set; }
}

/// <summary>
/// Status information about the current certificate configuration.
/// </summary>
public class CertificateStatusInfo
{
    /// <summary>
    /// The CA certificate, if available.
    /// </summary>
    public X509Certificate2? CACert { get; set; }

    /// <summary>
    /// The server certificate (with private key), if available.
    /// </summary>
    public X509Certificate2? ServerCert { get; set; }

    /// <summary>
    /// Whether the certificate was autogenerated.
    /// </summary>
    public bool IsAutogenerated { get; set; }

    /// <summary>
    /// Whether the CA certificate is installed in the system trust store.
    /// </summary>
    public bool IsCATrusted { get; set; }

    /// <summary>
    /// Days until the CA certificate expires, or null if no CA certificate.
    /// </summary>
    public double? CaDaysUntilExpiry { get; set; }

    /// <summary>
    /// Days until the server certificate expires, or null if no server certificate.
    /// </summary>
    public double? ServerDaysUntilExpiry { get; set; }

    /// <summary>
    /// DNS names from the server certificate's Subject Alternative Names.
    /// </summary>
    public List<string> DnsNames { get; set; } = new();

    /// <summary>
    /// IP addresses from the server certificate's Subject Alternative Names.
    /// </summary>
    public List<string> IpAddresses { get; set; } = new();

    /// <summary>
    /// Whether database field encryption is enabled.
    /// </summary>
    public bool IsEncryptingFields { get; set; }
}

/// <summary>
/// Helper class for certificate configuration operations.
/// Provides high-level operations for generating, renewing, and managing HTTPS certificates.
/// </summary>
public static class CertificateConfigurationHelper
{
    /// <summary>
    /// The log tag for messages from this class.
    /// </summary>
    private static readonly string LOGTAG = Log.LogTagFromType(typeof(CertificateConfigurationHelper));

    /// <summary>
    /// Installs a CA certificate in the system trust store.
    /// </summary>
    /// <param name="caCert">The CA certificate to install.</param>
    /// <param name="storeLocation">The store location to use (Windows only). If null, auto-detects based on privileges.</param>
    /// <param name="linuxCertDirectory">The certificate directory to use (Linux only). If null, uses the default system location.</param>
    /// <param name="macOSKeychainPath">The keychain path to use (macOS only). If null, uses the default login keychain.</param>
    /// <returns>The result of the installation attempt.</returns>
    public static CATrustInstallationResult InstallCATrust(X509Certificate2 caCert, StoreLocation? storeLocation, string? linuxCertDirectory, string? macOSKeychainPath)
    {
        var installer = CATrustInstallerFactory.CreateInstaller(storeLocation, linuxCertDirectory, macOSKeychainPath);
        if (installer == null)
        {
            return new CATrustInstallationResult
            {
                Status = CATrustInstallationStatus.NotSupported,
                ErrorMessage = "No trust installer available for this platform."
            };
        }

        var result = installer.Install(caCert);
        return new CATrustInstallationResult
        {
            Status = result switch
            {
                TrustInstallationResult.Success => CATrustInstallationStatus.Success,
                TrustInstallationResult.AlreadyInstalled => CATrustInstallationStatus.AlreadyInstalled,
                TrustInstallationResult.RequiresElevation => CATrustInstallationStatus.RequiresElevation,
                _ => CATrustInstallationStatus.Failed
            }
        };
    }

    /// <summary>
    /// Checks if a CA certificate is installed in the system trust store.
    /// </summary>
    /// <param name="caCert">The CA certificate to check.</param>
    /// <param name="storeLocation">The store location to use (Windows only). If null, auto-detects based on privileges.</param>
    /// <param name="linuxCertDirectory">The certificate directory to use (Linux only). If null, uses the default system location.</param>
    /// <param name="macOSKeychainPath">The keychain path to use (macOS only). If null, uses the default login keychain.</param>
    /// <returns>True if the certificate is installed; false otherwise.</returns>
    public static bool IsCATrustInstalled(X509Certificate2 caCert, StoreLocation? storeLocation, string? linuxCertDirectory, string? macOSKeychainPath)
    {
        var installer = CATrustInstallerFactory.CreateInstaller(storeLocation, linuxCertDirectory, macOSKeychainPath);
        return installer != null && installer.IsInstalled(caCert);
    }

    /// <summary>
    /// Removes a CA certificate from the system trust store.
    /// </summary>
    /// <param name="caCert">The CA certificate to remove.</param>
    /// <param name="storeLocation">The store location to use (Windows only). If null, auto-detects based on privileges.</param>
    /// <param name="linuxCertDirectory">The certificate directory to use (Linux only). If null, uses the default system location.</param>
    /// <param name="macOSKeychainPath">The keychain path to use (macOS only). If null, uses the default login keychain.</param>
    /// <returns>True if removal was successful or certificate was not installed; false otherwise.</returns>
    public static bool RemoveCATrust(X509Certificate2 caCert, StoreLocation? storeLocation, string? linuxCertDirectory, string? macOSKeychainPath)
    {
        var installer = CATrustInstallerFactory.CreateInstaller(storeLocation, linuxCertDirectory, macOSKeychainPath);
        if (installer == null)
            return false;

        var result = installer.Uninstall(caCert);
        return result is TrustUninstallationResult.Success or TrustUninstallationResult.NotInstalled;
    }

    /// <summary>
    /// Ensures HTTPS certificates are available and valid.
    /// If valid certificates already exist, returns success without changes.
    /// If a CA exists but the server certificate is expiring, renews the server certificate using the existing CA.
    /// If no CA exists, generates new CA and server certificates.
    /// </summary>
    /// <param name="existingIsAutogenerated">Whether existing certificate was autogenerated.</param>
    /// <param name="existingServerCert">The existing server certificate collection.</param>
    /// <param name="existingCaData">The existing CA certificate data.</param>
    /// <param name="hostnames">Optional comma-separated list of hostnames. If null, auto-detected hostnames are used.</param>
    /// <param name="skipTrustInstallation">If true, skips installing the CA in the system trust store.</param>
    /// <param name="storeLocation">The store location to use for trust installation (Windows only).</param>
    /// <param name="linuxCertDirectory">The certificate directory to use for trust installation (Linux only).</param>
    /// <param name="macOSKeychainPath">The keychain path to use for trust installation (macOS only).</param>
    /// <returns>A result object containing success status, certificate data, and trust installation status.</returns>
    public static CertificateGenerationResult EnsureCertificates(
        bool existingIsAutogenerated,
        X509Certificate2Collection? existingServerCert,
        CACertificateData? existingCaData,
        string? hostnames = null,
        bool skipTrustInstallation = false,
        StoreLocation? storeLocation = null,
        string? linuxCertDirectory = null,
        string? macOSKeychainPath = null)
    {
        var result = new CertificateGenerationResult();

        try
        {
            // Check if valid certificates already exist
            if (CertificateRenewalChecker.HasValidCertificates(existingIsAutogenerated, existingServerCert, existingCaData))
            {
                var serverCertWithKey = existingServerCert!.Cast<X509Certificate2>().FirstOrDefault(c => c.HasPrivateKey);
                var daysUntilExpiry = (serverCertWithKey!.NotAfter - DateTime.UtcNow).TotalDays;
                Log.WriteInformationMessage(LOGTAG, "CertificatesExist", $"Valid certificates already exist (expires in {daysUntilExpiry:F0} days).");
                result.Success = true;
                return result;
            }

            // If a CA already exists, try to renew the server certificate using the existing CA
            if (existingCaData != null)
            {
                Log.WriteInformationMessage(LOGTAG, "ExistingCAFound", "Existing CA found, attempting to renew server certificate...");
                var renewResult = RenewServerCertificate(existingCaData, hostnames);

                if (renewResult.Renewed && renewResult.RenewedCertificate != null)
                {
                    Log.WriteInformationMessage(LOGTAG, "ServerCertRenewed", "Server certificate renewed using existing CA.");
                    result.Success = true;
                    result.ServerCertificate = renewResult.RenewedCertificate;
                    return result;
                }

                // If renewal failed (e.g., CA expired), fall through to generate new CA + server cert
                Log.WriteWarningMessage(LOGTAG, "RenewalFailed", null, $"Could not renew with existing CA: {renewResult.RenewalFailedReason}. Generating new CA and server certificate...");
            }

            // No valid CA exists or renewal failed — generate new CA and server certificates
            return GenerateCertificatesCore(hostnames, skipTrustInstallation, storeLocation, linuxCertDirectory, macOSKeychainPath);
        }
        catch (Exception ex)
        {
            Log.WriteErrorMessage(LOGTAG, "EnsureCertificatesFailed", ex, $"Failed to ensure HTTPS certificates: {ex.Message}");
            result.Success = false;
            result.ErrorMessage = ex.Message;
            return result;
        }
    }

    /// <summary>
    /// Generates new CA and server certificates for HTTPS.
    /// If valid certificates already exist, returns success without generating new ones.
    /// </summary>
    /// <param name="existingIsAutogenerated">Whether existing certificate was autogenerated.</param>
    /// <param name="existingServerCert">The existing server certificate collection.</param>
    /// <param name="existingCaData">The existing CA certificate data.</param>
    /// <param name="hostnames">Optional comma-separated list of hostnames. If null, auto-detected hostnames are used.</param>
    /// <param name="skipTrustInstallation">If true, skips installing the CA in the system trust store.</param>
    /// <param name="storeLocation">The store location to use for trust installation (Windows only).</param>
    /// <param name="linuxCertDirectory">The certificate directory to use for trust installation (Linux only).</param>
    /// <param name="macOSKeychainPath">The keychain path to use for trust installation (macOS only).</param>
    /// <returns>A result object containing success status, certificate data, and trust installation status.</returns>
    public static CertificateGenerationResult GenerateCertificates(
        bool existingIsAutogenerated,
        X509Certificate2Collection? existingServerCert,
        CACertificateData? existingCaData,
        string? hostnames = null,
        bool skipTrustInstallation = false,
        StoreLocation? storeLocation = null,
        string? linuxCertDirectory = null,
        string? macOSKeychainPath = null)
    {
        var result = new CertificateGenerationResult();

        try
        {
            // Check if valid certificates already exist
            if (CertificateRenewalChecker.HasValidCertificates(existingIsAutogenerated, existingServerCert, existingCaData))
            {
                var serverCertWithKey = existingServerCert!.Cast<X509Certificate2>().FirstOrDefault(c => c.HasPrivateKey);
                var daysUntilExpiry = (serverCertWithKey!.NotAfter - DateTime.UtcNow).TotalDays;
                Log.WriteInformationMessage(LOGTAG, "CertificatesExist", $"Valid certificates already exist (expires in {daysUntilExpiry:F0} days).");
                result.Success = true;
                return result;
            }

            // Generate new CA and server certificates
            return GenerateCertificatesCore(hostnames, skipTrustInstallation, storeLocation, linuxCertDirectory, macOSKeychainPath);
        }
        catch (Exception ex)
        {
            Log.WriteErrorMessage(LOGTAG, "CertificateGenerationFailed", ex, $"Failed to generate HTTPS certificates: {ex.Message}");
            result.Success = false;
            result.ErrorMessage = ex.Message;
            return result;
        }
    }

    /// <summary>
    /// Regenerates the CA and server certificates, optionally removing the old CA from the trust store first.
    /// Unlike <see cref="GenerateCertificates"/>, this always generates new certificates regardless of existing validity.
    /// </summary>
    /// <param name="existingCaCertBase64">The existing CA certificate in Base64 format, for removal from trust store. Can be null.</param>
    /// <param name="hostnames">Optional comma-separated list of hostnames. If null, auto-detected hostnames are used.</param>
    /// <param name="skipTrustInstallation">If true, skips installing the CA in the system trust store.</param>
    /// <param name="storeLocation">The store location to use for trust installation (Windows only).</param>
    /// <param name="linuxCertDirectory">The certificate directory to use for trust installation (Linux only).</param>
    /// <param name="macOSKeychainPath">The keychain path to use for trust installation (macOS only).</param>
    /// <returns>A result object containing success status, certificate data, trust installation status, and old CA removal status.</returns>
    public static CertificateGenerationResult RegenerateCACertificates(
        string? existingCaCertBase64,
        string? hostnames = null,
        bool skipTrustInstallation = false,
        StoreLocation? storeLocation = null,
        string? linuxCertDirectory = null,
        string? macOSKeychainPath = null)
    {
        var result = new CertificateGenerationResult();

        try
        {
            // Remove old CA from trust store if it exists
            if (!string.IsNullOrWhiteSpace(existingCaCertBase64) && !skipTrustInstallation)
            {
                try
                {
                    var existingCaCert = CertificateStorageHelper.DeserializeCertificate(existingCaCertBase64);
                    Log.WriteInformationMessage(LOGTAG, "RemovingOldCA", "Removing old CA certificate from trust store...");

                    if (IsCATrustInstalled(existingCaCert, storeLocation, linuxCertDirectory, macOSKeychainPath))
                    {
                        if (RemoveCATrust(existingCaCert, storeLocation, linuxCertDirectory, macOSKeychainPath))
                            Log.WriteInformationMessage(LOGTAG, "OldCARemoved", "Old CA certificate removed from trust store.");
                        else
                            Log.WriteWarningMessage(LOGTAG, "OldCARemovalFailed", null, "Failed to remove old CA certificate from trust store.");
                    }
                    else
                    {
                        Log.WriteInformationMessage(LOGTAG, "OldCANotInTrustStore", "Old CA certificate was not in trust store.");
                    }
                }
                catch (Exception ex)
                {
                    Log.WriteWarningMessage(LOGTAG, "OldCARemovalError", ex, $"Could not remove old CA from trust store: {ex.Message}");
                }
            }

            // Generate new CA and server certificates (always, no validity check)
            return GenerateCertificatesCore(hostnames, skipTrustInstallation, storeLocation, linuxCertDirectory, macOSKeychainPath);
        }
        catch (Exception ex)
        {
            Log.WriteErrorMessage(LOGTAG, "CertificateRegenerationFailed", ex, $"Failed to regenerate HTTPS certificates: {ex.Message}");
            result.Success = false;
            result.ErrorMessage = ex.Message;
            return result;
        }
    }

    /// <summary>
    /// Renews the server certificate using an existing CA certificate.
    /// This always generates a new server certificate regardless of the current certificate's validity.
    /// </summary>
    /// <param name="caData">The CA certificate data to use for signing the new server certificate.</param>
    /// <param name="hostnames">Optional comma-separated list of hostnames. If null, auto-detected hostnames are used.</param>
    /// <returns>A result object containing renewal status and new certificate data if renewed.</returns>
    public static CertificateRenewalResult RenewServerCertificate(CACertificateData caData, string? hostnames = null)
    {
        var result = new CertificateRenewalResult();

        try
        {
            // Deserialize CA certificate pair
            CACertificatePair caPair;
            try
            {
                caPair = CertificateStorageHelper.DeserializeCACertificatePair(
                    caData.CACertificate,
                    caData.CAKey,
                    caData.CAPassword);
            }
            catch (Exception ex)
            {
                Log.WriteErrorMessage(LOGTAG, "CADeserializationFailed", ex, "Failed to deserialize CA certificate for renewal.");
                result.RenewalFailedReason = $"Failed to deserialize CA certificate: {ex.Message}";
                return result;
            }

            try
            {
                // Check if CA itself is expired
                if (caPair.Certificate.NotAfter < DateTime.UtcNow)
                {
                    Log.WriteErrorMessage(LOGTAG, "CAExpired", null, "Cannot renew certificate: CA certificate has expired.");
                    result.RenewalFailedReason = "CA certificate has expired.";
                    return result;
                }

                // Parse hostnames
                var hostnameList = CertificateRenewalChecker.ParseHostnames(hostnames);
                Log.WriteInformationMessage(LOGTAG, "GeneratingCertificate", $"Generating new server certificate with hostnames: {string.Join(", ", hostnameList)}");

                var newServerPair = CertificateGenerator.GenerateServerCertificate(caPair.Certificate, caPair.PrivateKey, hostnameList);

                try
                {
                    // Generate new password for PFX
                    var pfxPassword = CertificateStorageHelper.GeneratePfxPassword();

                    // Create PFX bundle
                    var pfxBytes = CertificateGenerator.CreatePfxBundle(newServerPair, caPair, pfxPassword);
                    var pfxBase64 = Convert.ToBase64String(pfxBytes);

                    Log.WriteInformationMessage(LOGTAG, "CertificateRenewed", $"Server certificate renewed successfully. New certificate expires: {newServerPair.Certificate.NotAfter:yyyy-MM-dd}");

                    result.Renewed = true;
                    result.RenewedCertificate = new ServerCertificateData
                    {
                        ServerCertificate = pfxBase64,
                        Password = pfxPassword,
                        Autogenerated = true
                    };

                    return result;
                }
                finally
                {
                    // Dispose server certificate key material promptly
                    newServerPair.PrivateKey.Dispose();
                    newServerPair.Certificate.Dispose();
                }
            }
            finally
            {
                // Dispose CA certificate key material promptly
                caPair.PrivateKey.Dispose();
                caPair.Certificate.Dispose();
            }
        }
        catch (Exception ex)
        {
            Log.WriteErrorMessage(LOGTAG, "RenewalFailed", ex, $"Failed to renew SSL certificate: {ex.Message}");
            result.RenewalFailedReason = ex.Message;
            return result;
        }
    }

    /// <summary>
    /// Gets the current certificate status information.
    /// </summary>
    /// <param name="caCertBase64">The CA certificate in Base64 format.</param>
    /// <param name="serverCertCollection">The server certificate collection.</param>
    /// <param name="isAutogenerated">Whether the certificate was autogenerated.</param>
    /// <param name="storeLocation">The store location to check (Windows only).</param>
    /// <param name="linuxCertDirectory">The certificate directory to check (Linux only).</param>
    /// <param name="macOSKeychainPath">The keychain path to check (macOS only).</param>
    /// <returns>A status info object with certificate details.</returns>
    public static CertificateStatusInfo GetCertificateStatus(
        string? caCertBase64,
        X509Certificate2Collection? serverCertCollection,
        bool isAutogenerated,
        StoreLocation? storeLocation = null,
        string? linuxCertDirectory = null,
        string? macOSKeychainPath = null)
    {
        var status = new CertificateStatusInfo
        {
            IsAutogenerated = isAutogenerated
        };

        // Parse CA certificate
        if (!string.IsNullOrWhiteSpace(caCertBase64))
        {
            try
            {
                var caCert = CertificateStorageHelper.DeserializeCertificate(caCertBase64);
                status.CACert = caCert;
                status.CaDaysUntilExpiry = (caCert.NotAfter - DateTime.Now).TotalDays;
                status.IsCATrusted = IsCATrustInstalled(caCert, storeLocation, linuxCertDirectory, macOSKeychainPath);
            }
            catch (Exception ex)
            {
                Log.WriteWarningMessage(LOGTAG, "CAStatusError", ex, $"Error reading CA certificate: {ex.Message}");
            }
        }

        // Parse server certificate
        if (serverCertCollection != null)
        {
            try
            {
                var serverCert = serverCertCollection.Cast<X509Certificate2>().FirstOrDefault(c => c.HasPrivateKey);
                if (serverCert != null)
                {
                    status.ServerCert = serverCert;
                    status.ServerDaysUntilExpiry = (serverCert.NotAfter - DateTime.Now).TotalDays;

                    // Extract SANs
                    var sanExtension = serverCert.Extensions["2.5.29.17"];
                    if (sanExtension is X509SubjectAlternativeNameExtension sanExt)
                    {
                        status.DnsNames = sanExt.EnumerateDnsNames().ToList();
                        status.IpAddresses = sanExt.EnumerateIPAddresses().Select(ip => ip.ToString()).ToList();
                    }
                }
            }
            catch (Exception ex)
            {
                Log.WriteWarningMessage(LOGTAG, "ServerCertStatusError", ex, $"Error reading server certificate: {ex.Message}");
            }
        }

        return status;
    }

    /// <summary>
    /// Core certificate generation logic shared by <see cref="GenerateCertificates"/> and <see cref="RegenerateCACertificates"/>.
    /// </summary>
    private static CertificateGenerationResult GenerateCertificatesCore(
        string? hostnames,
        bool skipTrustInstallation,
        StoreLocation? storeLocation,
        string? linuxCertDirectory,
        string? macOSKeychainPath)
    {
        var result = new CertificateGenerationResult();

        // Parse hostnames
        var hostnameList = CertificateRenewalChecker.ParseHostnames(hostnames);
        Log.WriteInformationMessage(LOGTAG, "GeneratingCertificates", $"Generating HTTPS certificates with hostnames: {string.Join(", ", hostnameList)}");

        // Generate CA certificate
        Log.WriteInformationMessage(LOGTAG, "GeneratingCA", "Generating CA certificate...");
        var caPair = CertificateGenerator.GenerateCACertificate();

        try
        {
            // Generate server certificate
            Log.WriteInformationMessage(LOGTAG, "GeneratingServerCert", "Generating server certificate...");
            var serverPair = CertificateGenerator.GenerateServerCertificate(caPair.Certificate, caPair.PrivateKey, hostnameList);

            try
            {
                // Generate separate passwords for CA key encryption and server PFX
                var caPassword = CertificateStorageHelper.GeneratePfxPassword();
                var pfxPassword = CertificateStorageHelper.GeneratePfxPassword();

                // Install CA in trust store if not skipped
                if (!skipTrustInstallation)
                {
                    Log.WriteInformationMessage(LOGTAG, "InstallingCA", "Installing CA certificate in system trust store...");
                    var installResult = InstallCATrust(caPair.Certificate, storeLocation, linuxCertDirectory, macOSKeychainPath);
                    result.TrustInstallationStatus = installResult.Status;

                    switch (installResult.Status)
                    {
                        case CATrustInstallationStatus.Success:
                            Log.WriteInformationMessage(LOGTAG, "CAInstalled", "CA certificate installed successfully.");
                            break;
                        case CATrustInstallationStatus.AlreadyInstalled:
                            Log.WriteInformationMessage(LOGTAG, "CAAlreadyInstalled", "CA certificate was already installed.");
                            break;
                        case CATrustInstallationStatus.NotSupported:
                            Log.WriteWarningMessage(LOGTAG, "NoTrustInstaller", null, "No trust installer available for this platform.");
                            break;
                        case CATrustInstallationStatus.RequiresElevation:
                            Log.WriteWarningMessage(LOGTAG, "CARequiresElevation", null, "Administrator/root privileges required to install CA certificate.");
                            break;
                        case CATrustInstallationStatus.Failed:
                            Log.WriteWarningMessage(LOGTAG, "CAInstallFailed", null, "Failed to install CA certificate.");
                            break;
                    }
                }
                else
                {
                    Log.WriteInformationMessage(LOGTAG, "SkippingCATrust", "Skipping CA trust installation.");
                }

                // Serialize certificates
                Log.WriteInformationMessage(LOGTAG, "StoringCertificates", "Serializing certificates...");
                var (caCertBase64, caKeyEncrypted) = CertificateStorageHelper.SerializeCACertificatePair(caPair, caPassword);
                var pfxBytes = CertificateGenerator.CreatePfxBundle(serverPair, caPair, pfxPassword);
                var pfxBase64 = Convert.ToBase64String(pfxBytes);

                Log.WriteInformationMessage(LOGTAG, "CertificatesGenerated", "HTTPS certificates generated successfully.");

                result.Success = true;
                result.CACertificate = new CACertificateData
                {
                    CACertificate = caCertBase64,
                    CAKey = caKeyEncrypted,
                    CAPassword = caPassword
                };
                result.ServerCertificate = new ServerCertificateData
                {
                    ServerCertificate = pfxBase64,
                    Password = pfxPassword,
                    Autogenerated = true
                };

                return result;
            }
            finally
            {
                // Dispose server certificate key material promptly
                serverPair.PrivateKey.Dispose();
                serverPair.Certificate.Dispose();
            }
        }
        finally
        {
            // Dispose CA certificate key material promptly
            caPair.PrivateKey.Dispose();
            caPair.Certificate.Dispose();
        }
    }
}
