-- [ TRACKED TOOLTIP PARSER ] ------------------------------------------------------------------------
---@type Orbit
local Orbit = Orbit
Orbit.TooltipParser = {}
local Parser = Orbit.TooltipParser

-- [ CONSTANTS ] -------------------------------------------------------------------------------------
local ACTIVE_DURATION_OVERRIDES = {
    [1122] = 30, -- Summon Infernal: first match is 2s stun, pet lasts 30s
    [633] = 0, -- Lay on Hands: instant, Forbearance is not active phase
    [48743] = 0, -- Death Pact: instant heal, absorb debuff is not active phase
}

-- [ LOCALE PATTERNS ] -------------------------------------------------------------------------------
local LOCALE_DATA = {
    enUS = {
        active = { "for (%d+%.?%d*) sec", "lasts (%d+%.?%d*) sec", "over (%d+%.?%d*) sec" },
        cdKeywords = { "[Cc]ooldown", "[Rr]echarge" },
        sec = "[Ss]ec", min = "[Mm]in",
    },
    deDE = {
        active = { "für (%d+%.?%d*) Sek", "(%d+%.?%d*) Sek%. lang", "über (%d+%.?%d*) Sek" },
        cdKeywords = { "[Aa]bklingzeit", "[Aa]ufladung" },
        sec = "Sek%.?", min = "Min%.?",
    },
    frFR = {
        active = { "pendant (%d+%.?%d*) s", "dure (%d+%.?%d*) s", "sur (%d+%.?%d*) s" },
        cdKeywords = { "[Rr]echarge", "[Rr]écupération" },
        sec = "s%.?", min = "min%.?",
    },
    esES = {
        active = { "durante (%d+%.?%d*) s", "(%d+%.?%d*) s de duración", "a lo largo de (%d+%.?%d*) s" },
        cdKeywords = { "[Rr]eutilización", "[Rr]ecarga" },
        sec = "s%.?", min = "min%.?",
    },
    ptBR = {
        active = { "por (%d+%.?%d*) s", "durante (%d+%.?%d*) s", "ao longo de (%d+%.?%d*) s" },
        cdKeywords = { "[Rr]ecarga", "[Rr]ecuperação" },
        sec = "s%.?", min = "min%.?",
    },
    ruRU = {
        active = { "в течение (%d+%.?%d*) сек", "на (%d+%.?%d*) сек", "(%d+%.?%d*) сек%.?" },
        cdKeywords = { "[Пп]ерезарядк", "[Вв]осстановлени" },
        sec = "сек%.?", min = "мин%.?",
    },
    koKR = {
        active = { "(%d+%.?%d*)초 동안", "(%d+%.?%d*)초에 걸쳐" },
        cdKeywords = { "재사용 대기시간", "충전" },
        sec = "초", min = "분",
    },
    zhCN = {
        active = { "持续(%d+%.?%d*)秒", "(%d+%.?%d*)秒内" },
        cdKeywords = { "冷却", "充能" },
        sec = "秒", min = "分",
    },
    zhTW = {
        active = { "持續(%d+%.?%d*)秒", "(%d+%.?%d*)秒內" },
        cdKeywords = { "冷卻", "充能" },
        sec = "秒", min = "分",
    },
}
LOCALE_DATA.enGB = LOCALE_DATA.enUS
LOCALE_DATA.esMX = LOCALE_DATA.esES

local locale = GetLocale()
local L = LOCALE_DATA[locale] or LOCALE_DATA.enUS
local ACTIVE_DURATION_PATTERNS = L.active
local COOLDOWN_KEYWORDS = L.cdKeywords
local SEC_UNIT = L.sec
local MIN_UNIT = L.min

-- [ HELPERS ] ---------------------------------------------------------------------------------------
local function StripEscapes(text)
    text = text:gsub("|4([^:]+):([^;]+);", "%2")
    text = text:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", "")
    return text
end

-- [ PARSE ACTIVE DURATION ] -------------------------------------------------------------------------
function Parser:ParseActiveDuration(itemType, id)
    if itemType == "spell" and ACTIVE_DURATION_OVERRIDES[id] then
        return ACTIVE_DURATION_OVERRIDES[id]
    end
    local text
    if itemType == "spell" then
        text = C_Spell.GetSpellDescription(id)
    elseif itemType == "item" then
        local tooltipData = C_TooltipInfo and C_TooltipInfo.GetItemByID(id)
        if tooltipData and tooltipData.lines then
            for _, line in ipairs(tooltipData.lines) do
                text = (text or "") .. " " .. (line.leftText or "")
            end
        end
    end
    if not text then return nil end
    text = StripEscapes(text)
    for _, pattern in ipairs(ACTIVE_DURATION_PATTERNS) do
        local num = text:match(pattern)
        if num then return tonumber(num) end
    end
    return nil
end

-- [ PARSE COOLDOWN DURATION ] -----------------------------------------------------------------------
function Parser:ParseCooldownDuration(itemType, id)
    local tooltipData
    if itemType == "spell" then
        tooltipData = C_TooltipInfo and C_TooltipInfo.GetSpellByID(id)
    elseif itemType == "item" then
        tooltipData = C_TooltipInfo and C_TooltipInfo.GetItemByID(id)
    end
    if not tooltipData or not tooltipData.lines then return nil end
    local best
    for _, line in ipairs(tooltipData.lines) do
        local text = StripEscapes(line.rightText or line.leftText or "")
        local compoundMin, compoundSec = text:match("(%d+%.?%d*)%s*" .. MIN_UNIT .. "%s+(%d+%.?%d*)%s*" .. SEC_UNIT)
        if compoundMin and compoundSec then
            local val = (tonumber(compoundMin) * 60) + tonumber(compoundSec)
            if not best or val > best then best = val end
        else
            for _, keyword in ipairs(COOLDOWN_KEYWORDS) do
                local min = text:match("(%d+%.?%d*)%s*" .. MIN_UNIT .. "%s+" .. keyword)
                if min then
                    local val = tonumber(min) * 60
                    if not best or val > best then best = val end
                end
                local sec = text:match("(%d+%.?%d*)%s*" .. SEC_UNIT .. "%s+" .. keyword)
                if sec then
                    local val = tonumber(sec)
                    if not best or val > best then best = val end
                end
            end
        end
    end
    return best
end

-- [ BUILD PHASE CURVE ] -----------------------------------------------------------------------------
local _phaseCurveCache = setmetatable({}, { __mode = "v" })
function Parser:BuildPhaseCurve(activeDuration, cooldownDuration)
    if not activeDuration or not cooldownDuration or cooldownDuration <= 0 or activeDuration >= cooldownDuration then
        local key = "fallback"
        if not _phaseCurveCache[key] then
            local c = C_CurveUtil.CreateCurve()
            c:AddPoint(0.0, 0); c:AddPoint(0.001, 1); c:AddPoint(1.0, 1)
            _phaseCurveCache[key] = c
        end
        return _phaseCurveCache[key]
    end
    local key = activeDuration .. ":" .. cooldownDuration
    if _phaseCurveCache[key] then return _phaseCurveCache[key] end
    local breakpoint = 1.0 - (activeDuration / cooldownDuration)
    local curve = C_CurveUtil.CreateCurve()
    curve:AddPoint(0.0, 0)
    curve:AddPoint(0.001, 1)
    curve:AddPoint(math.max(breakpoint, 0.002), 1)
    curve:AddPoint(breakpoint + 0.001, 0)
    curve:AddPoint(1.0, 0)
    _phaseCurveCache[key] = curve
    return curve
end
