local QBCore = exports['qb-core']:GetCoreObject()
local PlayerData = {}
local ClosestTraphouse = nil
local InsideTraphouse = false
local CurrentTraphouse = nil
local TraphouseObj = {}
local POIOffsets = nil
local IsKeyHolder = false
local IsHouseOwner = false
local CanRob = true
local IsRobbingNPC = false
local RobbingTime = 3

-- zone check
local isInsideEntranceTarget = false
local isInsideExitTarget = false
local isInsideInteractionTarget = false

-- Functions

local function RegisterTraphouseEntranceTarget(traphouseID, traphouseData)
    local coords = traphouseData.coords['enter']
    local boxName = 'traphouseEntrance' .. traphouseID
    local boxData = traphouseData.polyzoneBoxData['enter']
    exports['qb-target']:AddBoxZone(boxName, coords, boxData.length, boxData.width, {
        name = boxName,
        heading = boxData.heading,
        debugPoly = boxData.debug,
        minZ = boxData.minZ,
        maxZ = boxData.maxZ,
    }, {
        options = {
            {
                type = 'client',
                event = 'qb-traphouse:client:EnterTraphouse',
                label = Lang:t('targetInfo.enter'),
            },
        },
        distance = boxData.distance
    })

    Config.TrapHouses[traphouseID].polyzoneBoxData['enter'].created = true
end

local function RegisterTraphouseEntranceZone(traphouseID, traphouseData)
    local coords = traphouseData.coords['enter']
    local boxName = 'traphouseEntrance' .. traphouseID
    local boxData = traphouseData.polyzoneBoxData['enter']

    local zone = BoxZone:Create(coords, boxData.length, boxData.width, {
        name = boxName,
        heading = boxData.heading,
        debugPoly = boxData.debug,
        minZ = boxData.minZ,
        maxZ = boxData.maxZ,
    })

    zone:onPlayerInOut(function (isPointInside)
        if isPointInside then
            exports['qb-core']:DrawText('[E] ' .. Lang:t('targetInfo.enter'), 'top')
        else
            exports['qb-core']:HideText()
        end

        isInsideEntranceTarget = isPointInside
    end)

    boxData.created = true
    boxData.zone = zone
end

local function SetTraphouseEntranceTargets()
    if Config.TrapHouses and next(Config.TrapHouses) then
        for id, traphouse in pairs(Config.TrapHouses) do
            if traphouse and traphouse.coords and traphouse.coords['enter'] then
                if Config.UseTarget then
                    RegisterTraphouseEntranceTarget(id, traphouse)
                else
                    RegisterTraphouseEntranceZone(id, traphouse)
                end
            end
        end
    end
end

local function RegisterTraphouseInteractionZone(traphouseID, traphouseData)
    local coords = traphouseData.coords['interaction']
    local boxName = 'traphouseInteraction' .. traphouseID
    local boxData = traphouseData.polyzoneBoxData['interaction']

    local zone = BoxZone:Create(coords, boxData.length, boxData.width, {
        name = boxName,
        heading = boxData.heading,
        debugPoly = boxData.debug,
        minZ = coords.z - 1.0,
        maxZ = coords.z + 1.0,
    })

    zone:onPlayerInOut(function (isPointInside)
        if isPointInside then
            exports['qb-core']:DrawText('[E] ' .. Lang:t('targetInfo.options'), 'top')
        else
            exports['qb-core']:HideText()
            TriggerEvent('qb-traphouse:client:target:CloseMenu')
        end

        isInsideInteractionTarget = isPointInside
    end)

    boxData.created = true
    boxData.zone = zone
end

local function RegisterTraphouseInteractionTarget(traphouseID, traphouseData)
    local coords = traphouseData.coords['interaction']
    local boxName = 'traphouseInteraction' .. traphouseID
    local boxData = traphouseData.polyzoneBoxData['interaction']

    local options = {
        {
            type = "client",
            event = "qb-traphouse:client:target:TakeOver",
            label = Lang:t("targetInfo.take_over"),
        },
    }
    if IsKeyHolder then
        options = {
            {
                type = "client",
                event = "qb-traphouse:client:target:ViewInventory",
                label = Lang:t("targetInfo.inventory"),
                traphouseData = traphouseData
            },
            {
                type = "client",
                event = "qb-traphouse:client:target:TakeMoney",
                label = Lang:t('targetInfo.take_cash', {value = traphouseData.money}),
            },
        }

        if IsHouseOwner then
            options[#options+1] = {
                type = "client",
                event = "qb-traphouse:client:target:SeePinCode",
                label = Lang:t("targetInfo.pin_code_see"),
                traphouseData = traphouseData
            }
        end
    end

    exports['qb-target']:AddBoxZone(boxName, coords, boxData.length, boxData.width, {
        name = boxName,
        heading = boxData.heading,
        debugPoly = boxData.debug,
        minZ = coords.z - 1.0,
        maxZ = coords.z + 1.0,
    }, {
        options = options,
        distance = boxData.distance
    })

    boxData.created = true
end

local function RegisterTraphouseExitZone(coords, traphouseID, traphouseData)
    local boxName = 'traphouseExit' .. traphouseID
    local boxData = traphouseData.polyzoneBoxData['exit']

    local zone = BoxZone:Create(coords, boxData.length, boxData.width, {
        name = boxName,
        heading = boxData.heading,
        debugPoly = boxData.debug,
        minZ = coords.z - 1.0,
        maxZ = coords.z + 1.0,
    })

    zone:onPlayerInOut(function (isPointInside)
        if isPointInside then
            exports['qb-core']:DrawText('[E] ' .. Lang:t("targetInfo.leave"), 'top')
        else
            exports['qb-core']:HideText()
        end

        isInsideExitTarget = isPointInside
    end)

    boxData.created = true
    boxData.zone = zone
end

local function RegisterTraphouseExitTarget(coords, traphouseID, traphouseData)
    local boxName = 'traphouseExit' .. traphouseID
    local boxData = traphouseData.polyzoneBoxData['exit']
    exports['qb-target']:AddBoxZone(boxName, coords, boxData.length, boxData.width, {
        name = boxName,
        heading = boxData.heading,
        debugPoly = boxData.debug,
        minZ = coords.z - 1.0,
        maxZ = coords.z + 1.0,
    }, {
        options = {
            {
                type = "client",
                event = "qb-traphouse:client:target:ExitTraphouse",
                label = Lang:t("targetInfo.leave"),
                traphouseID = traphouseID,
            },
        },
        distance = boxData.distance
    })

    boxData.created = true
end

local function OpenHeaderMenu(data)
    local headerMenu = {}

    headerMenu[#headerMenu+1] = {
        header = Lang:t("targetInfo.options"),
        isMenuHeader = true
    }

    if IsKeyHolder then
        headerMenu[#headerMenu+1] = {
            header = Lang:t("targetInfo.inventory"),
            params = {
                event = "qb-traphouse:client:target:ViewInventory",
                args = {
                    traphouseData = data
                }
            }
        }
        headerMenu[#headerMenu+1] = {
            header = Lang:t('targetInfo.take_cash', {value = data.money}),
            params = {
                event = "qb-traphouse:client:target:TakeMoney"
            }
        }

        if IsHouseOwner then
            headerMenu[#headerMenu+1] = {
                header = Lang:t("targetInfo.pin_code_see"),
                params = {
                    event = "qb-traphouse:client:target:SeePinCode",
                    args = {
                        traphouseData = data
                    }
                }
            }
        end
    else
        headerMenu[#headerMenu+1] = {
            header = Lang:t("targetInfo.take_over"),
            params = {
                event = "qb-traphouse:client:target:TakeOver",
            }
        }
    end

    headerMenu[#headerMenu+1] = {
        header = Lang:t("targetInfo.close_menu"),
        params = {
            event = "qb-traphouse:client:target:CloseMenu",
        }
    }

    exports['qb-menu']:openMenu(headerMenu)
end

local function HasKey(CitizenId)
    local haskey = false
    if ClosestTraphouse ~= nil then
        if Config.TrapHouses[ClosestTraphouse].keyholders ~= nil and next(Config.TrapHouses[ClosestTraphouse].keyholders) ~= nil then
            for _, data in pairs(Config.TrapHouses[ClosestTraphouse].keyholders) do
                if data.citizenid == CitizenId then
                    haskey = true
                end
            end
        end
    end
    return haskey
end

local function IsOwner(CitizenId)
    local retval = false
    if ClosestTraphouse ~= nil then
        if Config.TrapHouses[ClosestTraphouse].keyholders ~= nil and next(Config.TrapHouses[ClosestTraphouse].keyholders) ~= nil then
            for _, data in pairs(Config.TrapHouses[ClosestTraphouse].keyholders) do
                if data.citizenid == CitizenId then
                    if data.owner then
                        retval = true
                    else
                        retval = false
                    end
                end
            end
        end
    end
    return retval
end

local function SetClosestTraphouse()
    local pos = GetEntityCoords(PlayerPedId(), true)
    local current = nil
    local dist = nil
    for id, _ in pairs(Config.TrapHouses) do
        if current ~= nil then
            if #(pos - Config.TrapHouses[id].coords.enter) < dist then
                current = id
                dist = #(pos - Config.TrapHouses[id].coords.enter)
            end
        else
            dist = #(pos - Config.TrapHouses[id].coords.enter)
            current = id
        end
    end
    ClosestTraphouse = current
    IsKeyHolder = HasKey(PlayerData.citizenid)
    IsHouseOwner = IsOwner(PlayerData.citizenid)
end

local function EnterTraphouse(data)
    local coords = { x = data.coords["enter"].x, y = data.coords["enter"].y, z= data.coords["enter"].z - Config.MinZOffset}
    TriggerServerEvent("InteractSound_SV:PlayOnSource", "houses_door_open", 0.25)
    data = exports['qb-interior']:CreateTrevorsShell(coords)
    TraphouseObj = data[1]
    POIOffsets = data[2]
    CurrentTraphouse = ClosestTraphouse
    InsideTraphouse = true
    TriggerEvent('qb-weathersync:client:DisableSync')
    FreezeEntityPosition(TraphouseObj, true)
end

local function LeaveTraphouse(k, data)
    local ped = PlayerPedId()
    TriggerServerEvent("InteractSound_SV:PlayOnSource", "houses_door_open", 0.25)
    DoScreenFadeOut(250)
    Wait(250)
    exports['qb-interior']:DespawnInterior(TraphouseObj, function()
        TriggerEvent('qb-weathersync:client:EnableSync')
        DoScreenFadeIn(250)
        SetEntityCoords(ped, data.coords["enter"].x, data.coords["enter"].y, data.coords["enter"].z + 0.5)
        SetEntityHeading(ped, 107.71)
        TraphouseObj = nil
        POIOffsets = nil
        CurrentTraphouse = nil
        InsideTraphouse = false
    end)

    if Config.UseTarget then
        exports['qb-target']:RemoveZone('traphouseInteraction' .. k)
        data.polyzoneBoxData['interaction'].created = false

        exports['qb-target']:RemoveZone('traphouseExit' .. k)
        data.polyzoneBoxData['exit'].created = false
    else
        if Config.TrapHouses[k] and Config.TrapHouses[k].polyzoneBoxData['interaction'] and Config.TrapHouses[k].polyzoneBoxData['interaction'].zone then
            Config.TrapHouses[k].polyzoneBoxData['interaction'].zone:destroy()
            Config.TrapHouses[k].polyzoneBoxData['interaction'].created = false
            Config.TrapHouses[k].polyzoneBoxData['interaction'].zone = nil
        end

        if Config.TrapHouses[k] and Config.TrapHouses[k].polyzoneBoxData['exit'] and Config.TrapHouses[k].polyzoneBoxData['exit'].zone then
            Config.TrapHouses[k].polyzoneBoxData['exit'].zone:destroy()
            Config.TrapHouses[k].polyzoneBoxData['exit'].created = false
            Config.TrapHouses[k].polyzoneBoxData['exit'].zone = nil
        end

        isInsideExitTarget = false
        isInsideInteractionTarget = false
    end
end

local function RobTimeout(timeout)
    SetTimeout(timeout, function()
        CanRob = true
    end)
end

-- Events

RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
    PlayerData = QBCore.Functions.GetPlayerData()
end)

RegisterNetEvent('qb-traphouse:client:EnterTraphouse', function()
    if ClosestTraphouse ~= nil then
        local data = Config.TrapHouses[ClosestTraphouse]
        if not IsKeyHolder then
            SendNUIMessage({
                action = "open"
            })
            SetNuiFocus(true, true)
        else
            EnterTraphouse(data)
        end
    end
end)

RegisterNetEvent('qb-traphouse:client:TakeoverHouse', function(TraphouseId)
    QBCore.Functions.Progressbar("takeover_traphouse", Lang:t("info.taking_over"), math.random(1000, 3000), false, true, {
        disableMovement = true,
        disableCarMovement = true,
        disableMouse = false,
        disableCombat = true,
    }, {}, {}, {}, function() -- Done
        TriggerServerEvent('qb-traphouse:server:AddHouseKeyHolder', PlayerData.citizenid, TraphouseId, true)
    end, function()
        QBCore.Functions.Notify(Lang:t("error.cancelled"), "error")
    end)
end)

RegisterNetEvent('qb-traphouse:client:target:ViewInventory', function (data)
    local TraphouseInventory = {}
    TraphouseInventory.label = "traphouse_"..CurrentTraphouse
    TraphouseInventory.items = data.traphouseData.inventory
    TraphouseInventory.slots = 2
    TriggerServerEvent("inventory:server:OpenInventory", "traphouse", TraphouseInventory.label, TraphouseInventory)
end)

RegisterNetEvent('qb-traphouse:client:target:TakeOver', function ()
    TriggerServerEvent('qb-traphouse:server:TakeoverHouse', CurrentTraphouse)
end)

RegisterNetEvent('qb-traphouse:client:target:TakeMoney', function ()
    TriggerServerEvent("qb-traphouse:server:TakeMoney", CurrentTraphouse)
end)

RegisterNetEvent('qb-traphouse:client:target:SeePinCode', function (data)
    QBCore.Functions.Notify(Lang:t('info.pin_code', { value = data.traphouseData.pincode }))
end)

RegisterNetEvent('qb-traphouse:client:target:ExitTraphouse', function (data)
    LeaveTraphouse(data.traphouseID, Config.TrapHouses[data.traphouseID])
end)

RegisterNetEvent('qb-traphouse:client:SyncData', function(k, data)
    Config.TrapHouses[k] = data
    IsKeyHolder = HasKey(PlayerData.citizenid)
    IsHouseOwner = IsOwner(PlayerData.citizenid)

    if Config.UseTarget then
        exports['qb-target']:RemoveZone('traphouseInteraction' .. k)
        Config.TrapHouses[k].polyzoneBoxData['interaction'].created = false
    else
        if Config.TrapHouses[k] and Config.TrapHouses[k].polyzoneBoxData['interaction'] and Config.TrapHouses[k].polyzoneBoxData['interaction'].zone then
            Config.TrapHouses[k].polyzoneBoxData['interaction'].zone:destroy()
            Config.TrapHouses[k].polyzoneBoxData['interaction'].created = false
            Config.TrapHouses[k].polyzoneBoxData['interaction'].zone = nil
        end

        isInsideInteractionTarget = false
    end
end)

RegisterNetEvent('qb-traphouse:client:target:CloseMenu', function ()
    TriggerEvent('qb-menu:client:closeMenu')
end)


-- NUI

RegisterNUICallback('PinpadClose', function(_, cb)
    SetNuiFocus(false, false)
    cb('ok')
end)

RegisterNUICallback('ErrorMessage', function(data, cb)
    QBCore.Functions.Notify(data.message, 'error')
    cb('ok')
end)

RegisterNUICallback('EnterPincode', function(d, cb)
    local data = Config.TrapHouses[ClosestTraphouse]
    if tonumber(d.pin) == data.pincode then
        EnterTraphouse(data)
    else
        QBCore.Functions.Notify(Lang:t("error.incorrect_code"), 'error')
    end
    cb('ok')
end)

-- Threads

CreateThread(function()
    while true do
        local aiming, targetPed = GetEntityPlayerIsFreeAimingAt(PlayerId(-1))
        if targetPed ~= 0 and not IsPedAPlayer(targetPed) then
            local ped = PlayerPedId()
            local pos = GetEntityCoords(ped)
            if ClosestTraphouse ~= nil then
                local data = Config.TrapHouses[ClosestTraphouse]
                local dist = #(pos - data.coords["enter"])
                if dist < 200 then
                    if aiming then
                        local pcoords = GetEntityCoords(targetPed)
                        local peddist = #(pos - pcoords)
                        local InDistance = false
                        if peddist < 4 then
                            InDistance = true
                            if not IsRobbingNPC and CanRob then
                                if IsPedInAnyVehicle(targetPed) then
                                    TaskLeaveVehicle(targetPed, GetVehiclePedIsIn(targetPed), 1)
                                end
                                Wait(500)
                                InDistance = true

                                local dict = 'random@mugging3'
                                RequestAnimDict(dict)
                                while not HasAnimDictLoaded(dict) do
                                    Wait(10)
                                end

                                SetEveryoneIgnorePlayer(PlayerId(), true)
                                TaskStandStill(targetPed, RobbingTime * 1000)
                                FreezeEntityPosition(targetPed, true)
                                SetBlockingOfNonTemporaryEvents(targetPed, true)
                                TaskPlayAnim(targetPed, dict, 'handsup_standing_base', 2.0, -2, 15.0, 1, 0, 0, 0, 0)
                                for _ = 1, RobbingTime / 2, 1 do
                                    PlayPedAmbientSpeechNative(targetPed, "GUN_BEG", "SPEECH_PARAMS_FORCE_NORMAL_CLEAR")
                                    Wait(2000)
                                end
                                FreezeEntityPosition(targetPed, true)
                                IsRobbingNPC = true
                                SetTimeout(RobbingTime, function()
                                    IsRobbingNPC = false
                                    RobTimeout(math.random(30000, 60000))
                                    if not IsEntityDead(targetPed) then
                                        if CanRob then
                                            if InDistance then
                                                SetEveryoneIgnorePlayer(PlayerId(), false)
                                                SetBlockingOfNonTemporaryEvents(targetPed, false)
                                                FreezeEntityPosition(targetPed, false)
                                                ClearPedTasks(targetPed)
                                                AddShockingEventAtPosition(99, GetEntityCoords(targetPed), 0.5)
                                                TriggerServerEvent('qb-traphouse:server:RobNpc', ClosestTraphouse)
                                                CanRob = false
                                            end
                                        end
                                    end
                                end)
                            end
                        else
                            if InDistance then
                                InDistance = false
                            end
                        end
                    end
                end
            else
                Wait(1000)
            end
        end
        Wait(3)
    end
end)

CreateThread(function ()
    local wait = 500
    while not LocalPlayer.state.isLoggedIn do
        -- do nothing
        Wait(wait)
    end

    SetTraphouseEntranceTargets()

    if QBCore.Functions.GetPlayerData() ~= nil then
        PlayerData = QBCore.Functions.GetPlayerData()
    end

    while true do
        wait = 500
        SetClosestTraphouse()

        if ClosestTraphouse ~= nil then
            if not InsideTraphouse then
                if isInsideEntranceTarget then
                    wait = 0
                    if IsControlJustPressed(0, 38) then
                        TriggerEvent("qb-traphouse:client:EnterTraphouse")
                        exports['qb-core']:HideText()
                    end
                end
            else
                local data = Config.TrapHouses[ClosestTraphouse]
                if not data.polyzoneBoxData['exit'].created then
                    local exitCoords = vector3(data.coords["enter"].x + POIOffsets.exit.x, data.coords["enter"].y + POIOffsets.exit.y, data.coords["enter"].z - Config.MinZOffset + POIOffsets.exit.z)
                    if Config.UseTarget then
                        RegisterTraphouseExitTarget(exitCoords, CurrentTraphouse, data)
                    else
                        RegisterTraphouseExitZone(exitCoords, CurrentTraphouse, data)
                    end
                end

                if not data.polyzoneBoxData['interaction'].created then
                    if Config.UseTarget then
                        RegisterTraphouseInteractionTarget(CurrentTraphouse, data)
                    else
                        RegisterTraphouseInteractionZone(CurrentTraphouse, data)
                    end
                end

                if isInsideExitTarget then
                    wait = 0
                    if IsControlJustPressed(0, 38) then
                        LeaveTraphouse(ClosestTraphouse, data)
                        exports['qb-core']:HideText()
                    end
                end

                if isInsideInteractionTarget then
                    wait = 0
                    if IsControlJustPressed(0, 38) then
                        OpenHeaderMenu(data)
                        exports['qb-core']:HideText()
                    end
                end
            end
        end
        Wait(wait)
    end

end)