Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Logging
Imports DigitalData.Modules.Filesystem
Imports DigitalData.Modules
Imports System.IO
Imports System.ServiceModel
Imports System.Data.SqlClient

<ServiceBehavior(InstanceContextMode:=InstanceContextMode.PerSession)>
Public Class EDMIService
    Implements IEDMIService

    Public Shared LogConfig As LogConfig
    Public Shared MSSQL As MSSQLServer
    Public Shared Firebird As Firebird
    Public Shared AppConfig As AppConfig
    Public Shared Filesystem As Filesystem.File
    Public Shared EDMIArchive As EDMI.File.Archive
    Public Shared GlobalState As GlobalState

    Private ReadOnly _logger As Logger

    Private _request As Request = Nothing
    Private _debug As Boolean = False
    Private _username As String


    Public Sub New()
        Dim oOperationContext As OperationContext = OperationContext.Current
        Dim oInstanceContext As InstanceContext = oOperationContext.InstanceContext
        Dim oUsername = StripDomainFromUsername(oOperationContext.ServiceSecurityContext.WindowsIdentity.Name)

        _username = oUsername
        _logger = LogConfig.GetLogger()
        _logger.Debug("New Request by User [{0}]", _username)


    End Sub

    Public Function StripDomainFromUsername(UserName As String)
        If UserName.Contains("\") Then
            Return UserName.Split("\")(1)
        ElseIf UserName.Contains("@") Then
            Return UserName.Split("@")(0)
        Else
            Return UserName
        End If
    End Function

#Region "=== Authorization ==="
    Private Function TestUserAuth() As Boolean
        Try
            'Dim oSQL As String = $"SELECT FNIDB_AUTH_USER('{_username}') FROM RDB$DATABASE;"
            'Dim oResult As Boolean = Database.GetScalarValue(oSQL)
            'Return oResult
            Return True
        Catch ex As Exception
            _logger.Error(ex)
            Return False
        End Try
    End Function
#End Region
#Region "=== Heartbeat ==="
    Public Function Heartbeat() As Boolean Implements IEDMIService.Heartbeat
        Return True
    End Function
#End Region
#Region "=== Database Request ==="
    Public Sub CreateRequest(Name As String, Optional Debug As Boolean = False)
        _request = New Request(Name, _username, Firebird, Debug)
        _debug = Debug

        _logger.Info("Creating request {0}/{1}", _request.Name, _request.RequestId)
    End Sub

    Public Function CreateDatabaseRequest(Name As String, Optional Debug As Boolean = False) As String Implements IEDMIService.CreateDatabaseRequest
        CreateRequest(Name, Debug)

        Return _request.Name
    End Function

    Public Sub CloseDatabaseRequest() Implements IEDMIService.CloseDatabaseRequest
        If IsNothing(_request) Then
            Exit Sub
        End If

        _logger.Info("Closing request {0}", _request.ToString)
        _request.Connection.Close()
        _request = Nothing
    End Sub

#End Region
#Region "=== Database (Firebird) ==="
    Private Sub TestRequestCreated()
        If IsNothing(_request) Then
            Throw New Exceptions.NoRequestException()
        End If
    End Sub

    Public Function ReturnDatatable(SQL As String) As TableResult Implements IEDMIService.ReturnDatatable
        Try
            TestRequestCreated()

            _logger.Info($"ReturnDatatable, SQL: {SQL}")
            _request.LogDebug($"ReturnDatatable, SQL: {SQL}")

            Dim oResult As DataTable = Firebird.GetDatatableWithConnection(SQL, _request.Connection)
            Return New TableResult(oResult)
        Catch ex As Exception
            _logger.Error(ex)
            _request.LogError(ex.Message)
            Return New TableResult(ex.Message)
        End Try
    End Function

    Public Function ReturnScalar(SQL As String) As ScalarResult Implements IEDMIService.ReturnScalar
        Try
            TestRequestCreated()

            _logger.Info($"ReturnScalar, SQL: {SQL}")
            _request.LogDebug($"ReturnScalar, SQL: {SQL}")

            Dim oResult As Object = Firebird.GetScalarValueWithConnection(SQL, _request.Connection)
            Return New ScalarResult(oResult)
        Catch ex As Exception
            _logger.Error(ex)
            _request.LogError(ex.Message)
            Return New ScalarResult(ex.Message)
        End Try
    End Function

    Public Function ExecuteNonQuery(SQL As String) As NonQueryResult Implements IEDMIService.ExecuteNonQuery
        Try
            TestRequestCreated()

            _logger.Info($"ExecuteNonQuery, SQL: {SQL}")
            _request.LogDebug($"ExecuteNonQuery, SQL: {SQL}")

            Dim oResult As Boolean = Firebird.ExecuteNonQueryWithConnection(SQL, _request.Connection)
            Return New NonQueryResult()
        Catch ex As Exception
            _logger.Error(ex)
            _request.LogError(ex.Message)
            Return New NonQueryResult(ex.Message)
        End Try
    End Function
#End Region
#Region "=== Document ==="
    ''' <summary>
    ''' Imports a file according to ObjectStoreId
    ''' </summary>
    ''' <returns></returns>
    Public Function ImportFile(Data As Messages.DocumentImportRequest) As Messages.DocumentImportResponse Implements IEDMIService.ImportFile
        Dim oObjectStore = GlobalState.ObjectStores.
            Where(Function(s) s.Id = Data.ObjectStoreId).
            FirstOrDefault()

        If oObjectStore Is Nothing Then
            Throw New FaultException($"Object Store with Id [{Data.ObjectStoreId}] does not exist!")
        End If

        Dim EDMIPath = New EDMI.File.Path(LogConfig, oObjectStore.Path)

        ' TODO: 
        ' - Get Object Store -> Object Catalog -> Catalog Path
        ' - If IS_ARCHIVE = True And RetentionDays <> Nothing:
        '   DoArchive!
        ' - Refactor EDMIPath to get ObjectStore at service start up
        '   and return ObjectStore Path from ObjectStoreId + RelativePath
        ' VWIDB_OBJECTSTORE

        Dim oRelativePath As String = EDMIPath.GetRelativePath(Data.DocumentType, Data.FileName)
        Dim oAbsolutePath As String = EDMIPath.GetFullPath(Data.DocumentType, Data.FileName)
        Dim oDirectoryPath = EDMIPath.GetFullPath(Data.DocumentType)

        Try
            Directory.CreateDirectory(oDirectoryPath)
        Catch ex As Exception
            _logger.Error(ex)
            Throw New FaultException(ex.Message)
        End Try

        Try
            Dim oVersionedFileName As String = Filesystem.GetVersionedFilename(oAbsolutePath)

            _logger.Info("ImportFile: Saving file [{0}] to path [{1}]", Data.FileName, oVersionedFileName)
            Using oStream = New FileStream(oVersionedFileName, FileMode.CreateNew)
                oStream.Write(Data.Contents, 0, Data.Contents.Length)
                oStream.Flush(True)
                oStream.Close()
            End Using

            ' insert into db
            Dim oCommand As New SqlCommand("PRIDB_NEW_DOCUMENT")
            oCommand.Parameters.AddWithValue("@OBJ_ST_ID", 1)
            oCommand.Parameters.AddWithValue("@REL_PATH", oRelativePath)
            oCommand.Parameters.AddWithValue("@WHO", _username)
            oCommand.Parameters.AddWithValue("@REF_DOCID", 0)
            oCommand.Parameters.Add(New SqlParameter("@IDB_OBJ_ID", SqlDbType.BigInt))

            Dim oObjectId = MSSQL.GetScalarValue(oCommand, "@IDB_OBJ_ID")

            Return New Messages.DocumentImportResponse() With {.ObjectId = oObjectId}
        Catch ex As Exception
            _logger.Error(ex)
            Throw New FaultException(ex.Message)
        End Try
    End Function

    Public Function GetFileByObjectId(Data As Messages.DocumentStreamRequest) As Messages.DocumentStreamResponse Implements IEDMIService.GetFileByObjectId
        Try
            Dim oSQL As String = $"SELECT ObjectStoreId FROM VWIDB_DOC_DATA WHERE IDB_OBJ_ID = {Data.ObjectId}"
            Dim oObjectStoreId = MSSQL.GetScalarValue(oSQL)

            Dim oObjectStore = GlobalState.ObjectStores.
                Where(Function(s) s.Id = oObjectStoreId).
                FirstOrDefault()

            Dim oSQL2 As String = $"SELECT DocRelativePath FROM VWIDB_DOC_DATA WHERE IDB_OBJ_ID = {Data.ObjectId}"
            Dim oPath As String = MSSQL.GetScalarValue(oSQL2)


            If IsNothing(oPath) Then
                Throw New FaultException($"Object [{Data.ObjectId}] does not exist in database!")
            End If

            Dim EDMIPath As New EDMI.File.Path(LogConfig, oObjectStore.Path)
            Dim oFullPath = EDMIPath.GetFullPathFromRelativePath(oPath)

            _logger.Debug("GetFileByObjectId: Loading file [{0}]", oFullPath)

            Dim oFileInfo As New FileInfo(oFullPath)

            If Not oFileInfo.Exists Then
                Throw New FaultException($"Object [{Data.ObjectId}] does not exist on filesystem!")
            End If

            Dim oDestination As New MemoryStream()
            Using oSource As FileStream = IO.File.OpenRead(oFullPath)
                oSource.CopyTo(oDestination)
            End Using
            oDestination.Seek(0, SeekOrigin.Begin)

            Dim oMessage As New Messages.DocumentStreamResponse() With {
                .FileName = oFileInfo.Name,
                .FileContents = oDestination
            }

            Return oMessage
        Catch ex As IOException
            _logger.Error(ex)
            Throw New FaultException($"Object [{Data.ObjectId}] could not be streamed!")
        Catch ex As Exception
            _logger.Error(ex)
            Throw New FaultException(ex.Message)
        End Try
    End Function

    Public Function ListFilesForUser() As Messages.DocumentListResponse Implements IEDMIService.ListFilesForUser
        Try
            Dim oSQL = $"SELECT * FROM VWIDB_DOC_DATA"
            Dim oDatatable As DataTable = MSSQL.GetDatatable(oSQL)
            oDatatable.TableName = "DocumentList"

            Return New Messages.DocumentListResponse() With {
                .Datatable = oDatatable
            }
        Catch ex As Exception
            _logger.Error(ex)
            Throw New FaultException(ex.Message)
        End Try
    End Function
#End Region
End Class