Imports System.Data.SqlClient
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Database.MSSQLServer.TransactionMode
Imports DigitalData.Modules.Logging

Namespace Methods.IDB.NewFile
    Public Class NewFileMethod
        Inherits BaseMethod

        Private ReadOnly Connection As SqlConnection
        Private ReadOnly Transaction As SqlTransaction

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

            Connection = DatabaseIDB.GetConnection()
            Transaction = Connection.BeginTransaction()
        End Sub

        Public Function Run(pData As NewFile.NewFileRequest) As NewFile.NewFileResponse
            Dim oFilePath As String = Nothing

            Dim oExistingObjectId = Helpers.TestFileChecksumExists(pData.File.FileChecksum)
            If oExistingObjectId > 0 Then
                Return New NewFile.NewFileResponse(oExistingObjectId)
            End If

            Try

                Dim oObjectId = Helpers.NewObjectIdWithTransaction(pData.KindType, pData.BusinessEntity, pData.User.UserName, Connection, Transaction)
                If oObjectId = 0 Then
                    LogAndThrow("Could not create new ObjectId!")
                End If

                ' Find ObjectStore by Title
                Logger.Debug("Checking for DataStore [{0}].", pData.StoreName)
                Dim oStore = GlobalState.ObjectStores.Where(Function(store) store.Title.Equals(pData.StoreName, StringComparison.OrdinalIgnoreCase)).SingleOrDefault()

                If oStore Is Nothing Then
                    LogAndThrow($"DataStore [{pData.StoreName}] does not exist. Exiting.")
                End If

                ' Get Store base path
                Dim oBasePath As String = oStore.Path
                Logger.Debug("Store BasePath is [{0}]", oBasePath)

                ' Get directory by DateImported or, if not supplied, by current date
                Dim oSubDirectory As String
                If IsNothing(pData.File.FileImportedAt) Then
                    oSubDirectory = GetDateSubDirectory(Now)
                Else
                    oSubDirectory = GetDateSubDirectory(pData.File.FileImportedAt)
                End If
                Logger.Debug("Subdirectory is [{0}]", oSubDirectory)

                ' Check and create final path, if necessary
                Dim oFinalPath = IO.Path.Combine(oBasePath, oSubDirectory)
                If Not IO.Directory.Exists(oFinalPath) Then
                    Try
                        Logger.Debug("Path does not exist, creating: [{0}]", oFinalPath)
                        IO.Directory.CreateDirectory(oFinalPath)
                        Logger.Debug("Created folder [{0}]", oFinalPath)
                    Catch ex As Exception
                        LogAndThrow(ex, $"Store Directory [{oFinalPath}] could not be created!")
                    End Try
                End If
                Logger.Debug("Final Directory is [{0}]", oFinalPath)

                ' Get filename
                Dim oKeepFileName As Boolean = False
                If oStore.IsArchive Then
                    Logger.Debug("Object Store is an archive: [{0}]", oStore.IsArchive)
                    oKeepFileName = True
                End If

                Dim oFileName As String = GetFileObjectFileName(oObjectId, pData.File.FileName, oKeepFileName)
                Logger.Debug("Filename is [{0}]", oFileName)

                oFilePath = IO.Path.Combine(oFinalPath, oFileName)
                Dim oFileObjectInfo As IO.FileInfo = New IO.FileInfo(oFilePath)

                Dim oFileObjectSize As Long = pData.File.FileContents.Length
                Dim oFileObjectName As String = oFileObjectInfo.Name
                Dim oOriginalExtension As String = pData.File.FileInfoRaw.Extension.Substring(1)

                Logger.Debug("File Information for [{0}]:", oFileObjectName)
                Logger.Debug("Size: [{0}]", oFileObjectSize)
                Logger.Debug("Original Extension: [{0}]", oOriginalExtension)
                Logger.Debug("Checksum: [{0}]", pData.File.FileChecksum)

                Try
                    Using oStream = New IO.FileStream(oFilePath, IO.FileMode.Create, IO.FileAccess.Write)
                        Logger.Info("Saving file to path [{0}]", oFilePath)
                        oStream.Write(pData.File.FileContents, 0, oFileObjectSize)
                        oStream.Flush(True)
                        oStream.Close()
                    End Using
                Catch ex As Exception
                    LogAndThrow(ex, $"Could not write file [{oFilePath}] to disk!")
                End Try

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

                Logger.Info("Creating IDB FileObject for ObjectId [{0}].", oObjectId)

                ' Insert into DB
                Dim oSQL As String = $"EXEC PRIDB_NEW_IDBFO 
                '{oFinalPath}', 
                '{oFileObjectName}', 
                '{oOriginalExtension}',
                {oFileObjectSize},
                '{pData.File.FileChecksum}' ,
                '{pData.User.UserName}',
                '{oObjectId}',
                {oStore.Id}"

                Dim oResult As Boolean = DatabaseIDB.ExecuteNonQueryWithConnectionObject(oSQL, Connection, ExternalTransaction, Transaction)

                If oResult = False Then
                    LogAndThrow("IDB FileObject could not be created!")
                End If

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

                Dim oSystemAttributes As New Dictionary(Of String, Object) From {
                    {"OriginFileName", pData.File.FileName},
                    {"OriginCreationDatetime", pData.File.FileCreatedAt},
                    {"OriginChangedDatetime", pData.File.FileChangedAt}
                }

                For Each oAttribute As KeyValuePair(Of String, Object) In oSystemAttributes
                    Try
                        ' Dont write empty attributes
                        If oAttribute.Value Is Nothing Then
                            Continue For
                        End If

                        Dim oSuccess = Helpers.SetAttributeValue(Connection, Transaction, oObjectId, oAttribute.Key, oAttribute.Value, pData.User.Language, pData.User.UserName)
                        If oSuccess Then
                            Logger.Debug("System Attribute [{0}] written with value [{1}]", oAttribute.Key, oAttribute.Value)
                        Else
                            Logger.Warn("System attribute value could not be written")
                        End If
                    Catch ex As Exception
                        LogAndThrow(ex, $"System attribute [{oAttribute.Key}] could not be written!")
                    End Try
                Next

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

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

                Return New NewFile.NewFileResponse(oObjectId)
            Catch ex As Exception
                Logger.Warn("Error occurred while creating file!")
                Logger.Error(ex)

                Logger.Info("Cleaning up files.")
                If Not IsNothing(oFilePath) AndAlso IO.File.Exists(oFilePath) Then
                    Try
                        IO.File.Delete(oFilePath)
                    Catch exInner As Exception
                        Logger.Warn("Error while cleaning up files.")
                        Logger.Error(exInner)
                    End Try
                End If

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

                Return New NewFile.NewFileResponse(ex)

            End Try
        End Function

        Private Function GetFileObjectFileName(IDB_OBJ_ID As Long, pFilename As String, pKeepFilename As Boolean) As String
            ' TODO: save actual extensions
            If pKeepFilename Then
                Return pFilename
            Else
                Return $"{IDB_OBJ_ID}.ddfo"
            End If
        End Function

        Private Function GetDateSubDirectory(pDate As Date) As String
            Return IO.Path.Combine(pDate.ToString("yyyy"), pDate.ToString("MM"), pDate.ToString("dd"))
        End Function
    End Class

End Namespace