--[[----------------------------------------------------------------------------

NmbDeletionBacklogMessageProcessor.lua

--------------------------------------------------------------------------------

ADOBE CONFIDENTIAL
------------------
Copyright 2022 Adobe
All Rights Reserved.

NOTICE: All information contained herein is, and remains
the property of Adobe and its suppliers, if any. The intellectual
and technical concepts contained herein are proprietary to Adobe
and its suppliers and are protected by all applicable intellectual
property laws, including trade secret and copyright laws.
Dissemination of this information or reproduction of this material
is strictly forbidden unless prior written permission is obtained
from Adobe.

----------------------------------------------------------------------------]]--

	-- wf.core
local WFCloseSupport = require "WFCloseSupport"
local KSPathUtils = require 'KSPathUtils'
local WFFileUtils = require 'WFFileUtils'
local WFRxRunUntilDone = require 'WFRxRunUntilDone'

	-- WF.core.lua.threading
local WFRequestDispatcher = require 'WFRequestDispatcher'

	-- WF.lightroom
local NmbDeletionBacklogImp = require 'NmbDeletionBacklogImp'
local NmbDeletionBacklogUpdater = require 'NmbDeletionBacklogUpdater'

	-- WF.oz_client.managed_catalog
local WFRxDocStore = require 'WFRxDocStore'

	-- WF.web.json
local KSJsonDecoder = require 'KSJsonDecoder'

	-- Rx
local Rx = require "Rx"

	-- WF.sqlite.database
local WFSQLiteDatabase = require 'WFSQLiteDatabase'
local WFSQLiteDatabaseSchema = require 'WFSQLiteDatabaseSchema'

--------------------------------------------------------------------------------

local runUntilDone = assert( WFRxRunUntilDone )

--------------------------------------------------------------------------------

local NmbDeletionBacklogMessageProcessor = {
	
	_docStore = false,
		-- (WFRxDocStore)

	_db = false,
		-- (WFSQLiteDatabase)
		
	_dbPath = false,
		-- (string)
		
	_deletionBacklog = false,
		-- (NmbDeletionBacklog)
		
	WFCloseSupport.addCloseSupportMethods,

}

--------------------------------------------------------------------------------

local function buildDbSchema()

	local schema = WFSQLiteDatabaseSchema( '1.0' )

	-- Deletion Backlog
	
	schema:addTable
			[[ CREATE TABLE deletionBacklog( volumeGuid, relativePath )]]
	
	schema:addTable
			[[ CREATE TABLE deletionBacklogV2( localId, binaryType, volumeGuid, relativePath )]]
	
	schema:addTable [[ CREATE TEMPORARY TABLE TEMP_ids( docId PRIMARY KEY )]]
	
	return schema

end

--------------------------------------------------------------------------------

local function maybeExtractChangeCounter( docStore, db )

	local doesTableStatusExist = db:gatherArray( 
  			[[ SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'tableStatus' ]] )
  			
	local maybeSetChangeCounter = Rx.Observable.just( true )
			
  	if doesTableStatusExist and doesTableStatusExist[ 1 ] then
	
		local key = 'deletionBacklogContinueFrom'
	
		local changeCounter = db:gatherArray( 
			[[ SELECT needsUpdateFrom FROM tableStatus WHERE tableName = 'tableStatus' ]] )[ 1 ] or 0
					
		maybeSetChangeCounter = docStore:setLocalDocValue {
			_id = key,
			continueFrom = changeCounter,
		}
	
	end

	return maybeSetChangeCounter

end

--------------------------------------------------------------------------------

local function cleanupLegacyTables( db )

	-- These tables were previously a part of the mru.db and were used for the
	-- old purge agent.

	db:exec( [[ BEGIN TRANSACTION ]] )

	db:exec( [[ DROP TABLE IF EXISTS lastAccess ]] )
	db:exec( [[ DROP TABLE IF EXISTS lastAccessV2 ]] )
	db:exec( [[ DROP TABLE IF EXISTS lastAccessV3 ]] )
	db:exec( [[ DROP TABLE IF EXISTS tableStatus ]] )
	db:exec( [[ DROP TABLE IF EXISTS assetInfo ]] )
    db:exec( [[ DROP TABLE IF EXISTS lastAccessTimestamps ]] )

	db:exec( [[ COMMIT ]] )

end

--------------------------------------------------------------------------------

function NmbDeletionBacklogMessageProcessor:__init( docStorePath, dbPath, optArgs )

	optArgs = optArgs or {}

	local docStore = WFRxDocStore( docStorePath )

	self._dbPath = dbPath

	if dbPath ~= ':memory:' then
		local parentDir = KSPathUtils.parent( dbPath )
		WFFileUtils.mkdir( parentDir )
	end

	local schema = buildDbSchema()

	local db = WFSQLiteDatabase( dbPath, schema )

	local deletionBacklog = NmbDeletionBacklogImp( docStore, db, optArgs )

	maybeExtractChangeCounter( docStore, db )
	
		:map( function()
		
			NmbDeletionBacklogUpdater.trackAssetUpdates( docStore, deletionBacklog, optArgs.writeInterval )
			
			cleanupLegacyTables( db )
			
			return true
			
		end )
		
		:call( runUntilDone )
	
	self:setOwned {
		_docStore = docStore,
		_deletionBacklog = deletionBacklog,
		_db = db
	}
	
end

--------------------------------------------------------------------------------

function NmbDeletionBacklogMessageProcessor:haltAndDelete()

	local path = self._dbPath

	self:close() -- should cause DB object and friends to close

	if path ~= ':memory:' then

		WFFileUtils.deleteFile( path )

		WFFileUtils.deleteFile( path .. '-shm' )

		WFFileUtils.deleteFile( path .. '-wal' )

	end

	return Rx.Observable.empty()

end

--------------------------------------------------------------------------------

local NmbDeletionBacklogDatabaseMessageProcessor = 
		Rx.Class( 'NmbDeletionBacklogDatabaseMessageProcessor', 
					NmbDeletionBacklogMessageProcessor )

--------------------------------------------------------------------------------

local function initMessageProcessor( encodedPackedThreadArgs )

	local packedThreadArgs = KSJsonDecoder.decodeWebSafeJson(
								encodedPackedThreadArgs )

	local messageProcessor = NmbDeletionBacklogDatabaseMessageProcessor( 
			unpack( packedThreadArgs ) )

	WFCloseSupport.mustCloseAtThreadTermination( messageProcessor )

	----------------------------------------------------------------------------
	
	local deletionBacklog = messageProcessor._deletionBacklog
	
	local deletionBacklogMethods = {
			
		'addToDeletionBacklog',
		'removeFromDeletionBacklog',
		'bulkAddToDeletionBacklog',
		'bringDeletionBacklogUpToDate',
		'processDeletionBacklog',
		'DEBUG_processedDeletionBacklogStream',
		'DEBUG_dumpDeletionBacklogContents',
		'DEBUG_dumpDeletionBacklogContentsV1',
		'DEBUG_buildDeletionBacklogWhereClause'
	
	}
	
	for _, fn in ipairs( deletionBacklogMethods ) do
	
		WFRequestDispatcher.onMessageDo {
			message = fn,
			target = deletionBacklog,
		}
	
	end
	
	----------------------------------------------------------------------------

	WFRequestDispatcher.onMessageDo {
		message = 'haltAndDelete',
		target = messageProcessor,
	}

	WFRequestDispatcher.enableDiagnosticLogForRequestStats()

end

--------------------------------------------------------------------------------

return {
	initMessageProcessor = initMessageProcessor,
}
