Imports Independentsoft.Email
Imports Independentsoft.Email.Imap
Imports Independentsoft.Email.Mime
Imports DigitalData.EMLProfiler.ClassCurrent
Imports DigitalData.Modules.Logging
Imports AE
Imports System.Net
Imports System.Reflection
Imports System.IO
Imports DigitalData.Modules.Database

Public Class clsEmailIMAP
    Private Shared Logger As DigitalData.Modules.Logging.Logger
    Private Shared LogConfig As DigitalData.Modules.Logging.LogConfig
    Private _DB_MSSQL As clsDatabase
    Sub New(LogConf As LogConfig, ECMConnectionString As String)
        LogConfig = LogConf
        Logger = LogConf.GetLogger
        _DB_MSSQL = New clsDatabase(LogConf, ECMConnectionString)
    End Sub
    'Private Shared Sub OnWriteLog(ByVal sender As Object, ByVal e As WriteLogEventArgs)
    '    Logger.Info(e.Log)
    'End Sub

    Public Function FetchIMAPMessagesS22(Server As String, Port As Integer, Username As String, Password As String, pInbox As String, Optional IsTest As Boolean = False, Optional DeleteinTest As Boolean = False, Optional MoveMailTo As String = "") As Boolean
        Logger.Debug("FetchIMAPMessagesS22 - Connecting to Server {0}:{1} with user {2}", Server, Port, Username)
        Try
            Logger.Debug("FetchIMAPMessagesS22 - Connecting...")
            Using oClient As New S22.Imap.ImapClient(Server, Port, Username, Password, S22.Imap.AuthMethod.Login, True)
                If Not oClient.Authed Then
                    Logger.Warn("FetchIMAPMessagesS22 - Connected to server but authentication failed.")
                    Return False
                End If

                Logger.Info($"FetchIMAPMessagesS22 - Fetching unseen MessageIds from Inbox: {pInbox}")
                Dim oMessageIds As IEnumerable(Of UInteger) = oClient.Search(S22.Imap.SearchCondition.Unseen, pInbox)

                Logger.Info("FetchIMAPMessagesS22 - Found [{0}] messages", oMessageIds.Count)
                Logger.Debug("FetchIMAPMessagesS22 - Fetching messages...")
                Dim oMessageCountRegular As Integer = 0
                Dim oMessageCountWorked As Integer = 0
                Dim oLastLog As String
                For Each oMessageId As UInteger In oMessageIds
                    Logger.Debug($"Checking message...")
                    Dim oMessage = oClient.GetMessage(oMessageId, False, pInbox)
                    oLastLog = $"Checking message with Subject [{oMessage.Subject}] From [{oMessage.From}]"
                    Logger.Debug(oLastLog)
                    Dim oTempPath = Path.GetTempFileName()
                    Try
                        Dim oResult = WriteMessageToFile(oMessage, oTempPath)
                        oLastLog &= $" # Message written to TempPath [{oTempPath}]"
                        Dim oMessageREFGUID
                        Dim oMsg As Message
                        Try
                            oLastLog &= " # Creating the New Message(oTempPath)"
                            oMsg = New Message(oTempPath)
                            oLastLog &= " # Extracting the MessageID"
                            oMessageREFGUID = oMsg.MessageID
                        Catch ex As Exception
                            Logger.Error(ex)
                            Logger.Warn($"FetchIMAPMessagesS22 - Could not get a MessageID or create a MailObject - Error: {ex.Message} - Last Debug Log: [{oLastLog}]")
                            Continue For
                        End Try

                        oLastLog &= " # Got the MessageID"
                        oMessageREFGUID = oMessageREFGUID.Replace(">", "").Replace("<", "")
                        Dim oCHECKSQL = $"SELECT * FROM TBEMLP_HISTORY WHERE EMAIL_MSGID = '{oMessageREFGUID}'"
                        Dim oCHECKDT As DataTable = _DB_MSSQL.Return_Datatable(oCHECKSQL)
                        If Not IsNothing(oCHECKDT) Then
                            If oCHECKDT.Rows.Count = 0 Then
                                oMessageCountRegular += 1
                                CURRENT_WORKMAIL_LIST.Add(oMsg)
                                oLastLog &= " # Added to CURRENT_WORKMAIL_LIST"
                            Else
                                Logger.Info("FetchIMAPMessagesS22 - Message has already been worked! Skipping!")
                                Logger.Debug($"Message shall be deleted...")
                                oLastLog &= " # Message shall be deleted..."
                                oClient.DeleteMessage(oMessageId)
                                Logger.Debug($"FetchIMAPMessagesS22 - Message has been deleted!")
                                oLastLog &= " # FetchIMAPMessagesS22 - Message has been deleted!"
                                Dim oUpd = $"UPDATE TBEMLP_HISTORY SET DATE_DELETED_INBOX = GETDATE() WHERE EMAIL_MSGID = '{oMessageId}'"
                                _DB_MSSQL.Execute_non_Query(oUpd)
                                oMessageCountWorked += 1
                            End If
                            If IsTest = True Then
                                Logger.Debug($"FetchIMAPMessagesS22 - IMAP-Test Message#: {oMessageCountRegular} - Msgsubject is: {oMsg.Subject} - MsgMessageID is: {oMessageREFGUID}")
                                Logger.Debug($"FetchIMAPMessagesS22 - message correctly fetched. Mail has been downloaded to {oTempPath}")
                            End If
                            Try
                                If DeleteinTest = True Then
                                    Logger.Debug($"Message shall be deleted...")
                                    oClient.DeleteMessage(oMessageId,)
                                    Logger.Debug($"FetchIMAPMessagesS22 - Message has been deleted!")
                                End If
                            Catch ex As Exception
                                Logger.Error(ex)
                                Logger.Warn("Message could not be deleted: " & ex.Message)
                            End Try
                            If MoveMailTo <> String.Empty Then
                                Try
                                    Logger.Debug($"Moving to [{MoveMailTo}] is active...")
                                    oClient.MoveMessage(oMessageId, MoveMailTo)
                                    Logger.Debug($"FetchIMAPMessagesS22 - successfully moved!")
                                Catch ex As Exception
                                    Logger.Error(ex)
                                    Logger.Warn($"FetchIMAPMessagesS22 - Could not move message to folder [{MoveMailTo}] - Error: {ex.Message}")
                                End Try
                            End If


                        End If

                        Try
                            If IsTest = False Then
                                File.Delete(oTempPath)
                            End If

                        Catch ex As Exception

                        End Try
                    Catch ex As Exception
                        Logger.Error(ex)
                        Logger.Warn($"FetchIMAPMessages - Unexpected Error while working on email: [{ex.Message}] - Last Debug Log: [{oLastLog}]")
                    End Try

                Next
                oClient.Expunge()
                Logger.Debug("FetchIMAPMessagesS22 - Finished Message-Fetch")
                If oMessageCountRegular > 0 Or oMessageCountWorked > 0 Then
                    Logger.Info($"###############################################")
                    If oMessageCountRegular > 0 Then
                        Logger.Info($"Found [{oMessageCountRegular.ToString}] regular messages to work on!")
                    End If
                    If oMessageCountWorked > 0 Then
                        Logger.Info($"Found [{oMessageCountWorked.ToString}] worked messages to work on!")
                    End If
                    If IsTest = True Then
                        Dim omsgtext As String
                        If oMessageCountRegular > 0 Then
                            omsgtext = $"Found [{oMessageCountRegular.ToString}] regular Messages to work on!"
                        End If
                        If oMessageCountWorked > 0 Then
                            If omsgtext = String.Empty Then
                                Logger.Info($"Found [{oMessageCountWorked.ToString}] worked messages to work on!")
                            Else
                                omsgtext += vbNewLine & $"Found [{oMessageCountWorked.ToString}] worked messages to work on!"
                            End If
                        End If
                        MsgBox(omsgtext)
                    End If
                    Logger.Info($"###############################################")
                End If
            End Using
            Return True
        Catch ex As Exception
            Logger.Error(ex)
            Return False
        End Try
    End Function
    Private Shared Sub OnWriteLog(ByVal sender As Object, ByVal e As WriteLogEventArgs)
        Logger.Debug(e.Log)
    End Sub
    Public Function FetchIMAPMessagesIsoft(Server As String, Port As Integer, Username As String, Password As String, Inbox As String, Optional MoveMailTo As String = "", Optional IsoftLog As String = "", Optional IsTest As Boolean = False, Optional DeleteinTest As Boolean = False)
        Try
            Logger.Debug(String.Format("Working on IMAP_COLLECT Independentsoft..."))
            Dim oClient As New Independentsoft.Email.Imap.ImapClient(Server, Port)
            If IsoftLog <> "" And LogConfig.Debug = True Then
                Dim iLogger As New Independentsoft.Email.Logger(IsoftLog)
                AddHandler iLogger.WriteLog, AddressOf OnWriteLog
                oClient.Logger = iLogger
            End If

            oClient.EnableSsl = True
            oClient.ValidateRemoteCertificate = False
            Try
                oClient.Connect()
            Catch ex As Exception
                Logger.Error(ex)
                Logger.Warn("Error while oClient.Connec(): " & ex.Message)
                Return False
            End Try

            Try
                oClient.Login(Username, Password, AuthenticationType.Login)
            Catch ex As Exception
                Logger.Warn("Error while loginImap.now trying Ntlm-Auth: " & ex.Message)
                Try
                    oClient.Login(Username, Password, AuthenticationType.Ntlm)
                Catch ex1 As Exception
                    Logger.Error(ex)
                    Return False
                End Try

            End Try
            Logger.Debug("Logged in...")
            oClient.SelectFolder(Inbox)

            Dim coSearchCriteria As New SearchCriteria()
            coSearchCriteria.All = True
            Dim oMessageCountRegular As Integer = 0
            Dim oMessageCountWorked As Integer = 0
            Dim oUniqueID As Integer() = oClient.Search(coSearchCriteria)

            If IsTest = True Then
                MsgBox($"{oUniqueID.Count} messages in Postbox! (oUniqueID.Length: {oUniqueID.Length.ToString})")
            End If

            Dim oFoundMessages As Message() = New Message(oUniqueID.Length - 1) {}
            Dim oEnvelopes As Independentsoft.Email.Imap.Envelope() = oClient.ListMessages()

            For oCounterEnvelope As Integer = 0 To oEnvelopes.Length - 1
                Logger.Debug($"oEnvelopes.Length: {oEnvelopes.Length}")
                For oCounterEmailInPostbox As Integer = 0 To oUniqueID.Length - 1

                    If oEnvelopes(oCounterEnvelope).UniqueID = oUniqueID(oCounterEmailInPostbox) Then
                        Dim oMimeMessage As Mime.Message = oClient.GetMessage(oEnvelopes(oCounterEnvelope).UniqueID)
                        If Not IsNothing(oMimeMessage) Then
                            Dim oMessageID = oMimeMessage.MessageID
                            oMessageID = oMessageID.Replace(">", "").Replace("<", "")
                            'oEnvelopes(oCounterEnvelope).UniqueI
                            Logger.Info($"Isoft: Working on email: MessageID [{oMessageID}] - Subject[{oEnvelopes(oCounterEnvelope).Subject}] - Date [{oEnvelopes(oCounterEnvelope).Date.ToString}]")
                            Dim oCHECKSQL = $"SELECT * FROM TBEMLP_HISTORY WHERE lower(EMAIL_MSGID) = lower('{oMessageID}')"
                            Dim oCHECKDT As DataTable = _DB_MSSQL.Return_Datatable(oCHECKSQL)
                            If Not IsNothing(oCHECKDT) Then
                                If oCHECKDT.Rows.Count = 0 Then
                                    Logger.Info($"Isoft: Adding email: MessageID [{oMessageID}] - Subject[{oEnvelopes(oCounterEnvelope).Subject}] - Date [{oEnvelopes(oCounterEnvelope).Date.ToString}]")
                                    oMessageCountRegular += 1
                                    CURRENT_WORKMAIL_LIST.Add(oMimeMessage)

                                Else
                                    Logger.Info($"Isoft: Already existing email with MessageID [{oMessageID}] - Subject[{oEnvelopes(oCounterEnvelope).Subject}] - Date [{oEnvelopes(oCounterEnvelope).Date.ToString}] ")
                                    oMessageCountWorked += 1
                                    Try
                                        oClient.Delete(oEnvelopes(oCounterEnvelope).UniqueID)
                                        Dim oUpd = $"UPDATE TBEMLP_HISTORY SET DATE_DELETED_INBOX = GETDATE(), COMMENT = 'DELETED MESSAGE FROM INBOX (2ndRun ALREADY WORKED)' WHERE lower(EMAIL_MSGID) = lower('{oMessageID}')"
                                        _DB_MSSQL.Execute_non_Query(oUpd)
                                    Catch ex As Exception
                                        Logger.Error(ex)
                                        Logger.Warn($"Error deleting/Commenting message due to to already worked: {ex.Message}")
                                    End Try

                                End If
                                oClient.Store(oEnvelopes(oCounterEnvelope).UniqueID, "+FLAGS", "SEEN")

                                If MoveMailTo <> String.Empty Then
                                    Try
                                        oClient.AddMessage(MoveMailTo, oMimeMessage)
                                    Catch ex As Exception
                                        Logger.Error(ex)
                                        Logger.Warn($"Isoft: Could not move message to folder [{MoveMailTo}] - Error: {ex.Message}")
                                    End Try
                                End If
                                If oMessageCountRegular = 250 Then
                                    Logger.Warn($"Worked 100 Mails exiting oCounterEmailInPostbox!")
                                    Exit For
                                End If
                            End If

                        End If
                    End If
                Next
            Next
            oClient.Expunge()
            oClient.Disconnect()
            If oMessageCountRegular > 0 Or oMessageCountWorked > 0 Then
                Logger.Info($"###############################################")
                If oMessageCountRegular > 0 Then
                    Logger.Info($"Isoft: Found [{oMessageCountRegular.ToString}] regular messages to work on!")
                End If
                If oMessageCountWorked > 0 Then
                    Logger.Info($"Isoft: Found [{oMessageCountWorked.ToString}] worked messages to work on!")
                End If
                If IsTest = True Then
                    Dim omsgtext As String
                    If oMessageCountRegular > 0 Then
                        omsgtext = $"Isoft: Found [{oMessageCountRegular.ToString}] regular Messages to work on!"
                    End If
                    If oMessageCountWorked > 0 Then
                        If omsgtext = String.Empty Then
                            Logger.Info($"Isoft: Found [{oMessageCountWorked.ToString}] worked messages to work on!")
                        Else
                            omsgtext += vbNewLine & $"Isoft: Found [{oMessageCountWorked.ToString}] worked messages to work on!"
                        End If
                    End If
                    MsgBox(omsgtext)
                End If
                Logger.Info($"###############################################")
            End If
            '        Logger.Debug($"{oCount.ToString} messages will be worked..")
            Logger.Debug("Isoft: IMAP COLLECT Independentsoft finished!")
            Return True
        Catch ex As Exception
            Logger.Error(ex)
            Logger.Error(ex, "Unexpected Error in IMAP COLLECT Independentsoft:")
            Return False
        End Try
    End Function
    Public Function IMAP_Set2Seen(Server As String, Port As Integer, Username As String, Password As String, IsoftLog As String)
        Try
            Logger.Debug(String.Format("Working on IMAP_Set2Seen Independentsoft..."))
            Dim oClient As New Independentsoft.Email.Imap.ImapClient(Server, Port)
            If IsoftLog <> "" And LogConfig.Debug = True Then
                Dim iLogger As New Independentsoft.Email.Logger(IsoftLog)
                AddHandler iLogger.WriteLog, AddressOf OnWriteLog
                oClient.Logger = iLogger
            End If

            oClient.EnableSsl = True
            oClient.ValidateRemoteCertificate = False
            oClient.Connect()
            Try
                oClient.Login(Username, Password, AuthenticationType.Login)
            Catch ex As Exception
                Logger.Error(ex)
                Logger.Warn("Error while loginImap.now trying Ntlm-Auth: " & ex.Message)
                Try
                    oClient.Login(Username, Password, AuthenticationType.Ntlm)
                Catch ex1 As Exception
                    Logger.Error(ex)
                    Return False
                End Try

            End Try
            Logger.Debug("Logged in...")
            oClient.SelectFolder("Inbox")

            Dim coSearchCriteria As New SearchCriteria()
            coSearchCriteria.Unseen = True

            Dim oUniqueID As Integer() = oClient.Search(coSearchCriteria)

            Dim oFoundMessages As Message() = New Message(oUniqueID.Length - 1) {}

            For i As Integer = 0 To oUniqueID.Length - 1
                oFoundMessages(i) = oClient.GetMessage(oUniqueID(i))
                If oFoundMessages(i).Subject.ToLower.Contains("seen") Then
                    Logger.Debug($"Working on unseen email: [{oUniqueID(i)}] Subject:{oFoundMessages(i).Subject} - Date {oFoundMessages(i).Date}")
                    Dim oEnvelopes1 As Independentsoft.Email.Imap.Envelope() = oClient.ListMessages()
                    For i1 As Integer = 0 To oEnvelopes1.Length - 1
                        If oEnvelopes1(i1).UniqueID = oUniqueID(i) Then
                            Logger.Debug($"Setting email to Unseen!")
                            oClient.Store(oEnvelopes1(i1).UniqueID, "+FLAGS", "SEEN")
                            MsgBox("Set to SEEN")
                        End If

                    Next
                End If

            Next
            oClient.Expunge()
            MsgBox("Expunge done")
            oClient.Disconnect()


            Logger.Debug("IMAP_Set2Seen finished!")
            Return True
        Catch ex As Exception
            Logger.Warn("Unexpected Error in IMAP_Set2Seen Independentsoft")
            Logger.Error(ex)
            Return False
        End Try
    End Function

    ''' <summary>
    ''' Uses a private API from MailWriter to write a MailMessage to disk.
    ''' May break in future versions of .NET
    ''' </summary>
    Public Function WriteMessageToFile(Message As Mail.MailMessage, Filename As String) As Boolean
        Dim oAssembly As Assembly = GetType(Mail.SmtpClient).Assembly
        Dim oMailWriterType As Type = oAssembly.[GetType]("System.Net.Mail.MailWriter")

        Try
            Using oStream As New FileStream(Filename, FileMode.Create)
                Dim oMailWriterConstructor As ConstructorInfo = oMailWriterType.GetConstructor(
                    BindingFlags.Instance Or BindingFlags.NonPublic, Nothing, New Type() {GetType(Stream)}, Nothing
                )
                Dim oMailWriter As Object = oMailWriterConstructor.Invoke(New Object() {oStream})
                Dim sendMethod As MethodInfo = GetType(Mail.MailMessage).GetMethod("Send", BindingFlags.Instance Or BindingFlags.NonPublic)
                sendMethod.Invoke(Message, BindingFlags.Instance Or BindingFlags.NonPublic, Nothing, {oMailWriter, True, True}, Nothing)
            End Using

            Return True
        Catch ex As Exception
            Logger.Error(ex)
            Return Nothing
        End Try
    End Function



End Class
