-- UALL: переклад банерів СИСТЕМИ НАВЧАННЯ (Blizzard_TutorialManager).
-- Джерело: TutorialMainFrame_Frame.ContainerFrame.Text (банер «Mount up by...»).
-- Ці рядки специфічні для туторіалів і НЕ входять у наш датамайн, тож:
--   • перекладаємо словником ns.uiMisc/ns.uiStrings;
--   • незнайомі ЗБИРАЄМО в db.ui (як HelpTip) → доперекладемо оновленням.
-- Звичайні (не forbidden) фрейми; текст оновлюється покроково → self-heal SetText.
-- Guard'и «вже зачеплено» тримаємо у ВЛАСНИХ таблицях (не пишемо в Blizzard-фрейми).

local _, ns = ...

local function Enabled()
    return (not ns.IsEnabled) or ns.IsEnabled("interface.menu")
end

-- Відомі фрейми банерів навчання (захисно — імена в Midnight стабільні, але
-- перевіряємо існування).
local FRAMES = { "TutorialMainFrame_Frame", "TutorialPointerFrame" }

local function Lookup(t)
    local ua = (ns.uiMisc and ns.uiMisc[t]) or (ns.uiStrings and ns.uiStrings[t])
    if ua and ua ~= t then return ua end
    return nil
end

local function Capture(t)
    if not (ns.GetCaptureDB and t) then return end
    if #t < 6 or not t:find("%a") then return end            -- код/коротке — пропуск
    if ns.HasCyrillic and ns.HasCyrillic(t) then return end
    local ok, db = pcall(ns.GetCaptureDB)
    if not ok or not db then return end
    db.ui = db.ui or {}
    db.ui[t] = true          -- МОВЧКИ (тутор-рядків багато — не спамимо чат)
end

local applying = false
local hookedFS = setmetatable({}, { __mode = "k" })

local function TrFS(fs)
    if applying or not fs or not fs.GetText then return end
    local t = ns.SafeText and ns.SafeText(fs:GetText()) or fs:GetText()
    if not t or t == "" then return end
    if ns.ApplyCyrFont then ns.ApplyCyrFont(fs) end
    if ns.HasCyrillic and ns.HasCyrillic(t) then return end
    if ns.ShowOriginal and ns.ShowOriginal() then return end
    -- Текст банера буває ОБГОРНУТИЙ кольором |cnNORMAL_FONT_COLOR:...|r (іменований
    -- колір, новий Midnight-синтаксис) або |cAARRGGBB...|r. Для пошуку знімаємо
    -- обгортку, а переклад загортаємо назад тим самим кольором.
    local pre, inner, post = t:match("^(|c[nN][%w_]+:)(.-)(|r)$")
    if not pre then pre, inner, post = t:match("^(|c%x%x%x%x%x%x%x%x)(.-)(|r)$") end
    inner = inner or t
    local ua = Lookup(inner)
    if ua and ua ~= inner then
        applying = true
        local out = pre and (pre .. ua .. post) or ua
        if ns.Remember then ns.Remember(fs, t, out) end
        fs:SetText(out)
        if ns.ApplyCyrFont then ns.ApplyCyrFont(fs) end
        applying = false
    else
        Capture(inner)                                        -- незнайоме → у чергу збору
    end
end

-- self-heal: наступний крок навчання перепише .Text без нового Show.
local function selfHeal(fs)
    if applying or not Enabled() then return end
    pcall(TrFS, fs)
end
local function hookFS(fs)
    if hookedFS[fs] or type(fs) ~= "table" or type(fs.SetText) ~= "function" then return end
    hookedFS[fs] = true
    hooksecurefunc(fs, "SetText", selfHeal)
    if type(fs.SetFormattedText) == "function" then
        hooksecurefunc(fs, "SetFormattedText", selfHeal)
    end
end

local function walk(frame, depth)
    if not frame or depth > 6 or type(frame) ~= "table" then return end
    if frame.GetRegions then
        for _, rg in ipairs({ frame:GetRegions() }) do
            if rg.IsObjectType and rg:IsObjectType("FontString") then
                hookFS(rg); pcall(TrFS, rg)
            end
        end
    end
    if frame.GetChildren then
        for _, c in ipairs({ frame:GetChildren() }) do if not ns.IsTooltipFrame(c) then walk(c, depth + 1) end end
    end
end

-- NAME-AGNOSTIC: `_G["TutorialMainFrame_Frame"]` може бути НЕ тим ключем (фрейм
-- анонімний, а це лише debug-ім'я зі стека). Тому не покладаємось на _G — скануємо
-- EnumerateFrames і чіпляємо будь-який фрейм, чиє ім'я/debug-ім'я містить "Tutorial".
-- Guard у НАШІЙ слабкій таблиці (не пишемо маркери в Blizzard-фрейм → taint).
local hookedFrames = setmetatable({}, { __mode = "k" })

local function frameName(f)
    local ok, n = pcall(function()
        return (f.GetName and f:GetName()) or (f.GetDebugName and f:GetDebugName())
    end)
    return (ok and type(n) == "string") and n or nil
end

local function HookOne(f)
    if not f or hookedFrames[f] or type(f) ~= "table" or not f.HookScript then return end
    hookedFrames[f] = true
    f:HookScript("OnShow", function(self)
        -- текст банера інколи ставлять ПІСЛЯ Show → кілька відкладених спроб (не тікер).
        for _, d in ipairs({ 0, 0.05, 0.2 }) do
            C_Timer.After(d, function()
                if self.IsShown and self:IsShown() then pcall(walk, self, 1) end
            end)
        end
    end)
    pcall(walk, f, 1)                                         -- якщо вже показаний
end

local function ScanTutorialFrames()
    if not Enabled() or not EnumerateFrames then return end
    local f = EnumerateFrames()
    while f do
        pcall(function()
            local n = frameName(f)
            if n and n:find("Tutorial") then HookOne(f) end
        end)
        f = EnumerateFrames(f)
    end
end
ScanTutorialFrames()

local ev = CreateFrame("Frame")
ev:RegisterEvent("ADDON_LOADED")
ev:RegisterEvent("PLAYER_ENTERING_WORLD")
ev:SetScript("OnEvent", function()
    ScanTutorialFrames()
    -- фрейми навчання можуть створюватись трохи пізніше за подію → кілька дострілів.
    for _, d in ipairs({ 0.5, 2, 5 }) do C_Timer.After(d, ScanTutorialFrames) end
end)
