require("config")
require("scanner_speed")

_G.NOT_ONLY_TITANIUM_MAIN = true

local UEHelpers = require("UEHelpers")

if not _G.NOT_ONLY_TITANIUM_CONFIG then return end

local Config = _G.NOT_ONLY_TITANIUM_CONFIG

local RESOURCE_PATHS = {
    Titanium       = "/Game/Blueprints/Items/Resources/BP_Titanium.BP_Titanium_C",
    Quartz         = "/Game/Blueprints/Items/Resources/BP_Quartz.BP_Quartz_C",
    Copper         = "/Game/Blueprints/Items/Resources/BP_Copper.BP_Copper_C",
    Silver         = "/Game/Blueprints/Items/Resources/BP_Silver.BP_Silver_C",
    Gold           = "/Game/Blueprints/Items/Resources/BP_Gold.BP_Gold_C",
    Lead           = "/Game/Blueprints/Items/Resources/BP_Lead.BP_Lead_C",
    Lithium        = "/Game/Blueprints/Items/Resources/BP_Lithium.BP_Lithium_C",
    Sulfur         = "/Game/Blueprints/Items/Resources/BP_Sulfur.BP_Sulfur_C",
    ConduitCrystal = "/Game/Blueprints/Items/Resources/BP_ConduitCrystal.BP_ConduitCrystal_C",
    Enamel         = "/Game/Blueprints/Items/Resources/BP_Enamel.BP_Enamel_C",
    Celestine      = "/Game/Blueprints/Items/Resources/BP_Celestine.BP_Celestine_C",
    Atacamite      = "/Game/Blueprints/Items/Resources/BP_Atacamite.BP_Atacamite_C",
    Troilite       = "/Game/Blueprints/Items/Resources/BP_Troilite.BP_Troilite_C",
}

local UWEScanData, UWEScanData_GetScanDataForActor
local SN2Statics, SN2Statics_GetLocalPlayerInventory
local KismetSystemLibrary, KSL_LoadAssetClass_Blocking, KSL_MakeSoftClassPath, KSL_Conv_SoftClassPathToSoftClassRef

pcall(function()
    UWEScanData = StaticFindObject("/Script/UWEScanner.UWEScanData")
    UWEScanData_GetScanDataForActor = StaticFindObject("/Script/UWEScanner.UWEScanData:GetScanDataForActor")
end)

pcall(function()
    SN2Statics = StaticFindObject("/Script/Subnautica2.SN2Statics")
    SN2Statics_GetLocalPlayerInventory = StaticFindObject("/Script/Subnautica2.SN2Statics:GetLocalPlayerInventory")
end)

pcall(function()
    KismetSystemLibrary = StaticFindObject("/Script/Engine.KismetSystemLibrary")
    KSL_LoadAssetClass_Blocking = StaticFindObject("/Script/Engine.KismetSystemLibrary:LoadClassAsset_Blocking")
    KSL_MakeSoftClassPath = StaticFindObject("/Script/Engine.KismetSystemLibrary:MakeSoftClassPath")
    KSL_Conv_SoftClassPathToSoftClassRef = StaticFindObject("/Script/Engine.KismetSystemLibrary:Conv_SoftClassPathToSoftClassRef")
end)

local lastScanEvent  = nil
local rewardPending  = false
local lastRewardTime = 0

local function getDepth()
    local ok, result = pcall(function()
        local player = UEHelpers.GetPlayerController()
        if not player then return 0 end
        local ok1, pawn = pcall(function() return player.Pawn end)
        if not ok1 or not pawn then return 0 end
        local ok2, location = pcall(function() return pawn:K2_GetActorLocation() end)
        if not ok2 or not location then return 0 end
        local z = location.Z
        return z < 0 and math.abs(z) / 100 or 0
    end)
    return ok and result or 0
end

local function buildRewardPool(includeTitanium)
    local ok, result = pcall(function()

        local depth = getDepth()

        local rawReduction = (depth / 100) * Config.DepthReductionPerTier
        local reduction = math.min(rawReduction, Config.MaxDepthReduction or 12)

        local pool = {}

        local function addShallow(path, baseChance)
            local chance = math.max(0, baseChance - reduction)
            local minChance = baseChance * 0.5
            chance = math.max(chance, minChance)

            pool[#pool + 1] = { path = path, chance = chance }
        end

        local function addDeep(path, baseChance)
            if baseChance > 0 then
                pool[#pool + 1] = { path = path, chance = baseChance }
            end
        end

        if includeTitanium and Config.Titanium_Chance > 0 then
            addShallow(RESOURCE_PATHS.Titanium, Config.Titanium_Chance)
        end
        if Config.Quartz_Chance > 0 then addShallow(RESOURCE_PATHS.Quartz, Config.Quartz_Chance) end
        if Config.Copper_Chance > 0 then addShallow(RESOURCE_PATHS.Copper, Config.Copper_Chance) end
        if Config.Silver_Chance > 0 then addShallow(RESOURCE_PATHS.Silver, Config.Silver_Chance) end

        if depth >= Config.Depth_Tier1 then
            if Config.Gold_Chance > 0 then addDeep(RESOURCE_PATHS.Gold, Config.Gold_Chance) end
            if Config.Lead_Chance > 0 then addDeep(RESOURCE_PATHS.Lead, Config.Lead_Chance) end
        end
        if depth >= Config.Depth_Tier2 then
            if Config.Lithium_Chance > 0 then addDeep(RESOURCE_PATHS.Lithium, Config.Lithium_Chance) end
            if Config.Sulfur_Chance  > 0 then addDeep(RESOURCE_PATHS.Sulfur,  Config.Sulfur_Chance)  end
        end
        if depth >= Config.Depth_Tier3 then
            if Config.ConduitCrystal_Chance > 0 then addDeep(RESOURCE_PATHS.ConduitCrystal, Config.ConduitCrystal_Chance) end
            if Config.Enamel_Chance         > 0 then addDeep(RESOURCE_PATHS.Enamel,         Config.Enamel_Chance)         end
            if Config.Celestine_Chance      > 0 then addDeep(RESOURCE_PATHS.Celestine,      Config.Celestine_Chance)      end
        end
        if depth >= Config.Depth_Tier4 then
            if Config.Atacamite_Chance > 0 then addDeep(RESOURCE_PATHS.Atacamite, Config.Atacamite_Chance) end
            if Config.Troilite_Chance  > 0 then addDeep(RESOURCE_PATHS.Troilite,  Config.Troilite_Chance)  end
        end

        return pool
    end)
    return ok and result or {}
end

local function roll(pool)
    local ok, result = pcall(function()
        local total = 0
        for _, r in ipairs(pool) do
            if type(r.chance) == "number" and r.chance > 0 then
                total = total + r.chance
            end
        end
        if total <= 0 then return nil end

        local rollv = math.random() * total
        local acc   = 0
        for _, r in ipairs(pool) do
            if type(r.chance) == "number" and r.chance > 0 then
                acc = acc + r.chance
                if rollv <= acc then return r.path end
            end
        end
        return nil
    end)
    return ok and result or nil
end

local function loadClassByPath(path)
    local ok, result = pcall(function()
        local world = UEHelpers.GetWorld()
        if not world then return nil end
        if not KismetSystemLibrary or not KSL_MakeSoftClassPath or not KSL_Conv_SoftClassPathToSoftClassRef or not KSL_LoadAssetClass_Blocking then return nil end
        local soft = KSL_MakeSoftClassPath(KismetSystemLibrary, path)
        local ptr  = KSL_Conv_SoftClassPathToSoftClassRef(KismetSystemLibrary, soft)
        local ok2, res = pcall(function()
            return KSL_LoadAssetClass_Blocking(world, ptr)
        end)
        return ok2 and res or nil
    end)
    return ok and result or nil
end

local function addItem(path)
    ExecuteInGameThread(function()
        local ok, err = pcall(function()
            local world = UEHelpers.GetWorld()
            if not world then return end

            local item = loadClassByPath(path)
            if not item then return end

            if not SN2Statics or not SN2Statics_GetLocalPlayerInventory then return end

            local ok_inv, inv = pcall(function()
                return SN2Statics_GetLocalPlayerInventory(SN2Statics, SN2Statics)
            end)
            if not ok_inv or not inv then return end

            local full = false
            pcall(function() full = inv:IsFull() end)
            if full then return end

            local spawnLoc = { X = 0, Y = 0, Z = 0 }
            local player = UEHelpers.GetPlayerController()
            if player then
                local ok_loc, loc = pcall(function()
                    return player.Pawn:K2_GetActorLocation()
                end)
                if ok_loc and loc then
                    spawnLoc = { X = loc.X, Y = loc.Y, Z = loc.Z }
                end
            end

            local ok_spawn, actor = pcall(function()
                return world:SpawnActor(item, spawnLoc, {})
            end)
            if not ok_spawn or not actor then return end

            local ok_pick = pcall(function() inv:Pickup(actor, true) end)
            if not ok_pick then
                pcall(function() actor:K2_DestroyActor() end)
            end
        end)
        if not ok then
            print("[NotOnlyTitanium] ERROR in addItem: " .. tostring(err))
        end
    end)
end

RegisterHook(
    "/Script/UWEScanner.UWEScannedActorsComponent:GetActorInstanceScannedProgressForPlayer",
    function(Context, ScannedActor)
        local ok, err = pcall(function()
            local component = Context:get()
            local actor     = ScannedActor:get()
            if not component or not actor then return end

            if not UWEScanData or not UWEScanData_GetScanDataForActor then return end

            local ok_sd, scan_data = pcall(function()
                return UWEScanData_GetScanDataForActor(UWEScanData, actor)
            end)
            if not ok_sd or not scan_data then return end

            local ok_c, completed = pcall(function()
                return component:IsScanDataProgressComplete(scan_data)
            end)
            if not ok_c then return end

            lastScanEvent = {
                completed = completed,
                time      = os.clock()
            }
        end)
        if not ok then
            print("[NotOnlyTitanium] ERROR in GetActorInstanceScannedProgressForPlayer: " .. tostring(err))
        end
    end
)

RegisterHook(
    "/Script/UWEScanner.UWEScannedActorsComponent:ClientNotifyScannedInstanceCompleted",
    function()
        local ok, err = pcall(function()
            if rewardPending then return end
            rewardPending = true

            local now = os.clock()

            if now - lastRewardTime < 0.25 then
                rewardPending = false
                return
            end
            lastRewardTime = now

            local is_first_scan
            if lastScanEvent and (now - lastScanEvent.time) < 1.0 then
                is_first_scan = not lastScanEvent.completed
            else
                is_first_scan = false
            end
            lastScanEvent = nil

            local reward = nil

            if is_first_scan then
                if math.random(1, 100) <= math.floor(Config.NewScanRewardChance) then
                    reward = roll(buildRewardPool(true))
                end
            else
                reward = roll(buildRewardPool(false))
            end

            if reward then addItem(reward) end

            rewardPending = false
        end)
        if not ok then
            print("[NotOnlyTitanium] ERROR in ClientNotifyScannedInstanceCompleted: " .. tostring(err))
            rewardPending = false
        end
    end
)