Imports System.IO
Imports DigitalData.Modules.Base
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Logging
Imports DigitalData.Modules.Messaging.Mail
Imports EmailProfiler.Common.ClassCurrent
Imports Limilabs.Mail
Imports Limilabs.Mail.Headers

Public Class clsWorker
    Private ReadOnly _Logger As Logger
    Private ReadOnly _Fetcher As MailFetcher
    Private ReadOnly _Database As MSSQLServer
    Private ReadOnly _UseWindream As Boolean = False
    Private ReadOnly ClassWindreamAllgemein As clsWindream_allgemein
    Private ReadOnly ClassWindreamIndex As clsWindream_Index
    Private ReadOnly _ClassWorkMail As clsWorkEmail
    Private ReadOnly Encryption As clsEncryption
    Private ReadOnly _ProfileId As Integer = 0D
    Private ReadOnly EmailLimitationSender As String = ""
    Private ReadOnly EmailLimitationEnabled As Boolean = False

    Private Const SUCCESS_IMAP_FOLDER = "Verarbeitet"

    Private ReadOnly LocalEmlFile As String = ""

    Sub New(pLogConfig As LogConfig, pConnectionString As String, pWindreamConnectionString As String, pPollProfileId As Integer, pConfigData As Config, Optional pLocalEML As String = "")
        _Logger = pLogConfig.GetLogger
        'ClassEmailImap = New clsEmailIMAP(pLogConfig)
        _Fetcher = New MailFetcher(pLogConfig)
        _Database = New MSSQLServer(pLogConfig, pConnectionString)

        _UseWindream = pConfigData.UseWindream
        EmailLimitationSender = pConfigData.EmailSenderLimitation
        If EmailLimitationSender.Contains("@") Then
            _Logger.Info("Email Sender Limitation active for address: [{0}]", EmailLimitationSender)
            EmailLimitationEnabled = True
        End If

        If _UseWindream Then
            ClassWindreamAllgemein = New clsWindream_allgemein(pLogConfig)
            ClassWindreamIndex = New clsWindream_Index(pLogConfig)
        End If

        _ClassWorkMail = New clsWorkEmail(pLogConfig, pConnectionString, pWindreamConnectionString, pConfigData)

        Encryption = New clsEncryption("!35452didalog=", pLogConfig)
        _ProfileId = pPollProfileId
        LocalEmlFile = pLocalEML
    End Sub

    Private Sub DeleteTempFiles()
        For Each _file In TEMP_FILES
            _Logger.Debug("Trying to delete temp file: [{0}]", _file)

            If File.Exists(_file) Then
                Try
                    File.Delete(_file)
                Catch ex As Exception
                    _Logger.Error(ex)
                    _Logger.Warn("Could not delete the tempfile from TEMP_FILES: [{0}]", _file)
                End Try
            End If
        Next
        TEMP_FILES.Clear()
    End Sub

    Private Function LoadEmailAccounts() As DataTable
        Return _Database.GetDatatable("SELECT * FROM TBDD_EMAIL_ACCOUNT WHERE ACTIVE = 1")
    End Function

    Private Function LoadPollingProfiles(pProfileId As Integer) As DataTable
        Dim oSQL = "SELECT * FROM TBEMLP_POLL_PROFILES WHERE ACTIVE = 1"

        If _ProfileId = 0 Then
            oSQL &= " ORDER BY SEQUENCE"
        Else
            oSQL &= $" AND GUID = {pProfileId}"
        End If

        Return _Database.GetDatatable(oSQL)
    End Function

    Public Sub Start_WorkingProfiles(Optional LocalEmail As Boolean = False)
        Try
            DeleteTempFiles()
            IS_LOCAL_TEST = LocalEmail
            If _Database.DBInitialized = False Then
                _Logger.Warn("Database is not initialized. Exiting.")
                Exit Sub
            End If

            _Logger.Debug("now windream_init... ")
            If _UseWindream Then
                If ClassWindreamAllgemein.Init = False Then
                    _Logger.Info("windream could not be initialized!!")
                    Exit Sub
                Else
                    _Logger.Debug("windream_initialized!")
                End If
            End If

            Dim EmailAccountTable As DataTable = LoadEmailAccounts()
            Dim PollingProfileTable = LoadPollingProfiles(_ProfileId)

            If IsNothing(PollingProfileTable) Then
                _Logger.Warn("Error while fetching Polling Profiles. Exiting.")
                Exit Sub
            End If

            If PollingProfileTable.Rows.Count = 0 Then
                _Logger.Warn("No active Polling Profiles found. Exiting.")
                Exit Sub
            End If

            _Logger.Debug("Count of active profiles: " & PollingProfileTable.Rows.Count.ToString)

            For Each oProfile As DataRow In PollingProfileTable.Rows

                Dim oValidationSql = oProfile.ItemEx("VALIDATION_SQL", "")

                CURRENT_PROFILE_GUID = oProfile.Item("GUID")
                DT_POLL_PROCESS = Nothing
                Dim sql = String.Format("SELECT * FROM TBEMLP_POLL_PROCESS WHERE PROFILE_ID = {0} AND ACTIVE = 1", CURRENT_PROFILE_GUID)
                DT_POLL_PROCESS = _Database.GetDatatable(sql)
                If Not IsNothing(DT_POLL_PROCESS) Then
                    If DT_POLL_PROCESS.Rows.Count = 0 Then
                        _Logger.Info("No processes configured for this Email-Profile - " & sql)
                        Continue For
                    Else
                        DT_STEPS = Nothing
                        DT_STEPS = _Database.GetDatatable($"SELECT T.* FROM TBEMLP_POLL_STEPS T,TBEMLP_POLL_PROCESS T1  WHERE T.PROCESS_ID = T1.GUID AND T1.PROFILE_ID = {CURRENT_PROFILE_GUID} AND T1.ACTIVE = 1")

                    End If
                Else
                    _Logger.Warn("DT_POLL_PROCESS is nothing")
                    Continue For
                End If

                CURRENT_EMAIL_GUID = 0

                CURRENT_POLL_TYPE = oProfile.Item("POLL_TYPE")
                _Logger.Debug(String.Format("Working on profile: ({0}-{1}-{2}) ", oProfile.Item("GUID"), oProfile.Item("PROFILE_NAME"), CURRENT_POLL_TYPE))
                CURRENT_EMAIL_GUID = oProfile.Item("EMAIL_CONF_ID")

                Dim FilteredRows As List(Of DataRow) = EmailAccountTable.
                    Select($"GUID = {CURRENT_EMAIL_GUID}").
                    ToList()
                _Logger.Debug("FilteredRows: " & FilteredRows.Count)

                If FilteredRows.Count = 1 Then
                    Dim oRow = FilteredRows(0)
                    Dim oMailFrom = oRow("EMAIL_FROM")
                    Dim oMailServer = oRow("EMAIL_SMTP")
                    Dim oMailUser = oRow("EMAIL_USER")
                    Dim oMailPassword = oRow("EMAIL_PW")
                    Dim oMailPortIn = oRow("PORT_IN")
                    Dim oMailboxName = "Inbox"
                    Dim oMailArchiveFolder = oRow("ARCHIVE_FOLDER")
                    Dim oMailAuthType = oRow("AUTH_TYPE")

                    _Logger.Debug("Mail Server: {0}", oMailServer)
                    _Logger.Debug("Mail From: {0}", oMailFrom)

                    Dim PWPlain = Encryption.DecryptData(oMailPassword)
                    If Not IsNothing(PWPlain) Then
                        If PWPlain <> "" Then
                            oMailPassword = PWPlain
                        Else
                            _Logger.Warn("PWPlain is string.empty - Could not decrypt passwort")
                        End If
                    Else
                        _Logger.Warn("PWPlain is nothing - Could not decrypt passwort")
                    End If

                    CURRENT_WORKMAIL_UID_LIST.Clear()

                    If oMailServer <> "" Then
                        Dim oPollResult As Boolean = False
                        If LocalEmail = True Then
                            oPollResult = True
                        Else
                            Select Case CURRENT_POLL_TYPE
                                Case "IMAP"
                                    ' We are using 'Archive Folder' as an additional field to save the Tenant-ID for O365-OAuth2
                                    oPollResult = FetchMessages(oMailServer, oMailPortIn, oMailUser, oMailPassword, oMailAuthType, oMailArchiveFolder)

                                Case Else
                                    _Logger.Error("Poll Type [{0}] is not supported!", CURRENT_POLL_TYPE)
                                    oPollResult = False
                            End Select
                        End If

                        If CURRENT_WORKMAIL_UID_LIST.Count() > 0 Or LocalEmail = True Then
                            If LocalEmail Then
                                _Logger.Info("Working with local Mail")
                                Dim oEmail As IMail = New MailBuilder().CreateFromEmlFile(LocalEmlFile)

                                Dim oUID = String.Concat(Now.Month.ToString, Now.Day, Now.Hour, Now.Minute, Now.Second)
                                _ClassWorkMail.WorkEmailMessage(oEmail, oUID, oValidationSql)
                            Else
                                Try
                                    _Logger.Info(String.Format("Pulled: [{0}] E-Mails", CURRENT_WORKMAIL_UID_LIST.Count()))

                                    For Each oMailId In CURRENT_WORKMAIL_UID_LIST
                                        Dim oEmail As IMail = _Fetcher.FetchMail(oMailId)

                                        If Not IsNothing(oEmail) Then
                                            If EmailLimitationEnabled Then
                                                Dim oEmailFrom As String = ""
                                                For Each m As MailBox In oEmail.From
                                                    oEmailFrom = m.Address
                                                Next
                                                If oEmailFrom <> EmailLimitationSender Then
                                                    _Logger.Debug($"Skipping email {oEmailFrom} ...Subject [{oEmail.Subject}]")
                                                    Continue For
                                                End If
                                            End If
                                            ' Hier wird die einzelne EMail jetzt verarbeitet
                                            If _ClassWorkMail.WorkEmailMessage(oEmail, oMailId, oValidationSql) = True Then
                                                If LocalEmlFile = "" Then
                                                    DeleteOrMoveEmailFile(oMailId)
                                                End If
                                                _Logger.Info("Email with Id [{0}] was successfully worked.", oMailId)
                                            End If

                                        Else
                                            _Logger.Info("### oEmail was nothing ###")
                                        End If
                                    Next

                                    If CURRENT_POLL_TYPE = "IMAP" And CURRENT_WORKMAIL_UID_LIST.Count > 0 Then
                                        _Fetcher.Disconnect()
                                    End If

                                Catch ex As Exception
                                    _Logger.Error(ex)
                                    _Logger.Warn($"Unexpected Error working CURRENT_WORKMAIL_UID_LIST: {ex.Message} ")
                                End Try

                            End If
                        Else
                            _Logger.Debug(String.Format("No emails for profile!"))
                        End If

                    Else
                        _Logger.Warn("For the Email-Profile ID " & CURRENT_EMAIL_GUID & " no record could be found!")
                    End If

                    _Database.ExecuteNonQuery("UPDATE TBEMLP_POLL_PROFILES SET LAST_TICK = GETDATE() WHERE GUID = " & oProfile.Item("GUID").ToString)
                Else
                    _Logger.Warn("For the Email-Profile ID " & CURRENT_EMAIL_GUID & " no record could be found! Check wether Email-Profile is active!")
                End If
            Next

            DeleteTempFiles()

            _Database.ExecuteNonQuery("UPDATE TBEMLP_CONFIG SET LAST_TICK = GETDATE() WHERE GUID = 1")
        Catch ex As Exception
            _Logger.Error(ex)
        End Try
    End Sub

    Public Function FetchMessages(pServer As String, pPort As Integer, pUsername As String, pPassword As String, pAuthType As String, pArchiveFolder As String) As Boolean
        Try
            Dim oSession As MailSession.SessionInfo

            If pAuthType = MailSession.AUTH_OAUTH2 Then
                Dim oClientId As String = pServer
                Dim oClientSecret As String = pPassword
                Dim oTenantId As String = pArchiveFolder
                oSession = _Fetcher.ConnectToO365(pUsername, oClientId, oTenantId, oClientSecret)
            Else
                oSession = _Fetcher.Connect(pServer, pPort, pUsername, pPassword, pAuthType, New MailSession.MailSessionOptions() With {
                    .EnableTls1_1 = True,
                    .EnableTls1_2 = True
                })
            End If


            If oSession.Connected = False AndAlso oSession.Error IsNot Nothing Then
                _Logger.Warn("Connection to Mail Server failed!")
                _Logger.Error(oSession.Error)
                Return False

            ElseIf oSession.Connected = False Then
                _Logger.Warn("Connection to Mail Server failed!")
                Return False
            End If

            CURRENT_ImapObject = _Fetcher.Client
            Dim oMailIds As List(Of Long) = _Fetcher.ListAllMails()

            If oMailIds Is Nothing Then
                _Logger.Warn("List of UIDs was Nothing. Exiting.")
                Return False
            End If

            If oMailIds.Count = 0 Then
                _Logger.Debug("No Emails found.")
                Return True
            End If

            CURRENT_WORKMAIL_UID_LIST = oMailIds

            Return True

        Catch ex As Exception
            _Logger.Error(ex)
            Return False

        End Try
    End Function
    Private Sub DeleteOrMoveEmailFile(pMailId As Integer)
        Try
            If MESSAGE_ERROR = True Then
                _Logger.Warn("Did not delete or move Message with UID [{0}] as there was an MessageError!", pMailId)
                Return
            End If

            If IsNothing(_Fetcher.Client) Then
                _Logger.Warn("Did not delete or move Message with UID [{0}] as ImapClient is null", pMailId)
                Return
            End If

            If DeleteMail = True Then
                _Fetcher.Client.DeleteMessageByUID(pMailId)
                _Logger.Info("Email with Id [{0}] was deleted.", pMailId)
            Else
                If TestImapFolderExists(SUCCESS_IMAP_FOLDER) Then
                    If _Fetcher.Client.MoveByUID(pMailId, SUCCESS_IMAP_FOLDER) IsNot Nothing Then
                        _Logger.Info("Email with UID [{0}] was moved", pMailId)
                    End If
                Else
                    _Logger.Warn("IMAP Folder [{0}] does not exist. Emails could not be moved!", SUCCESS_IMAP_FOLDER)
                End If
            End If
        Catch ex As Exception
            _Logger.Error(ex)
        End Try
    End Sub

    Private Function TestImapFolderExists(pFolderName As String) As Boolean
        Try
            Return _Fetcher.Client.
                GetFolders().
                Where(Function(f) f.Name = pFolderName).
                Any()
        Catch ex As Exception
            _Logger.Warn("Could not get IMAP folders. Returning False.")
            _Logger.Error(ex)
            Return False
        End Try
    End Function
End Class
