Imports DigitalData.Modules.Base
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Jobs.Exceptions
Imports DigitalData.Modules.Logging
Imports System.Data
Imports System.IO
Imports System.Security.Cryptography

Public Class HashFunctions
    Inherits BaseClass

    Private ReadOnly Database As MSSQLServer

    Public Sub New(pLogConfig As LogConfig, pDatabase As MSSQLServer)
        MyBase.New(pLogConfig)
        Database = pDatabase
    End Sub

    ''' <summary>
    ''' Generates the MD5 Checksum of a file and checks it against the histroy table TBEDM_ZUGFERD_HISTORY_IN
    ''' </summary>
    ''' <param name="pFilePath">The path of the file to be checked</param>
    ''' <param name="pIgnoreRejectionStatus">Should the check take into account the rejection status of the file?</param>
    ''' <returns>The MD5 Checksum of the file, or an empty string, if the Checksum could not be created</returns>
    ''' <exception cref="MD5HashException">Throws, when the file should be rejected, ie. if it already exists in the table</exception>
    Public Function GenerateAndCheck_MD5Sum(pFile As FileInfo, pMessageId As String, pIgnoreRejectionStatus As Boolean) As String
        Dim oMD5CheckSum = CreateMD5(pFile.FullName)

        ' Exit if MD5 could not be created
        If oMD5CheckSum = String.Empty Then
            Logger.Warn("MD5 Checksum is nothing for file [{0}]!", pFile.FullName)
            Return oMD5CheckSum
        End If

        ' Check if Checksum exists in History Table
        Dim oCheckCommand = $"SELECT * FROM TBEMLP_HISTORY WHERE GUID = (SELECT MAX(GUID) FROM TBEMLP_HISTORY WHERE UPPER(MD5HASH) = UPPER('{oMD5CheckSum}'))"
        Dim oTable As DataTable = Database.GetDatatable(oCheckCommand, MSSQLServer.TransactionMode.NoTransaction)

        ' If History entries could not be fetched, just return the MD5 Checksum
        If IsNothing(oTable) Then
            Logger.Warn("Be careful: oExistsDT is nothing for file [{0}]!", pFile.FullName)
            Return oMD5CheckSum
        End If

        ' If Checksum does not exist in History entries, just return the MD5 Checksum
        If oTable.Rows.Count = 0 Then
            Logger.Debug("File [{0}] was not found in History!", pFile.FullName)
            Return oMD5CheckSum
        End If

        ' ====================================================
        ' Checksum exists in History entries, reject!
        ' ====================================================

        Dim oRejected As Boolean
        Dim oHistoryId As Integer

        ' Try to read Rejected Status and History Id
        Try
            Dim oRow As DataRow = oTable.Rows.Item(0)
            oRejected = oRow.ItemEx("CUST_REJECTED", False)
            oHistoryId = oRow.Item("GUID")

        Catch ex As Exception
            Logger.Warn("Error while converting REJECTED: " & ex.Message)
            oRejected = False

        End Try

        Dim oOriginalName As Object = GetOriginalFilename(pFile.Name)
        Logger.Info("File with MessageId [{0}] and Filename [{1}] has already been processed.", pMessageId, oOriginalName)

        ' If the file was already rejected, it is allowed to be processed again, 
        ' even if the Checksum exists in the history entries (default case)
        ' Which means, if it was not rejected before, it will be rejected in any case!
        '
        ' This logic can be overwritten by the IgnoreRejectionStatus parameter.
        ' If it is set to true, the file will be rejected if the file exists in the history entries,
        ' regardless of the rejected parameter.

        If oRejected = True And pIgnoreRejectionStatus = True Then
            Logger.Info("ZuGFeRDFile already has been processed, but Rejection Status will be ignored!")
        ElseIf oRejected = False Then
            Logger.Info("ZuGFeRDFile already has been processed. Will be rejected.")
            Throw New MD5HashException($"There is already an identical invoice! - HistoryID [{oHistoryId}] - Case 1", oOriginalName)
        Else
            Logger.Info("ZuGFeRDFile already has been processed. Will be rejected.")
            Throw New MD5HashException($"There is already an identical invoice! - HistoryID [{oHistoryId}] - Case 2", oOriginalName)
        End If

        Return oMD5CheckSum
    End Function

    Public Function GetOriginalFilename(pFilename As String) As String
        ' Try to get the original filename from Attachment table
        ' If this fails, falls back to the new filename (<msgid>~Attm<i>.ext)
        Dim oSQL = $"SELECT EMAIL_ATTMT FROM TBEMLP_HISTORY_ATTACHMENT WHERE EMAIL_ATTMT_INDEX = '{pFilename}'"
        Dim oEmailAttachment = Database.GetScalarValue(oSQL, MSSQLServer.TransactionMode.NoTransaction)
        Dim oOriginalName = ObjectEx.NotNull(oEmailAttachment, pFilename)
        Return oOriginalName
    End Function

    Private Function CreateMD5(pFilename As String) As String
        Try
            Dim oMD5 As New MD5CryptoServiceProvider
            Dim oHash As Byte()
            Dim oHashString As String
            Dim oResult As String = ""

            Using oFileStream As New FileStream(pFilename, FileMode.Open, FileAccess.Read, FileShare.Read, 8192)
                oHash = oMD5.ComputeHash(oFileStream)
                oHashString = BitConverter.ToString(oHash)
            End Using

            oResult = oHashString.Replace("-", "")
            Return oResult
        Catch ex As Exception
            Logger.Error(ex)
            Return ""
        End Try
    End Function
End Class
