-- Initialize config(s)
local shared = require 'config.shared'
local sv_config = require 'config.server'

-- Initialize global state for cooldowns
GlobalState.cooldown = false
GlobalState.started = false

-- Initialize proper coordinates
local gabz = GetResourceState('cfx-gabz-247') == 'started'
local fm = GetResourceState('cfx-fm-supermarkets') == 'started'
local stores = gabz and require 'data.gabz' or fm and require 'data.fmshop' or require 'data.default'

-- Initialize table to store known locations & robbery states
local locations, states = {}, {}

-- Builds local table containing all locations categorized
local function InitializeStores()
    for type, coords in pairs(stores) do
        for _, location in pairs(coords) do
            if not locations[type:lower()] then locations[type:lower()] = {} end
            locations[type:lower()][#locations[type:lower()] + 1] = location
        end
    end
end

-- Returns boolean if player is nearby defined coords based on location type
--- @param source number
--- @param location string registers, computers, safes
--- @return boolean
local function IsPlayerNearby(source, location)
    if not source or not location then return false end
    local playerPos = GetEntityCoords(GetPlayerPed(source))
    for _, coords in pairs(locations[location]) do
        if #(playerPos - coords) <= 2.5 then
            return true
        end
    end
    return false
end

-- Returns boolean if player can start robbery
--- @param identifier string
--- @return boolean
local function CanPlayerRob(identifier)
    if not identifier then return false end
    local currentTime = os.time()
    if shared.setup.global.enable then
        if GlobalState.cooldown or GlobalState.started then
            return false
        end
    end
    if not states[identifier] then return true end
    local lastCompleted = states[identifier].completed
    if lastCompleted then
        if (currentTime - lastCompleted) < shared.setup.cooldown then
            return false
        end
    end
    return true
end

-- Starts & ends global cooldown if enabled
local function StartCooldown()
    GlobalState.cooldown = true
    local wait = math.floor(shared.setup.global.duration * 1000)
    SetTimeout(wait, function()
        GlobalState.cooldown = false
    end)
end

-- Callback to initialize store robbery
--- @param source number
--- @return boolean
lib.callback.register('lation_247robbery:StartRobbery', function(source)
    if not source then
        EventLog('[main.lua]: lation_247robbery:StartRobbery: unable to retrieve source', 'error')
        return false
    end
    local source = source
    local identifier = GetIdentifier(source)
    if not identifier then
        EventLog('[main.lua]: lation_247robbery:StartRobbery: unable to retrieve player identifier', 'error')
        return false
    end
    local isNearRegister = IsPlayerNearby(source, 'registers')
    if not isNearRegister then
        EventLog('[main.lua]: lation_247robbery:StartRobbery: player not nearby any registers', 'error')
        return false
    end
    local hasRequiredItem = GetItemCount(source, shared.registers.item) >= 1
    if not hasRequiredItem then
        TriggerClientEvent('lation_247robbery:Notify', source, locale('notify.missing-item'), 'error')
        EventLog('[main.lua]: lation_247robbery:StartRobbery: player missing required item', 'error')
        return false
    end
    if shared.police.count > 0 then
        local police = GetPoliceCount()
        if not police or police < shared.police.count then
            TriggerClientEvent('lation_247robbery:Notify', source, locale('notify.no-police'), 'error')
            EventLog('[main.lua]: lation_247robbery:StartRobbery: not enough police to start robbery', 'error')
            return false
        end
    end
    if not CanPlayerRob(identifier) then
        TriggerClientEvent('lation_247robbery:Notify', source, locale('notify.cooldown'), 'error')
        EventLog('[main.lua]: lation_247robbery:StartRobbery: cooldown is still active (for player or global)', 'error')
        return false
    end
    GlobalState.started = true
    if not states[identifier] then states[identifier] = {} end
    states[identifier].state = 'in_progress'
    states[identifier].started = os.time()
    -- Fallback to reset states if not completed in a reasonable amount of time
    SetTimeout(600000, function() -- 10 minute timeout
        if states[identifier] and states[identifier].state == 'in_progress' then
            states[identifier] = nil
        end
        if GlobalState.started then
            GlobalState.started = false
        end
    end)
    return true
end)

-- Event to handle lockpick breaking chance
RegisterNetEvent('lation_247robbery:DoesLockpickBreak', function()
    if not source then
        EventLog('[main.lua]: lation_247robbery:DoesLockpickBreak: unable to retrieve source', 'error')
        return
    end
    local source = source
    local identifier = GetIdentifier(source)
    if not identifier then
        EventLog('[main.lua]: lation_247robbery:DoesLockpickBreak: unable to retrieve player identifier', 'error')
        return
    end
    if not GlobalState.started then
        EventLog('[main.lua]: lation_247robbery:RegisterIncomplete: global state wasnt initiated', 'error')
        return
    end
    if not states[identifier] or states[identifier].state ~= 'in_progress' then
        EventLog('[main.lua]: lation_247robbery:DoesLockpickBreak: robbery wasnt initiated for player', 'error')
        return
    end
    local isNearRegister = IsPlayerNearby(source, 'registers')
    if not isNearRegister then
        EventLog('[main.lua]: lation_247robbery:DoesLockpickBreak: player not nearby any registers', 'error')
        return
    end
    if math.random(100) <= shared.registers.breakChance then
        RemoveItem(source, shared.registers.item, 1)
        TriggerClientEvent('lation_247robbery:Notify', source, locale('notify.item-broke'), 'error')
    end
    if GlobalState.started then GlobalState.started = false end
end)

-- Event to handle robbery completion and rewards
RegisterNetEvent('lation_247robbery:CompleteRegisterRobbery', function()
    if not source then
        EventLog('[main.lua]: lation_247robbery:CompleteRegisterRobbery: unable to retrieve source', 'error')
        return
    end
    local source = source
    local identifier = GetIdentifier(source)
    if not identifier then
        EventLog('[main.lua]: lation_247robbery:CompleteRegisterRobbery: unable to retrieve player identifier', 'error')
        return
    end
    local name = GetName(source)
    if not name then
        EventLog('[main.lua]: lation_247robbery:CompleteRegisterRobbery: unable to retrieve player name', 'error')
        return
    end
    if not states[identifier] or states[identifier].state ~= 'in_progress' then
        EventLog('[main.lua]: lation_247robbery:CompleteRegisterRobbery: robbery wasnt initiated for player', 'error')
        return
    end
    if not GlobalState.started then
        EventLog('[main.lua]: lation_247robbery:CompleteRegisterRobbery: global state wasnt initiated', 'error')
        return
    end
    local isNearRegister = IsPlayerNearby(source, 'registers')
    if not isNearRegister then
        EventLog('[main.lua]: lation_247robbery:CompleteRegisterRobbery: player not nearby any registers', 'error')
        return
    end
    local police = shared.police.risk and GetPoliceCount() or 0
    local items = {}
    for _, add in pairs(shared.registers.reward) do
        if math.random(100) <= add.chance then
            local quantity = math.random(add.min, add.max)
            if police > 0 then
                local increase = 1 + (police * shared.police.percent / 100)
                quantity = math.floor(quantity * increase)
            end
            if add.metadata then
                AddItem(source, add.item, quantity, add.metadata)
            else
                if add.item == 'cash' or add.item == 'money' or add.item == 'bank' then
                    AddMoney(source, add.item, quantity)
                else
                    AddItem(source, add.item, quantity)
                end
            end
            items[#items + 1] = { item = add.item, quantity = quantity }
        end
    end
    local rewards = ''
    for _, reward in ipairs(items) do
        rewards = rewards .. 'x' .. reward.quantity .. ' ' ..reward.item .. ', '
    end
    rewards = rewards:sub(1, -3)
    states[identifier].state = 'completed'
    states[identifier].completed = os.time()
    if sv_config.logs.events.register_robbed and #items > 0 then
        ---@diagnostic disable-next-line: undefined-field
        PlayerLog(source, locale('logs.register-robbed-title'), locale('logs.register-robbed-message', name, identifier, rewards))
    end
end)

-- Event to handle robbery completion and rewards
RegisterNetEvent('lation_247robbery:CompleteSafeRobbery', function()
    if not source then
        EventLog('[main.lua]: lation_247robbery:CompleteSafeRobbery: unable to retrieve source', 'error')
        return
    end
    local source = source
    local identifier = GetIdentifier(source)
    if not identifier then
        EventLog('[main.lua]: lation_247robbery:CompleteSafeRobbery: unable to retrieve player identifier', 'error')
        return
    end
    local name = GetName(source)
    if not name then
        EventLog('[main.lua]: lation_247robbery:CompleteSafeRobbery: unable to retrieve player name', 'error')
        return
    end
    if not GlobalState.started then
        EventLog('[main.lua]: lation_247robbery:CompleteSafeRobbery: global state wasnt initiated', 'error')
        return
    end
    if not states[identifier] or states[identifier].state ~= 'completed' then
        EventLog('[main.lua]: lation_247robbery:CompleteSafeRobbery: player has not completed the register robbery', 'error')
        return
    end
    local isNearSafe = IsPlayerNearby(source, 'safes')
    if not isNearSafe then
        EventLog('[main.lua]: lation_247robbery:CompleteSafeRobbery: player not nearby any safes', 'error')
        return
    end
    local police = shared.police.risk and GetPoliceCount() or 0
    local items = {}
    for _, add in pairs(shared.safes.reward) do
        if math.random(100) <= add.chance then
            local quantity = math.random(add.min, add.max)
            if police > 0 then
                local increase = 1 + (police * shared.police.percent / 100)
                quantity = math.floor(quantity * increase)
            end
            if add.metadata then
                AddItem(source, add.item, quantity, add.metadata)
            else
                if add.item == 'cash' or add.item == 'money' or add.item == 'bank' then
                    AddMoney(source, add.item, quantity)
                else
                    AddItem(source, add.item, quantity)
                end
            end
            items[#items + 1] = { item = add.item, quantity = quantity }
        end
    end
    local rewards = ''
    for _, reward in ipairs(items) do
        rewards = rewards .. 'x' .. reward.quantity .. ' ' ..reward.item .. ', '
    end
    rewards = rewards:sub(1, -3)
    GlobalState.started = false
    states[identifier].state = nil
    states[identifier].completed = os.time()
    StartCooldown()
    if sv_config.logs.events.safe_robbed and #items > 0 then
        ---@diagnostic disable-next-line: undefined-field
        PlayerLog(source, locale('logs.safe-robbed-title'), locale('logs.safe-robbed-message', name, identifier, rewards))
    end
end)

-- Event to handle failed robbery
RegisterNetEvent('lation_247robbery:FailedRobbery', function()
    if not source then
        EventLog('[main.lua]: lation_247robbery:FailedRobbery: unable to retrieve source', 'error')
        return
    end
    local source = source
    local identifier = GetIdentifier(source)
    if not identifier then
        EventLog('[main.lua]: lation_247robbery:FailedRobbery: unable to retrieve player identifier', 'error')
        return
    end
    local name = GetName(source)
    if not name then
        EventLog('[main.lua]: lation_247robbery:FailedRobbery: unable to retrieve player name', 'error')
        return
    end
    if not GlobalState.started then
        EventLog('[main.lua]: lation_247robbery:FailedRobbery: global state wasnt initiated', 'error')
        return
    end
    if not states[identifier] or states[identifier].state ~= 'completed' then
        EventLog('[main.lua]: lation_247robbery:FailedRobbery: robbery wasnt initiated for player', 'error')
        return
    end
    local isNearRegister = IsPlayerNearby(source, 'computers')
    if not isNearRegister then
        EventLog('[main.lua]: lation_247robbery:CompleteRegisterRobbery: player not nearby any registers', 'error')
        return
    end
    GlobalState.started = false
    states[identifier].state = nil
    states[identifier].completed = os.time()
    StartCooldown()
end)

InitializeStores()