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

LrDocStoreTypeMigrator.lua

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

Copyright 2020 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.utils
local KSDate = require 'KSDate'
local WFAnalytics = require 'WFAnalytics'
local WFArguments = require 'WFArguments'
local WFConfiguration = require 'WFConfiguration'
local WFFileUtils = require 'WFFileUtils'

	-- LrDocStoreMigrator
local LrDocStoreTypeMigratorImp = require 'LrDocStoreTypeMigratorImp'


--==============================================================================

local check = assert( WFArguments.check )

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

local LrDocStoreTypeMigrator = {}

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

--[[!

	\brief Determine whether or not we need to backfill the type and subtype
		fields for documents in the doc store.

	\return <code>number</code> The number of documents that need to be backfilled.
	
--]]

function LrDocStoreTypeMigrator.needBackFillTypeAndSubtype( path )

	-- In memory or temp docStores do not need a type/subtype back fill, neither does the
	-- oz-user docStore. Restrict the back fill to actual on disk "Managed Catalog"
	-- type docStores.
	
	if not string.find( path, "Managed Catalog" ) then
		return 0
	end
	
	return LrDocStoreTypeMigratorImp.needBackFillTypeAndSubtype( path )

end

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


function LrDocStoreTypeMigrator.docsWithTypeBackFillRequired( wfDocStoreImp )

	check( 'WFRxDocStoreImp' )

	-- In memory or temp docStores do not need a type/subtype back fill, neither does the
	-- oz-user docStore. Restrict the back fill to actual on disk "Managed Catalog"
	-- type docStores.
	
	if not string.find( wfDocStoreImp:path(), "Managed Catalog" ) then
		return 0
	end

	-- Check the type column of the document with the highest local docId for NULL value.
	-- If it is NULL we need to back fill the type and subtype columns of all docs.
	local maxLocalDocIdTypeRow = wfDocStoreImp._db:execOnce(
		"SELECT type, localDocId FROM docs WHERE localdocId = (SELECT MAX( localDocId ) FROM docs )" )

	if maxLocalDocIdTypeRow and maxLocalDocIdTypeRow.type == nil then
		return maxLocalDocIdTypeRow.localDocId
	end

	return 0

end

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

local function logTypeAndSubtypeExtractionFailureEvent( errorMessage )

	local event = {}

	event['event.type'] = 'error'
	event['event.subtype'] = 'type-and-subtype-extraction-failure'
	event['event.userWorkflow'] = 'LAUNCH'
	event['event.subcategory'] = 'Migration'
	event['event.errorMessage'] = errorMessage
	
	WFAnalytics.trackEvent( event )
	
end

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

local function logTypeAndSubtypeExtractionDurationEvent( duration )
	
	local durationEventThreshold = 
			WFConfiguration.TypeAndSubtypeExtraction.durationEventThreshold or 
			math.huge

	-- Only log a duration event if verboseAnalytics is enabled, or if the
	-- duration exceeds the conigured threshold. (configured via 
	-- WFConfiguration.TypeAndSubtypeExtraction.durationEventThreshold)

	if duration > durationEventThreshold or
			WFConfiguration.TypeAndSubtypeExtraction.verboseAnalytics then

		local event = {}
		
		event['event.type'] = 'info'
		event['event.subtype'] = 'type-and-subtype-extraction-duration'
		event['event.userWorkflow'] = 'LAUNCH'
		event['event.subcategory'] = 'Migration'
		event['event.duration'] = duration
		
		WFAnalytics.trackEvent( event )
	
	end
	
end

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

local function logNotPlausibleTypeOrSubtypeEvent( notPlausibleTypesAndSubtypes )

	if notPlausibleTypesAndSubtypes and
		notPlausibleTypesAndSubtypes ~= "" then

		local event = {}

		event['event.type'] = 'warning'
		event['event.subtype'] = 'type-and-subtype-extraction-warning'
		event['event.userWorkflow'] = 'LAUNCH'
		event['event.subcategory'] = 'Migration'
		event['event.notPlausibleTypesAndSubtypes'] = notPlausibleTypesAndSubtypes
		WFAnalytics.trackEvent( event )
	
	end

end

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

local function logPlausibleTypeOrSubtypeEvent( plausibleTypesAndSubtypes )

	if plausibleTypesAndSubtypes and
		plausibleTypesAndSubtypes ~= "" and
			WFConfiguration.TypeAndSubtypeExtraction.verboseAnalytics then

		local event = {}

		event['event.type'] = 'warning'
		event['event.subtype'] = 'type-and-subtype-extraction-warning'
		event['event.userWorkflow'] = 'LAUNCH'
		event['event.subcategory'] = 'Migration'
		event['event.plausibleTypesAndSubtypes'] = plausibleTypesAndSubtypes
		WFAnalytics.trackEvent( event )
		
	end
	
end

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

function LrDocStoreTypeMigrator.backFillTypeAndSubtype( wfDocStore )

	check( 'WFRxDocStoreImp' )

	local startBackFillTimestamp = KSDate.timeNow()

	local success, msg, plausibleTypesAndSubtypes, notPlausibleTypesAndSubtypes
			= LrDocStoreTypeMigratorImp.backFillTypeAndSubtype( wfDocStore._db._db )

	local backFillDuration = KSDate.timeNow() - startBackFillTimestamp

	logTypeAndSubtypeExtractionDurationEvent( backFillDuration )

	if not success then
	
		logTypeAndSubtypeExtractionFailureEvent( msg )
	
	else
	
		logPlausibleTypeOrSubtypeEvent( plausibleTypesAndSubtypes )
		logNotPlausibleTypeOrSubtypeEvent( notPlausibleTypesAndSubtypes )

	end
	
	return success, msg, plausibleTypesAndSubtypes, notPlausibleTypesAndSubtypes
	
end

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

function LrDocStoreTypeMigrator.canBackFillTypeAndSubtype( dbPath )

	local numDocsNeedBackFillTypeAndSubtype = LrDocStoreTypeMigrator.needBackFillTypeAndSubtype( dbPath )

	if numDocsNeedBackFillTypeAndSubtype > 0 then

		-- SPAM [jandrew 2021-06-17]: In testing on my own catalog, I found that the
		-- new type and subtype columns added around 215 bytes per doc to the doc store.
		-- Rounding up to 300 just to be safe.

		local spaceNeededPerDoc = 300 -- bytes

		local spaceNeeded = numDocsNeedBackFillTypeAndSubtype * spaceNeededPerDoc

		local success, freeSpace = pcall( WFFileUtils.freeSpaceAtPath, dbPath )

		freeSpace = success and freeSpace or nil

		-- If we fail to fetch disk space, then we continue on as if we have enough
		-- space.

		if freeSpace and freeSpace < spaceNeeded then

			return false, spaceNeeded

		end

	end
	
	return true

end

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

return LrDocStoreTypeMigrator
