Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Logging
Imports DigitalData.Modules.Patterns
Imports DigitalData.Modules.Language
Imports DigitalData.Services.EDMIService.Methods.GetDatatableFromCache
Imports System.IO
Imports DigitalData.Modules.ZooFlow.State

Namespace Methods.GlobalIndexer.ImportFile
    Public Class ImportFileMethod
        Inherits BaseMethod

        Private ReadOnly Patterns As Patterns2
        Private ReadOnly GetDatatable As GetDatatableFromCacheMethod
        Private ReadOnly Connection As SqlClient.SqlConnection
        Private ReadOnly Transaction As SqlClient.SqlTransaction

        Private User As UserState
        Private Profile As DataRow

        Private Const VIEW_PROFILE = "VWGI_DOCTYPE_IDB"
        Private Const VIEW_INDEX_MANUAL = "VWDDINDEX_MAN"
        Private Const VIEW_INDEX_AUTOMATIC = "VWDDINDEX_AUTOM"
        Private Const TABLE_POST_PROCESSING = "TBDD_INDEX_MAN_POSTPROCESSING"

        Public Sub New(pLogConfig As LogConfig, pMSSQLServer As MSSQLServer, pGlobalState As GlobalState)
            MyBase.New(pLogConfig, pMSSQLServer, pGlobalState)

            Patterns = New Patterns2(pLogConfig)
            GetDatatable = New GetDatatableFromCacheMethod(LogConfig, Database, GlobalState)
            Connection = Database.GetConnection()
            Transaction = Connection.BeginTransaction()
        End Sub

        ''' <summary>
        ''' 
        ''' </summary>
        ''' <remarks>
        ''' 
        ''' 
        ''' 
        ''' </remarks>
        Public Function Run(pData As ImportFileRequest)
            Try
                User = pData.User

                ' TODO: Add missing user properties in UserState from TBDD_USER
                'pData.User = ResolveUserFromUserName(pData.User.UserName)

                Dim oManualIndexes = LoadManualIndexes(pData.ProfileId)
                Dim oAutomaticIndexes = LoadAutomaticIndexes(pData.ProfileId)
                Dim oPostProcessingSteps As DataTable = LoadPostProcessingSteps(oManualIndexes)
                LoadProfile(pData.ProfileId)

                Dim oUserAttributes = pData.AttributeValues
                Dim oAutoAttributes As List(Of UserAttributeValue) = Nothing

                ' Apply post processing
                Dim oPostProcessing = New Steps.PostProcessing(LogConfig, oPostProcessingSteps)
                oUserAttributes = oPostProcessing.ApplyManualPostprocessing(oUserAttributes)

                ' Apply automatic attributes
                Dim oAutomaticIndexing = New Steps.AutomaticIndexing(LogConfig, Database, oAutomaticIndexes, GlobalState)
                oAutoAttributes = oAutomaticIndexing.ApplyAutomaticeAttributes(oUserAttributes, pData.File.FileInfoRaw, User)

                ' Import the file
                Dim oNewFile As New NewFileMethod(LogConfig, Database, GlobalState)
                Dim oResponse = oNewFile.Run(New NewFile.NewFileRequest With {
                    .File = pData.File,
                    .BusinessEntity = pData.BusinessEntity,
                    .KindType = pData.KindType,
                    .StoreName = pData.StoreName,
                    .User = User
                })

                If oResponse.OK Then
                    Logger.Info("Import of file [{0}] under ObjectId [{1}] successful!", pData.File.FileName, oResponse.ObjectId)
                Else
                    Throw New ApplicationException(oResponse.ErrorMessage)
                End If

                Logger.Info("Generating display filename for file [{0}]", pData.File.FileName)

                Dim oNameconvention As String = Profile.ItemEx(Of String)("NAMENKONVENTION")
                Dim oDisplayFilename = GetFilenameByNameconvention(pData.File.FileInfoRaw, oNameconvention, User, oUserAttributes, oAutoAttributes)

                Logger.Info("Collecting Attributes for ObjectId [{0}]", oResponse.ObjectId)

                Dim oFinalAttributes As New Dictionary(Of String, List(Of String)) From {
                    {"DisplayFileName", New List(Of String) From {oDisplayFilename}}
                }
                oFinalAttributes = oFinalAttributes.
                    Concat(Helpers.UserAttributesToDictionary(oUserAttributes)).
                    Concat(Helpers.UserAttributesToDictionary(oAutoAttributes)).
                    ToDictionary(Function(kv) kv.Key, Function(kv) kv.Value)

                Logger.Info("Writing [{0}] Attributes for ObjectId [{0}] ", oResponse.ObjectId)
                WriteAttributeValues(oResponse.ObjectId, oFinalAttributes)

                ' Finally, commit the transaction
                Transaction?.Commit()

                Return New ImportFileResponse(oResponse.ObjectId)

            Catch ex As Exception
                Logger.Warn("Error occurred while importing file!")
                Logger.Error(ex)

                Logger.Info("Rolling back transaction.")
                Transaction?.Rollback()

                Return New ImportFileResponse(ex)
            End Try
        End Function

        Private Function GetFilenameByNameconvention(pFileInfo As FileInfo, pNameconvention As String, pUser As UserState, pAttributes As List(Of UserAttributeValue), pAutoAttributes As List(Of UserAttributeValue)) As String
            Dim oAttributeDict = Helpers.UserAttributesToDictionary(pAttributes)
            Dim oAutoAttributeDict = Helpers.UserAttributesToDictionary(pAutoAttributes)

            If pNameconvention Is Nothing OrElse pNameconvention = String.Empty Then
                Logger.Warn("Nameconvention for File [{0}] was empty. Returning original filename.", pFileInfo.Name)
                Return pFileInfo.Name

            End If

            Dim oFileName As String = Helpers.GetPlaceholderValue(pNameconvention, pFileInfo, pUser, oAttributeDict, oAutoAttributeDict)
            Return oFileName & pFileInfo.Extension
        End Function

        Private Sub WriteAttributeValues(pObjectId As Long, pAttributes As Dictionary(Of String, List(Of String)))
            For Each oAttribute As KeyValuePair(Of String, List(Of String)) In pAttributes
                Try
                    ' TODO: This works only for simple attributes for now!!!
                    Dim oValue = oAttribute.Value.FirstOrDefault()

                    ' Dont write empty attributes
                    If oValue Is Nothing Then
                        Continue For
                    End If

                    ' Now make the call to the database
                    Dim oSuccess = Helpers.SetAttributeValue(Connection, Transaction, pObjectId, oAttribute.Key, oValue, User.Language, User.UserName)
                    If oSuccess Then
                        Logger.Info("Attribute [{0}] written with value [{1}]", oAttribute.Key, oAttribute.Value)
                    Else
                        Logger.Warn("Attribute value could not be written")
                    End If
                Catch ex As Exception
                    LogAndThrow(ex, $"Attribute [{oAttribute.Key}] could not be written!")
                End Try
            Next
        End Sub

        ''' <summary>
        ''' Load Profiles for this Import 
        ''' </summary>
        Private Sub LoadProfile(pProfileId As Integer)
            Logger.Debug("Start of Method [LoadAutomaticIndexes]")

            Try
                Dim oProfile = GetDatatable.Run(
                    New GetDatatableFromCacheRequest With {
                        .DataTable = VIEW_PROFILE,
                        .FilterExpression = $"DOCTYPE_ID = {pProfileId}"
                    })

                If oProfile.OK = False Then
                    LogAndThrow(oProfile.ErrorMessage)
                End If

                Profile = oProfile.Table.Rows.Item(0)
            Catch ex As Exception
                LogAndThrow(ex, "Error while automatic loading indexes!")
            End Try
        End Sub

        ''' <summary>
        ''' Load automatic indexes for this Import
        ''' </summary>
        Private Function LoadAutomaticIndexes(pProfileId As Integer) As List(Of AutomaticIndex)
            Logger.Debug("Start of Method [LoadAutomaticIndexes]")

            Try
                ' Load automatic Indexes for this Import 
                Dim oAutomaticIndexes = GetDatatable.Run(
                    New GetDatatableFromCacheRequest With {
                        .DataTable = VIEW_INDEX_AUTOMATIC,
                        .FilterExpression = $"DOCTYPE_ID = {pProfileId}"
                    })

                If oAutomaticIndexes.OK = False Then
                    LogAndThrow(oAutomaticIndexes.ErrorMessage)
                End If

                Dim oIndexes As New List(Of AutomaticIndex)

                For Each oRow As DataRow In oAutomaticIndexes.Table.Rows
                    Dim oAutomaticIndex As New AutomaticIndex With {
                        .Id = oRow.ItemEx(Of Integer)("GUID"),
                        .Name = oRow.ItemEx(Of String)("INDEXNAME"),
                        .ProfileId = oRow.ItemEx(Of Integer)("DOCTYPE_ID"),
                        .SQLCommand = oRow.ItemEx(Of String)("SQL_RESULT"),
                        .SQLConnectionId = oRow.ItemEx(Of Integer)("CONNECTION_ID"),
                        .Sequence = oRow.ItemEx(Of String)("SEQUENCE"),
                        .Value = oRow.ItemEx(Of String)("VALUE")
                    }

                    oIndexes.Add(oAutomaticIndex)
                Next

                Logger.Info("Automatic indexes loaded: [{0}]", oIndexes.Count)

                Return oIndexes
            Catch ex As Exception
                LogAndThrow(ex, "Error while automatic loading indexes!")
            End Try
        End Function

        ''' <summary>
        ''' Load manual indexes for this Import
        ''' </summary>
        Private Function LoadManualIndexes(pProfileId As Integer) As List(Of ManualIndex)
            Logger.Debug("Start of Method [LoadManualIndexes]")

            Try
                ' Load manual Indexes for this Import 
                Dim oManualIndexes = GetDatatable.Run(
                    New GetDatatableFromCacheRequest With {
                        .DataTable = VIEW_INDEX_MANUAL,
                        .FilterExpression = $"DOK_ID = {pProfileId}"
                    })

                If oManualIndexes.OK = False Then
                    LogAndThrow(oManualIndexes.ErrorMessage)
                End If

                Dim oIndexes As New List(Of ManualIndex)

                For Each oRow As DataRow In oManualIndexes.Table.Rows
                    Dim oManualIndex As New ManualIndex With {
                        .Id = oRow.ItemEx(Of Integer)("GUID"),
                        .Name = oRow.ItemEx(Of String)("INDEXNAME"),
                        .ProfileId = oRow.ItemEx(Of Integer)("DOCTYPE_ID"),
                        .IsOptional = oRow.ItemEx(Of Boolean)("OPTIONAL"),
                        .IsMultiselect = oRow.ItemEx(Of String)("MULTISELECT"),
                        .SQLCommand = oRow.ItemEx(Of String)("SQL_RESULT"),
                        .SQLConnectionId = oRow.ItemEx(Of Integer)("CONNECTION_ID"),
                        .DefaultValue = oRow.ItemEx(Of String)("DEFAULT_VALUE"),
                        .DataType = oRow.ItemEx(Of String)("DATA_TYPE")
                    }

                    oIndexes.Add(oManualIndex)
                Next

                Logger.Info("Manual indexes loaded: [{0}]", oIndexes.Count)

                Return oIndexes

            Catch ex As Exception
                LogAndThrow(ex, "Error while loading indexes!")
            End Try
        End Function

        Private Function LoadPostProcessingSteps(pManualIndexes As List(Of ManualIndex)) As DataTable
            Logger.Debug("Start of Method [LoadPostProcessingSteps]")

            Try
                ' Generate a string containing all index ids joined into a string
                Dim oIndexIdList As List(Of Integer) = pManualIndexes.
                    Select(Function(index) index.Id).
                    ToList()
                Dim oIndexIds As String = String.Join(",", oIndexIdList)

                If oIndexIdList.Count = 0 Then
                    Logger.Debug("No Postprocessing steps found for this profile. Exiting.")
                    Return Nothing
                End If

                ' Load all relevant postprocessing steps
                Dim oPostProcessingSteps = GetDatatable.Run(
                    New GetDatatableFromCacheRequest With {
                        .DataTable = TABLE_POST_PROCESSING,
                        .FilterExpression = $"IDXMAN_ID IN ({oIndexIds})"
                    })

                If oPostProcessingSteps.OK = False Then
                    LogAndThrow(oPostProcessingSteps.ErrorMessage)
                End If

                Return oPostProcessingSteps.Table
            Catch ex As Exception
                LogAndThrow(ex, "Error while loading post processing steps!")
            End Try
        End Function
    End Class

End Namespace