local target, ox_target
local locations = {}
local pedBlips = {}
local clothingComponents = {
    face = 0,
    mask = 1,
    hair = 2,
    torso = 3,
    leg = 4,
    bag = 5,
    shoes = 6,
    accessory = 7,
    undershirt = 8,
    kevlar = 9,
    badge = 10,
    torso2 = 11
}
local clothingProps = {
    hat = 0,
    glasses = 1,
    ears = 2,
    watch = 6,
    bracelets = 7
}

NDCore.isResourceStarted("ox_target", function(started)
    target = started
    if not target then return end
    ox_target = exports.ox_target
end)

local function configPed(ped)
    SetPedCanBeTargetted(ped, false)
    SetEntityCanBeDamaged(ped, false)
    SetBlockingOfNonTemporaryEvents(ped, true)
    SetPedCanRagdollFromPlayerImpact(ped, false)
    SetPedResetFlag(ped, 249, true)
    SetPedConfigFlag(ped, 185, true)
    SetPedConfigFlag(ped, 108, true)
    SetPedConfigFlag(ped, 208, true)
    SetPedCanRagdoll(ped, false)
end

local function setClothing(ped, clothing)
    if not clothing then return end
    for component, clothingInfo in pairs(clothing) do
        if clothingComponents[component] then
            SetPedComponentVariation(ped, clothingComponents[component], clothingInfo.drawable, clothingInfo.texture, 0)
        elseif clothingProps[component] then
            SetPedPropIndex(ped, clothingProps[component], clothingInfo.drawable, clothingInfo.texture, true)
        end
    end
end

local function groupCheck(groups, playerGroups)
    if not groups or #groups == 0 then return true end
    for i=1, #groups do
        if playerGroups and playerGroups[groups[i]] then
            return true
        end
    end
end

local function createBlip(coords, info, i)
    if not info then return end
    local groups = info.groups
    local playerGroups = NDCore.player?.groups

    local key = i or #pedBlips+1
    pedBlips[key] = {
        groups = groups,
        info = info,
        coords = coords
    }

    if not groupCheck(groups, playerGroups) then return end
    local blip = AddBlipForCoord(coords.x, coords.y, coords.z)
    SetBlipSprite(blip, info.sprite or 280)
    SetBlipScale(blip, info.scale or 0.8)
    SetBlipColour(blip, info.color or 3)
    SetBlipAsShortRange(blip, true)
    if info.label then                
        BeginTextCommandSetBlipName("STRING")
        AddTextComponentString(info.label)
        EndTextCommandSetBlipName(blip)
    end

    pedBlips[key].blip = blip
    return blip
end

local function updateBlips(playerGroups)
    for i=1, #pedBlips do
        local blipInfo = pedBlips[i]
        local access = groupCheck(blipInfo.groups, playerGroups)
        if access and not blipInfo.blip or not DoesBlipExist(blipInfo.blip) then
            createBlip(blipInfo.coords, blipInfo.info, i)
        elseif not access and blipInfo.blip and DoesBlipExist(blipInfo.blip) then
            RemoveBlip(blipInfo.blip)
        end
    end
end

local function disableCollisionWithModels(ped, models)
    if not models then return end
    local coords = GetEntityCoords(ped)
    for i=1, #models do
        local m = models[i]
        local model = type(m) == "number" and m or GetHashKey(m)
        local obj = GetClosestObjectOfType(coords.x, coords.y, coords.z, 5.0, model, false, false, false)
        SetEntityNoCollisionEntity(ped, obj, false)
    end
end

function NDCore.createAiPed(info, cb)
    local ped
    local model = type(info.model) == "string" and GetHashKey(info.model) or info.model
    local blipInfo = info.blip
    local anim = info.anim
    local clothing = info.clothing
    local coords = info.coords
    local options = info.options
    local blip = createBlip(coords, blipInfo)
    local point = lib.points.new({
        coords = coords.xyz,
        distance = info.distance or 25.0
    })
    
    local id = #locations+1
    locations[id] = {
        point = point,
        blip = blip,
        options = info.options,
        resource = info.resource or GetInvokingResource()
    }

    if blip and blipInfo and blipInfo.showWhenNear and DoesBlipExist(blip) and #(GetEntityCoords(cache.ped)-coords.xyz) > point.distance then
        SetBlipAlpha(blip, 0)
    end

    function point:onEnter()
        local found, ground = GetGroundZFor_3dCoord(coords.x, coords.y, coords.z, true)
        lib.requestModel(model)
        ped = CreatePed(4, model, coords.x, coords.y, found and ground or coords.z, coords.w or coords.h or info.heading, false, false)

        local time = GetCloudTimeAsInt()
        while not DoesEntityExist(ped) and time-GetCloudTimeAsInt() < 5 do
            Wait(100)
        end

        if cb then
            cb(ped)
        end

        disableCollisionWithModels(ped, info.disableCollisionWithModels)
        configPed(ped)
        setClothing(ped, clothing)
        locations[id].ped = ped

        if anim and anim.dict and anim.clip then
            lib.requestAnimDict(anim.dict)
            TaskPlayAnim(ped, anim.dict, anim.clip, 2.0, 8.0, -1, 1, 0, 0, 0, 0)
        end

        if target and options then
            Wait(500)
            ox_target:addLocalEntity({ped}, options)
        end

        if blip and blipInfo and blipInfo.showWhenNear and DoesBlipExist(blip) then
            SetBlipAlpha(blip, 255)
        end
    end

    function point:onExit()
        if blip and blipInfo and blipInfo.showWhenNear and DoesBlipExist(blip) then
            SetBlipAlpha(blip, 0)
        end
        if ped and DoesEntityExist(ped) then
            if target and options then
                ox_target:removeLocalEntity({ped})
            end
            Wait(500)
            DeleteEntity(ped)
        end
    end

    return id
end

function NDCore.removeAiPed(id)
    local info = locations[id]
    if not info then return end

    local ped = info.ped
    local blip = info.blip
    info.point:remove()
    locations[id] = nil
    
    if blip and DoesBlipExist(blip) then
        RemoveBlip(blip)
    end

    if ped and DoesEntityExist(ped) then
        if info.options then
            ox_target:removeLocalEntity({ped})
        end
        DeleteEntity(ped)
    end
end

RegisterNetEvent("ND:updateCharacter", function(character)
    Wait(3000)
    if character.id ~= NDCore.player?.id then return end
    updateBlips(character.groups)
end)

RegisterNetEvent("ND:characterLoaded", function(character)
    Wait(3000)
    if character.id ~= NDCore.player?.id then return end
    updateBlips(character.groups)
end)

AddEventHandler("onResourceStop", function(name)
    if name == GetCurrentResourceName() then
        for i, _ in ipairs(locations) do
            NDCore.removeAiPed(i)
        end
    else
        for i, v in ipairs(locations) do
            if v.resource == name then
                NDCore.removeAiPed(i)
            end
        end
    end
end)

RegisterCommand("getclothing", function(source, args, rawCommand)
    local info = ""
    for k, v in pairs(clothingComponents) do
        info = ("%s\n%s = { drawable = %s, texture = %s },"):format(info, k, GetPedDrawableVariation(cache.ped, v), GetPedTextureVariation(cache.ped, v))
    end
    for k, v in pairs(clothingProps) do
        info = ("%s\n%s = { drawable = %s, texture = %s },"):format(info, k, GetPedPropIndex(cache.ped, v), GetPedPropTextureIndex(cache.ped, v))
    end
    lib.setClipboard(info)
end, false)
