Imports System.Collections.Generic
Imports System.IO
Imports System.Linq
Imports DigitalData.Modules.Base
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Interfaces
Imports DigitalData.Modules.Logging

Namespace ZUGFeRD
    Public Class FileFunctions
        Private ReadOnly _logConfig As LogConfig
        Private ReadOnly _logger As Logger
        Private ReadOnly _filesystem As FilesystemEx
        Private ReadOnly _email As ZUGFeRD.EmailFunctions

        Public Sub New(pLogConfig As LogConfig, pMSSQL As MSSQLServer)
            _logConfig = pLogConfig
            _logger = _logConfig.GetLogger()
            _email = New EmailFunctions(pLogConfig, pMSSQL)
            _filesystem = New FilesystemEx(pLogConfig)
        End Sub

        Public Function CheckFileAge(oFiles As List(Of FileInfo), pMinFileAge As Integer)
            Return oFiles.Where(Function(f) Now.Subtract(f.CreationTime).Minutes < pMinFileAge).Any()
        End Function

        Public Sub MoveFiles(
                         pArgs As WorkerArgs,
                         pMessageId As String,
                         pFiles As List(Of FileInfo),
                         pAttachmentFiles As List(Of FileInfo),
                         pEmbeddedAttachments As List(Of PDFEmbeds.EmbeddedFile),
                         pMoveDirectory As String,
                         pIsSuccess As Boolean)

            Dim oFinalMoveDirectory As String = pMoveDirectory
            Dim oDateSubDirectoryName As String = Now.ToString("yyyy\\MM\\dd")
            Dim oAttachmentDirectory As String = Path.Combine(oFinalMoveDirectory, pArgs.AttachmentsSubDirectory, oDateSubDirectoryName)

            ' Files will be moved to a subfolder for the current day if they are rejected
            If Not pIsSuccess Then
                oFinalMoveDirectory = Path.Combine(oFinalMoveDirectory, oDateSubDirectoryName)
            End If

            ' Create directories if they don't exist
            If Not Directory.Exists(oFinalMoveDirectory) Then
                Try
                    Directory.CreateDirectory(oFinalMoveDirectory)
                Catch ex As Exception
                    _logger.Error(ex)
                End Try
            End If

            If Not Directory.Exists(oAttachmentDirectory) And pAttachmentFiles.Count > 0 Then
                Try
                    Directory.CreateDirectory(oAttachmentDirectory)
                Catch ex As Exception
                    _logger.Error(ex)
                End Try
            End If

            ' Filter out Attachments from `Files`
            Dim oInvoiceFiles As List(Of FileInfo) = pFiles.Except(pAttachmentFiles).ToList()

            ' Move PDF/A Files
            For Each oFile In oInvoiceFiles
                Try
                    Dim oFilePath = _filesystem.GetVersionedFilename(Path.Combine(oFinalMoveDirectory, oFile.Name))

                    _filesystem.MoveTo(oFile.FullName, oFilePath, oFinalMoveDirectory)

                    _logger.Info("File moved to {0}", oFilePath)
                Catch ex As Exception
                    _logger.Warn("Could not move file {0}", oFile.FullName)
                    _logger.Error(ex)
                End Try
            Next

            ' Move non-PDF/A Email Attachments/Files
            For Each oFile In pAttachmentFiles
                Try
                    Dim oFilePath = _filesystem.GetVersionedFilename(Path.Combine(oAttachmentDirectory, oFile.Name))
                    _filesystem.MoveTo(oFile.FullName, oFilePath, oAttachmentDirectory)
                    _logger.Info("Attachment moved to {0}", oFilePath)
                Catch ex As Exception
                    _logger.Warn("Could not move attachment {0}", oFile.FullName)
                    _logger.Error(ex)
                End Try
            Next

            ' Write Embedded Files to disk
            For Each oResult In pEmbeddedAttachments
                Try
                    Dim oFileName As String = $"{pMessageId}~{oResult.FileName}"
                    Dim oFilePath As String = Path.Combine(oAttachmentDirectory, oFileName)

                    If Not File.Exists(oAttachmentDirectory) Then
                        Directory.CreateDirectory(oAttachmentDirectory)
                    End If
                    System.IO.File.WriteAllBytes(oFilePath, oResult.FileContents)
                    If CheckBytes(oFilePath, oResult.FileContents) = True Then
                        If FileOpenwithError(oFilePath) Then
                            _logger.Info("Embedded Attachment moved to {0}", oFilePath)
                        Else
                            _logger.Info("File is corrupt. Deleting the created file ...")
                            File.Delete(oFilePath)
                        End If
                    Else
                        _logger.Info("File is corrupt (CheckBytes). Deleting the created file ...")
                        File.Delete(oFilePath)
                    End If
                    'Using oWriter As New FileStream(oFilePath, FileMode.Create)
                    '    oWriter.Write(oResult.FileContents, 0, oResult.FileContents.Length)

                    'End Using
                Catch ex As Exception
                    _logger.Warn("Could not save embedded attachment {0}", oResult.FileName)
                    _logger.Error(ex)
                End Try
            Next

            _logger.Info("Finished moving files")
        End Sub
        Private Function CheckBytes(oFilePath As String, oFileContents As IEnumerable(Of Byte)) As Boolean
            Dim savedBytes() As Byte = System.IO.File.ReadAllBytes(oFilePath)

            If savedBytes.SequenceEqual(oFileContents) Then
                Return True
            Else
                Return False
            End If
        End Function
        Public Function MoveAndRenameEmailToRejected(pArgs As WorkerArgs, pMessageId As String) As EmailData
            _logger.Info("Moving Mail with MessageId [{0}] to Rejected folder", pMessageId)
            _logger.Debug("Fetching Email Data")

            Dim oEmailData = _email.GetEmailDataForMessageId(pMessageId)
            _logger.Debug("Email Data fetched!")

            Dim oSource = _email.GetOriginalEmailPath(pArgs.OriginalEmailDirectory, pMessageId)
            _logger.Debug("Original email path: [{0}]", oSource)

            If oSource = String.Empty Then
                _logger.Warn("Original Email for [{0}] could not be found. Exiting.", pMessageId)
                Return New EmailData()
            End If

            Dim oDateSubDirectoryName As String = Now.ToString("yyyy-MM-dd")
            Dim oDestination As String

            Dim oRejectedDirectory As String = Path.Combine(pArgs.RejectedEmailDirectory, oDateSubDirectoryName)

            ' Create the destination directory if it does not exist
            _logger.Debug("Creating destination directory [{0}]", oRejectedDirectory)
            If Not Directory.Exists(oRejectedDirectory) Then
                Try
                    Directory.CreateDirectory(oRejectedDirectory)
                Catch ex As Exception
                    _logger.Error(ex)
                End Try
            End If

            ' If oEmailData is Nothing, TBEDM_EMAIL_PROFILER_HISTORY for MessageId was not found. 
            ' This only should happen when testing and db-tables are deleted frequently
            If oEmailData Is Nothing Then
                oDestination = GetEmailPathWithSubjectAsName(oRejectedDirectory, pMessageId)
            Else
                oDestination = GetEmailPathWithSubjectAsName(oRejectedDirectory, StringEx.ConvertTextToSlug(oEmailData.Subject))
            End If

            _logger.Debug("Destination for eml file is {0}", oDestination)

            Dim oFinalFileName = _filesystem.GetVersionedFilename(oDestination)

            _logger.Debug("Versioned filename for eml file is {0}", oFinalFileName)

            If oEmailData Is Nothing Then
                _logger.Warn("Could not get Email Data from Database. File {0} will not be moved!", oSource)
                Return New EmailData()
            End If

            '---------------------------

            Try
                _logger.Info("Moving email from {0} to {1}", oSource, oFinalFileName)
                File.Move(oSource, oFinalFileName)
                oEmailData.Attachment = oFinalFileName
            Catch ex As Exception
                _logger.Warn("File {0} could not be moved! Original Filename will be used!", oSource)
                _logger.Error(ex)
                oEmailData.Attachment = oSource
            End Try

            _logger.Info("Email [{0}] moved to rejected folder!", pMessageId)

            Return oEmailData
        End Function
        Private Function FileOpenwithError(pFilePath As String) As Boolean
            Try
                Using fs As FileStream = File.Open(pFilePath, FileMode.Open, FileAccess.Read, FileShare.None)
                    Return True ' Datei kann geöffnet werden
                End Using
            Catch ex As IOException
                _logger.Info("file [{0}] could not be opened! Error IOException: [{1}]", pFilePath, ex.Message)
                Return False ' Datei ist gesperrt oder existiert nicht
            Catch ex As Exception
                _logger.Info("file [{0}] could not be opened! Error: [{1}]", pFilePath, ex.Message)
                Return False ' Andere Fehler
            End Try
        End Function
        Public Function GetEmailPathWithSubjectAsName(RejectedEmailDirectory As String, UncleanedSubject As String) As String
            Dim oCleanSubject = String.Join("", UncleanedSubject.Split(Path.GetInvalidPathChars()))
            Dim oAttachmentDirectory = RejectedEmailDirectory
            Dim oAttachmentFile = oCleanSubject & ".eml"
            _logger.Debug("Email Filename is [{0}]", oAttachmentFile)

            Dim oAttachmentPath = Path.Combine(oAttachmentDirectory, oAttachmentFile)

            Return oAttachmentPath
        End Function
    End Class

End Namespace
