local Log = RmLogging.getLogger("RLRM")

RL_AnimalScreenDealer = {}


function RL_AnimalScreenDealer:initItems()

    AnimalScreenDealer:superClass().initItems(self)
	
    self.husbandries = {}
	self.targetHusbandries = {}
	self.targetAnimalTypes = {}

    local placeables = g_currentMission.husbandrySystem:getPlaceablesByFarm()
    local animalSystem = g_currentMission.animalSystem

	for _, placeable in pairs(placeables) do

		local animalTypeIndex = placeable:getAnimalTypeIndex()

		if self.husbandries[animalTypeIndex] == nil then self.husbandries[animalTypeIndex] = {} end

		if placeable:getNumOfAnimals() > 0 then

			table.insert(self.targetAnimalTypes, animalSystem:getTypeByIndex(animalTypeIndex))
			table.insert(self.targetHusbandries, placeable)

		end

		table.insert(self.husbandries[animalTypeIndex], placeable)

	end

	table.sort(self.targetAnimalTypes, function(a, b) return a.typeIndex < b.typeIndex end)
	
    table.sort(self.targetHusbandries, function(a, b) return a:getAnimalTypeIndex() < b:getAnimalTypeIndex() end)

end

AnimalScreenDealer.initItems = Utils.overwrittenFunction(AnimalScreenDealer.initItems, RL_AnimalScreenDealer.initItems)


function RL_AnimalScreenDealer:setCurrentHusbandry(_, animalTypeIndex, index, isBuyMode)

    if isBuyMode then
		local husbandries = self.husbandries[animalTypeIndex]
		local husbandry
		if husbandries == nil then
			husbandry = nil
		else
			husbandry = husbandries[index] or nil
		end
		self.husbandry = husbandry
	else
		self.husbandry = self.targetHusbandries[index]
	end
	self:initTargetItems()

end

AnimalScreenDealer.setCurrentHusbandry = Utils.overwrittenFunction(AnimalScreenDealer.setCurrentHusbandry, RL_AnimalScreenDealer.setCurrentHusbandry)


function RL_AnimalScreenDealer:initTargetItems(_)

    self.targetItems = {}
    if self.husbandry == nil then return end
    local animals = self.husbandry:getClusters()

    if animals ~= nil then
        for _, animal in pairs(animals) do
            local item = AnimalItemStock.new(animal)
            table.insert(self.targetItems, item)
        end
    end

end

AnimalScreenDealer.initTargetItems = Utils.overwrittenFunction(AnimalScreenDealer.initTargetItems, RL_AnimalScreenDealer.initTargetItems)



function RL_AnimalScreenDealer:initSourceItems(_)

    self.sourceItems = {}
	local animalSystem = g_currentMission.animalSystem
	self.sourceAnimalTypes = animalSystem:getTypes()

	local animalTypes = {}

	if g_localPlayer == nil then return end
	local farm = g_localPlayer.farmId

	for _, placeable in pairs(g_currentMission.placeableSystem.placeables) do

		if placeable.ownerFarmId == farm and placeable.spec_husbandryAnimals then
		
			local animalType = placeable.spec_husbandryAnimals:getAnimalTypeIndex()
		    animalTypes[animalType] = true

		end

	end


	for index, animalType in pairs(self.sourceAnimalTypes) do

		local animals = animalSystem:getSaleAnimalsByTypeIndex(animalType.typeIndex)
    
		self.sourceItems[animalType.typeIndex] = {}

		for _, animal in pairs(animals) do
			local item = AnimalItemNew.new(animal)
			table.insert(self.sourceItems[animalType.typeIndex], item)
		end

	end

end

AnimalScreenDealer.initSourceItems = Utils.overwrittenFunction(AnimalScreenDealer.initSourceItems, RL_AnimalScreenDealer.initSourceItems)


function RL_AnimalScreenDealer:getSourceMaxNumAnimals(_, _)

    return 1

end

AnimalScreenDealer.getSourceMaxNumAnimals = Utils.overwrittenFunction(AnimalScreenDealer.getSourceMaxNumAnimals, RL_AnimalScreenDealer.getSourceMaxNumAnimals)


function RL_AnimalScreenDealer:applySource(_, animalTypeIndex, animalIndex)

    if self.husbandry == nil then return false end

    self.sourceAnimals = nil

    local item = self.sourceItems[animalTypeIndex][animalIndex]
    local husbandry = self.husbandry
    local ownerFarmId = husbandry:getOwnerFarmId()

    local price = -item:getPrice()
	local transportationFee = -item:getTranportationFee(1)

    local errorCode = AnimalBuyEvent.validate(husbandry, item:getSubTypeIndex(), item:getAge(), 1, price, transportationFee, ownerFarmId)

    if errorCode ~= nil then
		local error = AnimalScreenDealerFarm.BUY_ERROR_CODE_MAPPING[errorCode]
		self.errorCallback(g_i18n:getText(error.text))
		return false
	end

    local animal = item.animal or item.cluster

    self.sourceAnimals = { animal }

    self.actionTypeCallback(AnimalScreenBase.ACTION_TYPE_SOURCE, g_i18n:getText(AnimalScreenDealerFarm.L10N_SYMBOL.BUYING))
    g_messageCenter:subscribe(AnimalBuyEvent, self.onAnimalBought, self)
	g_client:getServerConnection():sendEvent(AnimalBuyEvent.new(husbandry, self.sourceAnimals, price, transportationFee))

    return true

end

AnimalScreenDealer.applySource = Utils.overwrittenFunction(AnimalScreenDealer.applySource, RL_AnimalScreenDealer.applySource)


function RL_AnimalScreenDealer:onAnimalBought(errorCode)

    if errorCode == AnimalBuyEvent.BUY_SUCCESS and self.sourceAnimals ~= nil then

        for _, animal in pairs(self.sourceAnimals) do g_currentMission.animalSystem:removeSaleAnimal(animal.animalTypeIndex, animal.birthday.country, animal.farmId, animal.uniqueId) end

    end

end

AnimalScreenDealer.onAnimalBought = Utils.prependedFunction(AnimalScreenDealer.onAnimalBought, RL_AnimalScreenDealer.onAnimalBought)


function RL_AnimalScreenDealer:applyTarget(_, animalTypeIndex, animalIndex)

    if self.husbandry == nil then return false end

    local item = self.targetItems[animalIndex]
    local husbandry = self.husbandry
    local ownerFarmId = husbandry:getOwnerFarmId()

    local price = item:getPrice()
	local transportationFee = -item:getTranportationFee(1)

    local errorCode = AnimalSellEvent.validate(husbandry, item:getClusterId(), 1, price, transportationFee)

    if errorCode ~= nil then
		local error = AnimalScreenDealerFarm.SELL_ERROR_CODE_MAPPING[errorCode]
		self.errorCallback(g_i18n:getText(error.text))
		return false
	end

    local animal = item.animal or item.cluster
    local clusterSystem = husbandry:getClusterSystem()
    local key = RLAnimalUtil.toKey(animal.farmId, animal.uniqueId, animal.birthday.country)
    local cluster = clusterSystem:getClusterById(key)

    if cluster == nil then
        Log:warning("AnimalScreenDealer:applyTarget: cluster not found for key=%s", tostring(key))
        self.errorCallback("Sale failed")
        return false
    end

    local ok, err = pcall(function() clusterSystem:addPendingRemoveCluster(cluster) end)
    local ok2, err2 = pcall(function() clusterSystem:updateNow() end)

    if not (ok and ok2) then
        Log:error("AnimalScreenDealer:applyTarget: removal failed key=%s queue=%s flush=%s",
            tostring(key), tostring(err), tostring(err2))
        self.errorCallback("Sale failed")
        return false
    end

    g_currentMission:addMoney(price + transportationFee, ownerFarmId, MoneyType.NEW_ANIMALS_COST, true, true)
    g_currentMission.animalSystem:removeSaleAnimal(animalTypeIndex, animal.birthday.country, animal.farmId, animal.uniqueId)
    table.remove(self.targetItems, animalIndex)

    self.targetActionFinished(nil, "Animal sold successfully")

    Log:debug("AnimalScreenDealer:applyTarget: sold animal uniqueId=%s farmId=%s price=%s",
        tostring(animal.uniqueId), tostring(animal.farmId), tostring(price + transportationFee))

    return true

end

AnimalScreenDealer.applyTarget = Utils.overwrittenFunction(AnimalScreenDealer.applyTarget, RL_AnimalScreenDealer.applyTarget)


function RL_AnimalScreenDealer:getSourcePrice(_, animalTypeIndex, animalIndex, _)

    if self.sourceItems[animalTypeIndex] ~= nil then

        local item = self.sourceItems[animalTypeIndex][animalIndex]

        if item ~= nil then

	        local price = item:getPrice()
	        local transportationFee = item:getTranportationFee(1)
	        return true, price, transportationFee, price + transportationFee

        end

    end

    return false, 0, 0, 0

end

AnimalScreenDealer.getSourcePrice = Utils.overwrittenFunction(AnimalScreenDealer.getSourcePrice, RL_AnimalScreenDealer.getSourcePrice)


function RL_AnimalScreenDealer:getTargetPrice(_, _, animalIndex, _)

    local item = self.targetItems[animalIndex]

    if item ~= nil then

        local price = item:getPrice()
        local transportationFee = -item:getTranportationFee(1)
        return true, price, transportationFee, price + transportationFee

    end

    return false, 0, 0, 0

end

AnimalScreenDealer.getTargetPrice = Utils.overwrittenFunction(AnimalScreenDealer.getTargetPrice, RL_AnimalScreenDealer.getTargetPrice)


function RL_AnimalScreenDealer:getTargetMaxNumAnimals(_, animalIndex)

    local item = self.targetItems[animalIndex]

    if item ~= nil then
        return item:getNumAnimals()
    end

    return 0

end

AnimalScreenDealer.getTargetMaxNumAnimals = Utils.overwrittenFunction(AnimalScreenDealer.getTargetMaxNumAnimals, RL_AnimalScreenDealer.getTargetMaxNumAnimals)


--- Pre-validate a single buy item before the confirmation dialog.
--- @param animalTypeIndex number The animal type index
--- @param itemIndex number The item index in sourceItems
--- @return number|nil errorCode from AnimalBuyEvent.validate, or nil if valid
function AnimalScreenDealer:preValidateBuyItem(animalTypeIndex, itemIndex)
    if self.husbandry == nil then return nil end
    local sourceItems = self.sourceItems[animalTypeIndex]
    if sourceItems == nil then return nil end
    local sourceItem = sourceItems[itemIndex]
    if sourceItem == nil then return nil end

    local animal = sourceItem.animal
    local price = -sourceItem:getPrice()
    local transportationFee = -sourceItem:getTranportationFee(1)

    return AnimalBuyEvent.validate(self.husbandry, animal.subTypeIndex, animal.age, 1, price, transportationFee, self.husbandry:getOwnerFarmId())
end


function AnimalScreenDealer:applySourceBulk(animalTypeIndex, items)

    if self.husbandry == nil then return false end

    self.sourceAnimals = {}

    local husbandry = self.husbandry
    local ownerFarmId = husbandry:getOwnerFarmId()

    local sourceItems = self.sourceItems[animalTypeIndex]
    local totalPrice = 0
    local totalTransportPrice = 0
    local totalBoughtAnimals = 0
    local skippedCount = 0
    local firstErrorCode = nil

    for _, item in pairs(items) do

        if sourceItems[item] ~= nil then

            local sourceItem = sourceItems[item]
            local animal = sourceItem.animal
            local price = -sourceItem:getPrice()
            local transportationFee = -sourceItem:getTranportationFee(1)

            local errorCode = AnimalBuyEvent.validate(husbandry, animal.subTypeIndex, animal.age, 1, price, transportationFee, ownerFarmId)

            if errorCode ~= nil then
                skippedCount = skippedCount + 1
                if firstErrorCode == nil then firstErrorCode = errorCode end
                Log:trace("Dealer.applySourceBulk: skipping '%s' (errorCode=%d)",
                    animal.name or animal.uniqueId or "?", errorCode)
                continue
            end

            totalBoughtAnimals = totalBoughtAnimals + 1
            totalPrice = totalPrice + price
            totalTransportPrice = totalTransportPrice + transportationFee

            table.insert(self.sourceAnimals, animal)

        end

    end

    Log:debug("Dealer.applySourceBulk: %d of %d passed validation, %d skipped",
        totalBoughtAnimals, totalBoughtAnimals + skippedCount, skippedCount)

    if totalBoughtAnimals == 0 and skippedCount > 0 then
        Log:debug("Dealer.applySourceBulk: all %d items rejected (firstError=%d)", skippedCount, firstErrorCode)
        local mapping = AnimalScreenDealerFarm.BUY_ERROR_CODE_MAPPING[firstErrorCode]
        if mapping ~= nil and self.errorCallback ~= nil then
            self.errorCallback(g_i18n:getText(mapping.text))
        end
        return
    end

    if skippedCount > 0 then
        Log:warning("Dealer.applySourceBulk: %d items skipped (firstError=%d)", skippedCount, firstErrorCode)
    end

    self.actionTypeCallback(AnimalScreenBase.ACTION_TYPE_SOURCE, g_i18n:getText(AnimalScreenDealerFarm.L10N_SYMBOL.BUYING))
    g_messageCenter:subscribe(AnimalBuyEvent, self.onAnimalBought, self)
    g_client:getServerConnection():sendEvent(AnimalBuyEvent.new(husbandry, self.sourceAnimals, totalPrice, totalTransportPrice))

end


function AnimalScreenDealer:applyTargetBulk(animalTypeIndex, items)

    if self.husbandry == nil then return false end

    self.targetAnimals = {}

    local husbandry = self.husbandry

    local targetItems = self.targetItems
    local totalPrice = 0
    local totalTransportPrice = 0
    local totalSoldAnimals = 0
    local skippedCount = 0
    local firstErrorCode = nil

    for _, item in pairs(items) do

        if targetItems[item] ~= nil then

            local targetItem = targetItems[item]
            local animal = targetItem.animal or targetItem.cluster
            local price = targetItem:getPrice()
            local transportationFee = -targetItem:getTranportationFee(1)

            local errorCode = AnimalSellEvent.validate(husbandry, targetItem:getClusterId(), 1, price, transportationFee)

            if errorCode ~= nil then
                skippedCount = skippedCount + 1
                if firstErrorCode == nil then firstErrorCode = errorCode end
                Log:trace("Dealer.applyTargetBulk: skipping '%s' (errorCode=%d)",
                    animal.name or animal.uniqueId or "?", errorCode)
                continue
            end

            totalSoldAnimals = totalSoldAnimals + 1
            totalPrice = totalPrice + price
            totalTransportPrice = totalTransportPrice + transportationFee

            table.insert(self.targetAnimals, animal)

        end

    end

    Log:debug("Dealer.applyTargetBulk: %d of %d passed validation, %d skipped",
        totalSoldAnimals, totalSoldAnimals + skippedCount, skippedCount)

    if totalSoldAnimals == 0 and skippedCount > 0 then
        Log:debug("Dealer.applyTargetBulk: all %d items rejected (firstError=%d)", skippedCount, firstErrorCode)
        local mapping = AnimalScreenDealerFarm.SELL_ERROR_CODE_MAPPING[firstErrorCode]
        if mapping ~= nil and self.errorCallback ~= nil then
            self.errorCallback(g_i18n:getText(mapping.text))
        end
        return
    end

    if skippedCount > 0 then
        Log:warning("Dealer.applyTargetBulk: %d items skipped (firstError=%d)", skippedCount, firstErrorCode)
    end

    self.actionTypeCallback(AnimalScreenBase.ACTION_TYPE_SOURCE, g_i18n:getText(AnimalScreenDealerFarm.L10N_SYMBOL.SELLING))
    g_messageCenter:subscribe(AnimalSellEvent, self.onAnimalSold, self)
    g_client:getServerConnection():sendEvent(AnimalSellEvent.new(husbandry, self.targetAnimals, totalPrice, totalTransportPrice))

end