using System.Net;
using System.Net.Sockets;
using System.Text;
using e_suite.Service.Mail.Helper;
using Microsoft.Extensions.Diagnostics.HealthChecks;

namespace eSuite.API.HealthChecks;

/// <summary>
/// Health check used to perform a basic check to see that the smtp server is available.
/// </summary>
public class SmtpHealthCheck
{
    /// <summary>
    /// Returns Health check result for if the smtp server is up and available to connect too.
    /// </summary>
    /// <param name="configuration"></param>
    /// <param name="socketFactory"></param>
    /// <returns></returns>
    public static HealthCheckResult Healthy(IConfiguration configuration, ISocketFactory socketFactory)
    {
        //failure to be able to send an email should result in degraded.

        var host = configuration.GetConfigValue("MAIL_SERVER", "Smtp:Server", "defaultmailserver");
        var port = configuration.GetConfigValue("MAIL_PORT", "Smtp:Port", 25);
        //var fromAddress = configuration.GetConfigValue("MAIL_ADDRESS", "Smtp:FromAddress", "defaultfromaddress");
        //var fromDisplayName = configuration.GetConfigValue("MAIL_NAME", "Smtp:FromDisplayName", "MailService");
        //var username = configuration.GetConfigValue("MAIL_USERNAME", "Smtp:Username", string.Empty);
        //var password = configuration.GetConfigValue("MAIL_PASSWORD", "Smtp:Password", string.Empty);
        //var enableSsl = configuration.GetConfigValue("MAIL_ENABLE_SSL", "Smtp:EnableSSL", false);
        //client.Timeout = _configuration.GetConfigValue("MAIL_TIMEOUT", "Smtp:timeout", 30000);
        var isServerOk = TestConnection(host!, port, socketFactory);
        if (!isServerOk)
            return HealthCheckResult.Unhealthy("Unable to connect to SMTP Server");

        return HealthCheckResult.Healthy();
    }

    /// <summary>
    /// Performs the test to connect to the smtp server to see that the server is functional.
    /// </summary>
    /// <param name="smtpServerAddress"></param>
    /// <param name="port"></param>
    /// <param name="socketFactory"></param>
    /// <returns></returns>
    public static bool TestConnection(string smtpServerAddress, int port, ISocketFactory socketFactory)
    {
        var hostEntry = Dns.GetHostEntry(smtpServerAddress);
        var endPoint = new IPEndPoint(hostEntry.AddressList[0], port);
        using (var tcpSocket = socketFactory.CreateSocket( endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp))
        {
            //try to connect and test the rsponse for code 220 = success
            tcpSocket.Connect(endPoint);
            if (!CheckResponse(tcpSocket, 220))
            {
                return false;
            }

            // send HELO and test the response for code 250 = proper response
            SendData(tcpSocket, $"HELO {Dns.GetHostName()}\r\n");
            if (!CheckResponse(tcpSocket, 250))
            {
                return false;
            }

            // if we got here it's that we can connect to the smtp server
            return true;
        }
    }

    private static void SendData(ISocketFacade socket, string data)
    {
        var dataArray = Encoding.ASCII.GetBytes(data);
        socket.Send(dataArray, 0, dataArray.Length, SocketFlags.None);
    }

    private static bool CheckResponse(ISocketFacade socket, int expectedCode)
    {
        while (socket.Available == 0)
        {
            Thread.Sleep(100);
        }
        var responseArray = new byte[1024];
        socket.Receive(responseArray, 0, socket.Available, SocketFlags.None);
        var responseData = Encoding.ASCII.GetString(responseArray);
        var responseCode = Convert.ToInt32(responseData.Substring(0, 3));
        return responseCode == expectedCode;
    }
}