local isASLActive = false
local aslCamera = nil
local currentAnimations = {}
local targetPlayer = nil
local isThirdPersonMode = false
local isTypingActive = false

local function LoadAnimDict(dict)
    if not HasAnimDictLoaded(dict) then
        RequestAnimDict(dict)
        while not HasAnimDictLoaded(dict) do
            Wait(1)
        end
    end
end

local function GetNearestPlayer()
    local players = GetActivePlayers()
    local playerPed = PlayerPedId()
    local playerCoords = GetEntityCoords(playerPed)
    local nearestPlayer = nil
    local nearestDistance = math.huge

    for _, player in ipairs(players) do
        if player ~= PlayerId() then
            local targetPed = GetPlayerPed(player)
            if DoesEntityExist(targetPed) then
                local targetCoords = GetEntityCoords(targetPed)
                local distance = #(playerCoords - targetCoords)

                if distance < Config.Settings.maxTargetDistance and distance < nearestDistance then
                    nearestDistance = distance
                    nearestPlayer = targetPed
                end
            end
        end
    end

    return nearestPlayer, nearestDistance
end

local function CreateASLCamera()
    local playerPed = PlayerPedId()

    if not targetPlayer then  -- Self mode
        local playerCoords = GetEntityCoords(playerPed)
        local playerHeading = GetEntityHeading(playerPed)

        local rad = math.rad(playerHeading)
        local distance = 1.5
        local height = 0.5

        local camX = playerCoords.x - math.sin(rad) * distance
        local camY = playerCoords.y + math.cos(rad) * distance
        local camZ = playerCoords.z + height

        aslCamera = CreateCamWithParams(
            "DEFAULT_SCRIPTED_CAMERA",
            camX, camY, camZ,
            0.0, 0.0, 0.0,
            Config.Settings.cameraFOV,
            true, 2
        )

        PointCamAtEntity(aslCamera, playerPed, 0.0, 0.0, 0.5, true)
    else
        local playerCoords = GetEntityCoords(playerPed)
        local playerHeading = GetEntityHeading(playerPed)
        local rad = math.rad(playerHeading)

        local forwardOffset = 0.5
        local height = 0.5

        local camX = playerCoords.x - math.sin(rad) * forwardOffset
        local camY = playerCoords.y + math.cos(rad) * forwardOffset
        local camZ = playerCoords.z + height

        aslCamera = CreateCamWithParams(
            "DEFAULT_SCRIPTED_CAMERA",
            camX, camY, camZ,
            0.0, 0.0, 0.0,
            Config.Settings.cameraFOV,
            true, 2
        )

        PointCamAtEntity(aslCamera, targetPlayer, 0.0, 0.0, 0.5, true)
    end

    RenderScriptCams(true, true, Config.Settings.cameraTransitionTime, true, true)

    return aslCamera
end

local function RestoreCamera()
    if aslCamera then
        RenderScriptCams(false, true, Config.Settings.cameraTransitionTime, true, true)
        DestroyCam(aslCamera, false)
        aslCamera = nil
    end

    SetFollowPedCamViewMode(0)
end

local function NormalizeText(text)
    local normalized = string.lower(text)

    for abbr, full in pairs(Config.TextRules.abbreviations) do
        normalized = string.gsub(normalized, "%f[%a]" .. abbr .. "%f[%A]", full)
    end

    return normalized
end

local function ParseTextForAnimations(text)
    local animations = {}
    local normalizedText = NormalizeText(text)

    -- Check if entire text is a phrase
    if Config.PhraseAnimations[normalizedText] then
        local animData = Config.PhraseAnimations[normalizedText]
        table.insert(animations, {
            type = "phrase",
            animation = animData,
            text = normalizedText,
            duration = animData.duration or Config.Settings.wordDuration
        })
        return animations
    end

    -- Process text word by word, checking for phrases starting at current position
    local words = {}
    for word in string.gmatch(normalizedText, "%S+") do
        table.insert(words, word)
    end

    local i = 1
    while i <= #words do
        local foundPhrase = false

        -- Check for multi-word phrases starting at current position
        for phraseLength = math.min(5, #words - i + 1), 2, -1 do
            local phrase = table.concat(words, " ", i, i + phraseLength - 1)

            if Config.PhraseAnimations[phrase] then
                local animData = Config.PhraseAnimations[phrase]
                table.insert(animations, {
                    type = "phrase",
                    animation = animData,
                    text = phrase,
                    duration = animData.duration or Config.Settings.wordDuration
                })
                i = i + phraseLength
                foundPhrase = true
                break
            end
        end

        if not foundPhrase then
            local word = words[i]

            -- Check if single word is a phrase
            if Config.PhraseAnimations[word] then
                local animData = Config.PhraseAnimations[word]
                table.insert(animations, {
                    type = "word",
                    animation = animData,
                    text = word,
                    duration = animData.duration or Config.Settings.wordDuration
                })
            else
                -- Spell out letter by letter
                for j = 1, #word do
                    local char = string.sub(word, j, j)

                    if Config.LetterAnimations[char] then
                        table.insert(animations, {
                            type = "letter",
                            animation = Config.LetterAnimations[char],
                            text = char,
                            duration = Config.Settings.letterDuration
                        })
                    elseif Config.NumberAnimations[char] then
                        table.insert(animations, {
                            type = "number",
                            animation = Config.NumberAnimations[char],
                            text = char,
                            duration = Config.Settings.letterDuration
                        })
                    elseif Config.SpecialAnimations[char] then
                        table.insert(animations, {
                            type = "special",
                            animation = Config.SpecialAnimations[char],
                            text = char,
                            duration = Config.Settings.letterDuration
                        })
                    end
                end

                table.insert(animations, {
                    type = "pause",
                    duration = Config.Settings.pauseBetweenWords
                })
            end

            i = i + 1
        end
    end

    return animations
end

local function PlayASLAnimation(animData)
    local playerPed = PlayerPedId()

    if animData.type == "pause" then
        Wait(animData.duration)
        return
    end

    if not animData.animation or not animData.animation.dict or not animData.animation.name then
        if Config.Settings.debugMode then
            print(string.format("^3[ASL Debug] Missing animation data for: %s^0", animData.text or "unknown"))
        end
        return
    end

    local animDict = animData.animation.dict
    local animName = animData.animation.name

    if Config.Settings.debugMode then
        print(string.format("^2[ASL Debug] Playing: Dict=%s, Name=%s, Text=%s^0", animDict, animName, animData.text))
    end

    LoadAnimDict(animDict)

    if not HasAnimDictLoaded(animDict) then
        if Config.Settings.debugMode then
            print(string.format("^1[ASL Debug] Failed to load animation dictionary: %s^0", animDict))
        end
        SendNUIMessage({
            type = 'updateStatus',
            status = 'Animation not found: ' .. animDict
        })
        return
    end

    TaskPlayAnim(
        playerPed,
        animDict,
        animName,
        Config.Settings.blendInSpeed,
        Config.Settings.blendOutSpeed,
        animData.duration,
        Config.Settings.animFlag,
        0.0,
        false, false, false
    )

    if not IsEntityPlayingAnim(playerPed, animDict, animName, 3) then
        Wait(50)
        if not IsEntityPlayingAnim(playerPed, animDict, animName, 3) and Config.Settings.debugMode then
            print(string.format("^3[ASL Debug] Animation may not have started: %s/%s^0", animDict, animName))
        end
    end

    Wait(animData.duration)

    if animData.type == "letter" then
        Wait(Config.Settings.pauseBetweenLetters)
    end

    RemoveAnimDict(animDict)
end

local function StartASLMode()
    if isASLActive then
        StopASLMode()
        return
    end

    local nearest, distance = GetNearestPlayer()
    if not nearest then
        if Config.Settings.debugMode then
            TriggerEvent('chat:addMessage', {
                color = {255, 100, 100},
                args = {"ASL", "No players nearby to sign to!"}
            })
        end
        return
    end
    targetPlayer = nearest

    CreateASLCamera()
    isASLActive = true
    isThirdPersonMode = false

    SetNuiFocus(true, true)
    SendNUIMessage({
        type = 'showASLInput',
        persistent = true
    })
end

local function StartASLSelfMode()
    if isASLActive then
        StopASLMode()
        return
    end

    targetPlayer = nil

    CreateASLCamera()
    isASLActive = true
    isThirdPersonMode = false

    SetNuiFocus(true, true)
    SendNUIMessage({
        type = 'showASLInput',
        persistent = true
    })
end

local function StartASLThirdPersonMode()
    if isASLActive then
        StopASLMode()
        return
    end

    targetPlayer = nil
    isASLActive = true
    isThirdPersonMode = true

    SetNuiFocus(false, false)
    SendNUIMessage({
        type = 'showASLInput',
        persistent = true,
        thirdPerson = true
    })

    if Config.Settings.debugMode then
        TriggerEvent('chat:addMessage', {
            color = {100, 200, 100},
            args = {"ASL", "Third-person mode - Press ENTER to type, ESC to exit. You can move freely!"}
        })
    end
end

local function ToggleThirdPersonInput(enable)
    if isThirdPersonMode and isASLActive then
        if enable then
            isTypingActive = true
            SetNuiFocus(true, false)
            SendNUIMessage({
                type = 'focusInput'
            })
        else
            isTypingActive = false
            SetNuiFocus(false, false)
            SendNUIMessage({
                type = 'blurInput'
            })
        end
    end
end

local function ProcessASLText(text)
    if not text or text == "" then return end

    currentAnimations = ParseTextForAnimations(text)

    if #currentAnimations == 0 then
        SendNUIMessage({
            type = 'updateStatus',
            status = 'No valid animations found'
        })
        return
    end

    CreateThread(function()
        for _, animData in ipairs(currentAnimations) do
            if not isASLActive then break end

            if animData.type ~= "pause" and animData.text then
                SendNUIMessage({
                    type = 'updateStatus',
                    status = 'Signing: ' .. string.upper(animData.text)
                })
            end

            PlayASLAnimation(animData)
        end

        if isASLActive then
            SendNUIMessage({
                type = 'updateStatus',
                status = ''
            })
        end
    end)
end

RegisterNUICallback('submitASLText', function(data, cb)
    ProcessASLText(data.text)

    if isThirdPersonMode then
        ToggleThirdPersonInput(false)
    end

    cb('ok')
end)

RegisterNUICallback('closeASL', function(data, cb)
    StopASLMode()
    cb('ok')
end)

RegisterNUICallback('releaseThirdPersonFocus', function(data, cb)
    if isThirdPersonMode then
        ToggleThirdPersonInput(false)
    end
    cb('ok')
end)

function StopASLMode()
    if not isASLActive then return end

    isASLActive = false
    isThirdPersonMode = false
    isTypingActive = false
    targetPlayer = nil
    currentAnimations = {}

    local playerPed = PlayerPedId()
    ClearPedTasks(playerPed)

    if aslCamera then
        RestoreCamera()
    end

    SetNuiFocus(false, false)
    SendNUIMessage({
        type = 'hideASLInput'
    })

    if Config.Settings.debugMode then
        TriggerEvent('chat:addMessage', {
            color = {100, 200, 100},
            args = {"ASL", "Stopped signing"}
        })
    end
end

RegisterCommand('asl', function(_, args)
    if #args > 0 then
        if not isASLActive then
            StartASLMode()
            Wait(500)
        end

        local inputText = table.concat(args, " ")
        ProcessASLText(inputText)
    else
        StartASLMode()
    end
end, false)

RegisterCommand('aslself', function(_, args)
    if #args > 0 then
        if not isASLActive then
            StartASLSelfMode()
            Wait(500)
        end

        local inputText = table.concat(args, " ")
        ProcessASLText(inputText)
    else
        StartASLSelfMode()
    end
end, false)

RegisterCommand('asl2', function(_, args)
    if #args > 0 then
        if not isASLActive then
            StartASLThirdPersonMode()
        end

        local inputText = table.concat(args, " ")
        ProcessASLText(inputText)
    else
        StartASLThirdPersonMode()
    end
end, false)

CreateThread(function()
    while true do
        Wait(0)
        if isASLActive then
            if not isThirdPersonMode then
                HideHudAndRadarThisFrame()

                if not Config.Settings.allowMovement then
                    DisableControlAction(0, 30, true)
                    DisableControlAction(0, 31, true)
                    DisableControlAction(0, 32, true)
                    DisableControlAction(0, 33, true)
                    DisableControlAction(0, 34, true)
                    DisableControlAction(0, 35, true)
                end
            end
        end
    end
end)

CreateThread(function()
    while true do
        Wait(0)
        if isASLActive and isThirdPersonMode and not isTypingActive then
            if IsControlJustPressed(0, 18) or
               IsControlJustPressed(0, 201) or
               IsControlJustPressed(0, 176) then
                ToggleThirdPersonInput(true)
            end
        end
    end
end)

RegisterCommand('asl_activate_input', function()
    if isASLActive and isThirdPersonMode and not isTypingActive then
        ToggleThirdPersonInput(true)
    end
end, false)
RegisterKeyMapping('asl_activate_input', 'Activate ASL Input (Third Person)', 'keyboard', 'RETURN')

RegisterKeyMapping('aslclose', 'Close ASL Mode', 'keyboard', 'ESCAPE')
RegisterCommand('aslclose', function()
    if isASLActive then
        StopASLMode()
    end
end, false)

AddEventHandler('onResourceStop', function(resourceName)
    if GetCurrentResourceName() == resourceName then
        StopASLMode()
    end
end)

exports('openASL', function(text)
    if text and type(text) == 'string' and #text > 0 then
        if not isASLActive then
            StartASLMode()
            Wait(500)
        end
        ProcessASLText(text)
    else
        StartASLMode()
    end
end)

exports('openASLSelf', function(text)
    if text and type(text) == 'string' and #text > 0 then
        if not isASLActive then
            StartASLSelfMode()
            Wait(500)
        end
        ProcessASLText(text)
    else
        StartASLSelfMode()
    end
end)

exports('openASL2', function(text)
    if text and type(text) == 'string' and #text > 0 then
        if not isASLActive then
            StartASLThirdPersonMode()
        end
        ProcessASLText(text)
    else
        StartASLThirdPersonMode()
    end
end)

exports('closeASL', function()
    StopASLMode()
end)

exports('isASLActive', function()
    return isASLActive
end)

RegisterCommand('aslhelp', function()
    TriggerEvent('chat:addMessage', {
        color = {100, 200, 255},
        multiline = true,
        args = {"ASL Help", [[
Commands:
- /asl - Sign to nearest player (camera focus)
- /asl [text] - Sign text directly to nearest player
- /aslself - Sign to yourself (practice mode with camera)
- /aslself [text] - Sign text directly in self mode
- /asl2 - Third-person signing (move while signing!)
- /asl2 [text] - Sign text in third-person mode
- /asldebug - Toggle debug mode
- Press ESC to close ASL mode

Third-Person Mode Controls (/asl2):
- Press ENTER to activate text input
- Press ENTER again to sign the text
- Press ESC to exit text input or close ASL
- You can move freely with WASD when not typing

Modes:
- Camera modes (/asl, /aslself): Focuses camera, no movement
- Third-person mode (/asl2): Keep normal view, move freely

Exports Available (for developers):
- exports['aslmenu']:openASL() - Opens normal ASL mode
- exports['aslmenu']:openASL2() - Opens third-person mode
- exports['aslmenu']:closeASL() - Closes ASL
- exports['aslmenu']:isASLActive() - Check if active

The system will:
- Play full phrase animations when available
- Otherwise spell words letter by letter
- In third-person mode, you can walk/run while signing!]]
        }
    })
end, false)

RegisterCommand('asldebug', function()
    Config.Settings.debugMode = not Config.Settings.debugMode
    -- Always show debug toggle confirmation
    TriggerEvent('chat:addMessage', {
        color = {200, 200, 100},
        args = {"ASL", "Debug mode " .. (Config.Settings.debugMode and "enabled" or "disabled")}
    })
end, false)