local inProgress = false
local storedPID = nil


local progressFunc = {
    ox = {
        start =
            function(data)
                local options = {
                    duration = debugMode and 1000 or data.time,
                    label = data.label,
                    position = data.position or "bottom",
                    useWhileDead = data.dead or false,
                    canCancel = data.cancel or true,
                    anim = {
                        dict = data.dict,
                        clip = data.anim,
                        flag = (data.flag == 8 and 32 or data.flag) or nil,
                        scenario = data.task
                    },
                    disable = {
                        combat = data.combat or true,
                        move = data.disableMovement or false,
                        car = data.disableMovement or false,
                        mouse = data.mouse or false
                    },
                }
                if data.prop and data.prop.model then
                    options.prop = {
                        model = data.prop.model,
                        pos = data.prop.pos or vec3(0, 0, 0),
                        rot = data.prop.rot or vec3(0, 0, 0),
                        bone = data.prop.bone or 0
                    }
                end
                if data.propTwo and data.propTwo.model then
                    options.propTwo = {
                        model = data.propTwo.model,
                        pos = data.propTwo.pos or vec3(0, 0, 0),
                        rot = data.propTwo.rot or vec3(0, 0, 0),
                        bone = data.propTwo.bone or 0
                    }
                end
                if data.progressType == "circle" then
                    if exports[OXLibExport]:progressCircle(options) then
                        return true
                    else
                        return false
                    end
                end
                if not data.progressType or data.progressType == "bar" then
                    if exports[OXLibExport]:progressBar(options) then
                        return true
                    else
                        return false
                    end
                end
            end,
        stop =
            function()
                exports[OXLibExport]:cancelProgress()
            end,
    },

    qb = {
        start =
            function(data)
                local p = promise.new()
                Core.Functions.Progressbar("progbar",
                    data.label,
                    debugMode and 1000 or data.time,
                    data.dead or false,
                    data.cancel or true,
                    {
                        disableMovement = data.disableMovement or false,
                        disableCarMovement = data.disableMovement or false,
                        disableMouse = data.disableMouse or false,
                        disableCombat = data.disableCombat or true,
                    },
                    {
                        animDict = data.dict,
                        anim = data.anim,
                        flags = data.flag or 32,
                        task = data.task
                    },
                    {}, {},
                    function() p:resolve(true) end,
                    function() p:resolve(false) end,
                data.icon)
                return Citizen.Await(p)
            end,
        stop =
            function()
                TriggerEvent("progressbar:client:cancel")
            end,
    },

    esx = {
        start =
            function(data)
                local p = promise.new()
                ESX.Progressbar(data.label, debugMode and 1000 or data.time, {
                    FreezePlayer = true,
                    animation = {
                        type = data.anim,
                        dict = data.dict,
                        scenario = data.task,
                    },
                    onFinish = function()
                        p:resolve(true)
                    end,
                    onCancel = function()
                        p:resolve(false)
                        return false
                    end
                })
                return Citizen.Await(p)
            end,
        stop =
            function()
                ESX.CancelProgressbar()
            end,
    },

    lation = {
        start =
            function(data)
                if exports.lation_ui:progressBar({
                    label = data.label,
                    description = nil,
                    duration = debugMode and 1000 or data.time,
                    icon = data.icon,
                    useWhileDead = data.dead or false,
                    disable = {
                        combat = data.combat or true,
                        move = data.disableMovement or false,
                        car = data.disableMovement or false,
                    },
                    anim = {
                        dict = data.dict,
                        clip = data.anim,
                        flag = data.flag
                    },
                    prop = {
                        model = data.prop and data.prop.model,
                        pos = data.prop and (data.prop.pos or vec3(0, 0, 0)),
                        rot = data.prop and (data.prop.rot or vec3(0, 0, 0)),
                        bone = data.prop and (data.prop.bone or 0)
                    }
                }) then
                    return true
                else
                    return false
                end
            end,
        stop =
            function()
                exports.lation_ui:cancelProgress()
            end,
    },

    red = {
        start =
            function(data)
                if exports.jim_bridge:redProgressBar({
                    label = data.label,
                    time = debugMode and 1000 or data.time,
                    dict = data.dict,
                    anim = data.anim,
                    flag = data.flag or 32,
                    task = data.task,
                    disableMovement = data.disableMovement or false,
                    cancel = data.cancel or true,
                }) then
                    return true
                else
                    return false
                end
            end,
        stop =
            function()
                exports.jim_bridge:stopProgressBar()
            end,
    },

    gta = {
        start =
            function(data)
                if exports.jim_bridge:gtaProgressBar({
                    label = data.label,
                    time = debugMode and 1000 or data.time,
                    dict = data.dict,
                    anim = data.anim,
                    flag = data.flag or 32,
                    task = data.task,
                    disableMovement = data.disableMovement or false,
                    cancel = data.cancel or true,
                }) then
                    return true
                else
                    return false
                end
            end,
        stop =
            function()
                exports.jim_bridge:stopProgressBar()
            end,
    },

    ["17mov"] = {
        start =
            function(data)
                if exports["17mov_Hud"]:StartProgress({
                    duration = debugMode and 1000 or data.time,
                    label = data.label,
                    useWhileDead = data.dead or false,
                    canCancel = data.cancel or true,
                    controlDisables = {
                        disableMovement = data.disableMovement or false,
                        disableCarMovement = data.disableMovement or false,
                        disableMouse = data.mouse or false,
                        disableCombat = data.combat or true,
                    },
                    animation = {
                        animDict = data.dict,
                        anim = data.anim,
                        flags = (data.flag == 8 and 32 or data.flag) or nil,
                        task = data.task,
                    },
                    prop = data.prop and {
                        model = data.prop.model,
                        bone = data.prop.bone or 0,
                        coords = data.prop.pos or vec3(0, 0, 0),
                        rotation = data.prop.rot or vec3(0, 0, 0),

                    } or nil,
                    propTwo = data.propTwo and {
                        model = data.propTwo.model,
                        bone = data.propTwo.bone or 0,
                        coords = data.propTwo.pos or vec3(0, 0, 0),
                        rotation = data.propTwo.rot or vec3(0, 0, 0),
                    } or nil,

                }, nil, nil) then
                    return true
                else
                    return false
                end
            end,
        stop =
            function()
                exports["17mov_Hud"]:StopProgress()
            end,
    },
}

--- Displays a progress bar using the configured progress bar system.
---
--- This function handles displaying a progress bar to the player using the specified progress bar system (e.g., ox, qb, esx, gta).
--- It supports shared progress bars between players, animations, camera effects, and more.
---
---@param data table A table containing the progress bar configuration.
--- - **label** (`string`): The text label to display on the progress bar.
--- - **time** (`number`): The duration of the progress bar in milliseconds.
--- - **dict** (`string`, optional): The animation dictionary to use.
--- - **anim** (`string`, optional): The animation name to play.
--- - **task** (`string`, optional): The task scenario to perform.
--- - **flag** (`number`, optional): The animation flag.
--- - **dead** (`boolean`, optional): Whether to allow the progress bar when the player is dead. Default is `false`.
--- - **cancel** (`boolean`, optional): Whether the progress bar can be canceled by the player. Default is `true`.
--- - **icon** (`string`, optional): The icon to display (for qb progress bar).
--- - **cam** (`number`, optional): The camera handle to use.
--- - **shared** (`table`, optional): Data for shared progress bars.
---   - **pid** (`number`): The player ID to share the progress bar with.
---   - **label** (`string`): The label to display on the shared progress bar.
---
--- @return boolean `true` if the progress bar completed successfully, or `false` if it was canceled.
---
---@usage
--- ```lua
--- local success = progressBar({
---     label = "Processing...",
---     time = 5000,
---     dict = "amb@world_human_hang_out_street@female_hold_arm@base",
---     anim = "base",
---     flag = 49,
---     cancel = true,
--- })
--- ```

local progresssBarActive = false
function progressBar(data)
    if progresssBarActive then return else progresssBarActive = true end

    local ped = PlayerPedId()
    if data.shared then
        debugPrint("^6Bridge^7: ^6Sharing progressBar to player^7: ^6"..data.shared.pid.."^7")
        storedPID = data.shared.pid
        TriggerServerEvent(getScript()..":server:sharedProg:Start", data)
    end
    local result = nil
    if data.cam then startTempCam(data.cam) end

    if progressFunc[Config.System.ProgressBar].start(data) then
        result = true
    else
        result = false
    end

    while result == nil do
        Wait(10)
    end
    debugPrint("^5Debug^7: ^2ProgressBar result^7: ^3"..tostring(result).."^7")

    -- Cleanup
    FreezeEntityPosition(ped, false)
    lockInv(false)
    if data.cam then
        stopTempCam(data.cam)
    end
    if result == false and data.shared then
        debugPrint("^6Bridge^7: ^2Sending cancel to ^6"..storedPID.."^7")
        TriggerServerEvent(getScript().."server:sharedProg:cancel", storedPID)
    end
    storedPID = nil
    if result == false then
        currentToken = nil
        TriggerServerEvent(getScript()..":clearAuthToken")
    end
    if result == true and data.request then
        TriggerServerEvent(getScript()..":clearAuthToken")
        currentToken = triggerCallback(AuthEvent)
    end
    progresssBarActive = false
    return result
end

--- Stops the current progress bar.
---
--- This function cancels the progress bar based on the configured progress bar system, handling any necessary cleanup.
function stopProgressBar()
    progressFunc[Config.System.ProgressBar].stop()
    progresssBarActive = false
end

-- System to handle sending/sharing progress bars between players --
-- For example, healing someone --


--- Server event handler for starting a shared progress bar.
--- This event is triggered when a player wants to start a progress bar on another player.
--- It adjusts the data to prevent loops and sends the data to the target client.
RegisterNetEvent(getScript()..":server:sharedProg:Start", function(data)
    local pid = data.shared.pid     -- Get player ID from the client
    data.label = data.shared.label  -- Set progress bar label to the shared label
    data.cancel = false             -- Make it so it can't be canceled
    data.dead = true                -- Allow progress bar even if player is dead
    data.shared = nil               -- Remove shared info to prevent loops
    data.anim = nil                 -- Remove animation so players don't share it
    debugPrint("^6Bridge^7: ^6"..source.." ^2is sending shared progressBar to player^7, ^6"..pid.."^7")
    TriggerClientEvent(getScript()..":client:sharedProg:Start", pid, data)
end)

--- Client event handler for starting a shared progress bar.
--- This event is triggered when the server wants the client to start a shared progress bar.
RegisterNetEvent(getScript()..":client:sharedProg:Start", function(data)
    debugPrint("^6Bridge^7: ^2You have been sent a progressBar^7")
    progressBar(data)
end)

--- Server event handler for canceling a shared progress bar.
--- This event is triggered when a progress bar is canceled and the server needs to notify the other player.
RegisterNetEvent(getScript()..":server:sharedProg:Cancel", function(pid)
    debugPrint("^6Bridge^7: ^2Sending cancel progressBar to ^6"..pid.."^7")
    TriggerClientEvent(getScript()..":client:sharedProg:Cancel", pid)
end)

--- Client event handler for canceling a shared progress bar.
--- This event is triggered when the server wants the client to cancel a shared progress bar.
RegisterNetEvent(getScript()..":client:sharedProg:Cancel", function()
    debugPrint("^6Bridge^7: ^2Receiving cancel progressBar^7")
    stopProgressBar()
end)