-------------------------------------------------------------------------------
-- RealNibba's Raid Tools — AutoInvite.lua  v1.0
-- Invite players who whisper/guild-chat a keyword.
-- SavedVariable: RNToolsAutoInviteSave
-- UI styled to match the RNTools neutral/gold theme.
-------------------------------------------------------------------------------

local AceGUI = LibStub("AceGUI-3.0")

local INVITE_COOLDOWN = 10

local inviteCooldowns = {}

-------------------------------------------------------------------------------
-- Colour palette (matches RNTools theme)
-------------------------------------------------------------------------------
local C = {
    bg = { 0.08, 0.08, 0.08, 0.00 },
}

-------------------------------------------------------------------------------
-- Visual helpers
-------------------------------------------------------------------------------
local function AISetBG(f, r, g, b, a)
    if not f._bgTex then
        f._bgTex = f:CreateTexture(nil, "BACKGROUND")
        f._bgTex:SetAllPoints(f)
    end
    f._bgTex:SetTexture(r, g, b, a)
end

local function AIHeading(text)
    local h = AceGUI:Create("Heading")
    h:SetText(text)
    return h
end

-------------------------------------------------------------------------------
-- Saved variables bootstrap
-------------------------------------------------------------------------------
function RNTools_AI_EnsureSave()
    if type(RNToolsAutoInviteSave) ~= "table" then
        RNToolsAutoInviteSave = {}
    end
    local s = RNToolsAutoInviteSave
    if s.isEnabled       == nil then s.isEnabled       = false end
    if s.listenGuild     == nil then s.listenGuild     = false end
    if s.responseEnabled == nil then s.responseEnabled = false end
    if s.responseText    == nil then s.responseText    = "[RNTools] You have been invited!" end
    if type(s.keywords)  ~= "table" then
        s.keywords = {
            { keyword = "+",   contains = false },
            { keyword = "inv", contains = false },
            { keyword = "invite", contains = false },
        }
    end
    if type(s.lists) ~= "table" then s.lists = {} end
end

-------------------------------------------------------------------------------
-- Invite logic
-------------------------------------------------------------------------------
local function CheckListed(srcLower)
    for _, entry in ipairs(RNToolsAutoInviteSave.lists) do
        if string.lower(entry.playerName or "") == srcLower then
            return entry.isWhitelisted == true
        end
    end
    return nil
end

local function IsOnCooldown(srcLower)
    local t = inviteCooldowns[srcLower]
    return t and (GetTime() - t) < INVITE_COOLDOWN
end

local function TryInvite(msg, src, forceInvite)
    local s   = RNToolsAutoInviteSave
    local sl  = string.lower(src or "")
    local ml  = string.lower(msg or "")
    if IsOnCooldown(sl) then return end
    local hit = false
    if forceInvite then
        hit = true
    else
        for _, kw in ipairs(s.keywords) do
            local k = string.lower(kw.keyword or "")
            if k ~= "" then
                if kw.contains then
                    if string.find(ml, k, 1, true) then hit = true; break end
                else
                    if ml == k then hit = true; break end
                end
            end
        end
    end
    if hit then
        InviteUnit(src)
        inviteCooldowns[sl] = GetTime()
        if s.responseEnabled and (s.responseText or "") ~= "" then
            SendChatMessage(s.responseText, "WHISPER", nil, src)
        end
    end
end

-------------------------------------------------------------------------------
-- Event listener
-------------------------------------------------------------------------------
local aiEventFrame = CreateFrame("Frame")
aiEventFrame:RegisterEvent("CHAT_MSG_WHISPER")
aiEventFrame:RegisterEvent("CHAT_MSG_GUILD")
aiEventFrame:SetScript("OnEvent", function(self, event, msg, src)
    local s = RNToolsAutoInviteSave
    if not s or not s.isEnabled then return end
    if src == UnitName("player") then return end
    if UnitInParty(src) or UnitInRaid(src) then return end
    local listed = CheckListed(string.lower(src or ""))
    if listed == false then return end
    if event == "CHAT_MSG_WHISPER" then
        TryInvite(msg, src, listed == true)
    elseif event == "CHAT_MSG_GUILD" and s.listenGuild then
        TryInvite(msg, src, listed == true)
    end
end)

-------------------------------------------------------------------------------
-- Status function (used by overlay)
-------------------------------------------------------------------------------
function RNTools_IsAutoInviteEnabled()
    return RNToolsAutoInviteSave and RNToolsAutoInviteSave.isEnabled == true
end

-------------------------------------------------------------------------------
-- Dynamic row state
-------------------------------------------------------------------------------
local kwContent  = nil   -- AceGUI SimpleGroup holding the keyword rows
local lstContent = nil   -- AceGUI SimpleGroup holding the white/blacklist rows
local Relayout   = nil   -- re-runs the manual yOffset layout (set in CreateAutoInviteTabContent)

-------------------------------------------------------------------------------
-- Tab UI refs
-------------------------------------------------------------------------------
local tabRefs = {}

local function RebuildKeywordRows()
    if not kwContent then return end
    kwContent:ReleaseChildren()
    local s = RNToolsAutoInviteSave
    for i, kw in ipairs(s.keywords) do
        local row = AceGUI:Create("SimpleGroup")
        row:SetLayout("Flow")
        row:SetFullWidth(true)

        local idx = i

        local eb = AceGUI:Create("EditBox")
        eb:SetRelativeWidth(0.55)
        eb:DisableButton(true)
        eb:SetText(kw.keyword or "")
        eb:SetCallback("OnTextChanged", function(widget, event, text)
            if s.keywords[idx] then s.keywords[idx].keyword = text or "" end
        end)
        row:AddChild(eb)

        local cb = AceGUI:Create("CheckBox")
        cb:SetLabel("Contains")
        cb:SetRelativeWidth(0.25)
        cb:SetValue(kw.contains == true)
        cb:SetCallback("OnValueChanged", function(widget, event, value)
            if s.keywords[idx] then s.keywords[idx].contains = value end
        end)
        row:AddChild(cb)

        local del = AceGUI:Create("Button")
        del:SetText("|cffff4d4dRemove|r")
        del:SetRelativeWidth(0.20)
        del:SetCallback("OnClick", function()
            table.remove(s.keywords, idx)
            RebuildKeywordRows()
        end)
        row:AddChild(del)

        kwContent:AddChild(row)
    end
    if tabRefs.addKwBtn then tabRefs.addKwBtn:SetDisabled(#s.keywords >= 5) end
    if Relayout then Relayout() end
end

local function RebuildListRows()
    if not lstContent then return end
    lstContent:ReleaseChildren()
    local s = RNToolsAutoInviteSave
    for i, entry in ipairs(s.lists) do
        local row = AceGUI:Create("SimpleGroup")
        row:SetLayout("Flow")
        row:SetFullWidth(true)

        local idx = i

        local eb = AceGUI:Create("EditBox")
        eb:SetRelativeWidth(0.55)
        eb:DisableButton(true)
        eb:SetText(entry.playerName or "")
        eb:SetCallback("OnTextChanged", function(widget, event, text)
            if s.lists[idx] then s.lists[idx].playerName = text or "" end
        end)
        row:AddChild(eb)

        local cb = AceGUI:Create("CheckBox")
        cb:SetLabel("Whitelist")
        cb:SetRelativeWidth(0.25)
        cb:SetValue(entry.isWhitelisted == true)
        cb:SetCallback("OnValueChanged", function(widget, event, value)
            if s.lists[idx] then s.lists[idx].isWhitelisted = value end
        end)
        row:AddChild(cb)

        local del = AceGUI:Create("Button")
        del:SetText("|cffff4d4dRemove|r")
        del:SetRelativeWidth(0.20)
        del:SetCallback("OnClick", function()
            table.remove(s.lists, idx)
            RebuildListRows()
        end)
        row:AddChild(del)

        lstContent:AddChild(row)
    end
    if Relayout then Relayout() end
end

-------------------------------------------------------------------------------
-- Tab content creator
-------------------------------------------------------------------------------
function CreateAutoInviteTabContent(parent, onClose)
    RNTools_AI_EnsureSave()

    local f = CreateFrame("Frame", nil, parent)
    f:SetAllPoints(parent)
    f:Hide()
    AISetBG(f, unpack(C.bg))

    -- Manual yOffset anchoring (see CLAUDE.md "manually-anchored AceGUI
    -- containers" / Spammer.lua notes): a raw UIPanelScrollFrameTemplate
    -- ScrollFrame + fixed-width content frame, with every widget pinned via
    -- Relayout() below instead of AceGUI's "ScrollFrame"+"List" container,
    -- which proved fragile (FixScroll/LayoutFinished races).
    local CONTENT_W = 962
    local scrollFrame = CreateFrame("ScrollFrame", "RNToolsAutoInviteScrollFrame", f, "UIPanelScrollFrameTemplate")
    scrollFrame:ClearAllPoints()
    scrollFrame:SetPoint("TOP",    f, "TOP",    0, -10)
    scrollFrame:SetPoint("BOTTOM", f, "BOTTOM", 0,  36)
    scrollFrame:SetWidth(CONTENT_W)

    local content = CreateFrame("Frame", nil, scrollFrame)
    content:SetWidth(CONTENT_W)
    content:SetHeight(1)
    scrollFrame:SetScrollChild(content)

    -- Ordered list of layout items. Each item is either full-width
    -- (fixedWidth == nil, anchored TOPLEFT+TOPRIGHT of content) or a
    -- fixed-width left-anchored widget (e.g. the "+ Add ..." buttons, which
    -- shouldn't stretch across the whole tab). `height` may be a number or
    -- a function (for dynamic-height items like kwContent/lstContent, whose
    -- row count changes at runtime).
    local layoutItems = {}

    local function AddItem(widget, height, spacing, fixedWidth)
        table.insert(layoutItems, { widget = widget, height = height, spacing = spacing or 0, fixedWidth = fixedWidth })
        return widget
    end

    -- Re-anchors every layout item top-to-bottom using a running yOffset,
    -- then sets content's height to the total. Called once at construction
    -- and again whenever kwContent/lstContent's row count changes.
    Relayout = function()
        local y = 0
        for _, item in ipairs(layoutItems) do
            local widget = item.widget
            local h = type(item.height) == "function" and item.height() or item.height
            widget.frame:SetParent(content)
            widget.frame:ClearAllPoints()
            widget.frame:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -y)
            if item.fixedWidth then
                widget.frame:SetWidth(item.fixedWidth)
            else
                widget.frame:SetPoint("TOPRIGHT", content, "TOPRIGHT", 0, -y)
            end
            widget.frame:SetHeight(h)
            widget.frame:Show()
            y = y + h + item.spacing
        end
        content:SetHeight(y)
    end

    -- Title
    AddItem(AIHeading("Auto Invite"), 18, 4)

    local subLbl = AceGUI:Create("Label")
    subLbl:SetJustifyH("CENTER")
    subLbl:SetColor(0.60, 0.60, 0.60)
    subLbl:SetText("Automatically invite players who whisper a keyword")
    -- AceGUI Label only wraps/centers against the width passed to its own
    -- SetWidth (AceGUIWidget-Label OnWidthSet -> UpdateImageAnchor), not the
    -- TOPLEFT/TOPRIGHT anchors Relayout uses - call it here so the text wraps
    -- to CONTENT_W and frame.height reflects the wrapped line count.
    subLbl:SetWidth(CONTENT_W)
    AddItem(subLbl, subLbl.frame.height, 6)

    -- ── Status ───────────────────────────────────────────────────────
    AddItem(AIHeading("Status"), 18, 4)

    local enableCB = AceGUI:Create("CheckBox")
    enableCB:SetLabel("Enable Auto Invite")
    enableCB:SetValue(RNToolsAutoInviteSave.isEnabled == true)
    enableCB:SetCallback("OnValueChanged", function(widget, event, value)
        RNToolsAutoInviteSave.isEnabled = value
        if RefreshStatusOverlay then RefreshStatusOverlay() end
    end)
    AddItem(enableCB, 24, 2)
    tabRefs.enableCB = enableCB

    local guildCB = AceGUI:Create("CheckBox")
    guildCB:SetLabel("Also listen to Guild Chat")
    guildCB:SetValue(RNToolsAutoInviteSave.listenGuild == true)
    guildCB:SetCallback("OnValueChanged", function(widget, event, value)
        RNToolsAutoInviteSave.listenGuild = value
    end)
    AddItem(guildCB, 24, 6)
    tabRefs.guildCB = guildCB

    -- ── Auto Response ────────────────────────────────────────────────
    AddItem(AIHeading("Auto Response (whisper on successful invite)"), 18, 4)

    local respCB = AceGUI:Create("CheckBox")
    respCB:SetLabel("Send whisper response after inviting")
    respCB:SetValue(RNToolsAutoInviteSave.responseEnabled == true)
    respCB:SetCallback("OnValueChanged", function(widget, event, value)
        RNToolsAutoInviteSave.responseEnabled = value
    end)
    AddItem(respCB, 24, 2)
    tabRefs.respCB = respCB

    local respEB = AceGUI:Create("EditBox")
    respEB:SetLabel("Response message:")
    respEB:DisableButton(true)
    respEB:SetText(RNToolsAutoInviteSave.responseText or "")
    respEB:SetCallback("OnTextChanged", function(widget, event, text)
        RNToolsAutoInviteSave.responseText = text or ""
    end)
    AddItem(respEB, 44, 6)
    tabRefs.respEB = respEB

    -- ── Keywords ─────────────────────────────────────────────────────
    AddItem(AIHeading("Keywords"), 18, 4)

    local kwHint = AceGUI:Create("Label")
    kwHint:SetJustifyH("CENTER")
    kwHint:SetColor(0.60, 0.60, 0.60)
    kwHint:SetText("Exact: player must type exactly the keyword.  Contains: keyword appears anywhere in the message.  (max 5)")
    kwHint:SetWidth(CONTENT_W)
    AddItem(kwHint, kwHint.frame.height, 4)

    -- Row height (28px) covers a Flow row of EditBox(26, no label) +
    -- CheckBox(24) + Button(24) - tallest child (EditBox) sets the row
    -- height, plus 2px breathing room.
    local ROW_H = 28

    kwContent = AceGUI:Create("SimpleGroup")
    kwContent:SetLayout("List")
    kwContent.noAutoHeight = true
    kwContent:SetWidth(CONTENT_W)
    AddItem(kwContent, function() return #RNToolsAutoInviteSave.keywords * ROW_H end, 6)

    -- "+ Add ..." buttons are fixed-width (not SetFullWidth) per design -
    -- a button stretched across the whole 962px tab looked wrong.
    local addKwBtn = AceGUI:Create("Button")
    addKwBtn:SetText("|cff33ff99+ Add Keyword|r")
    addKwBtn:SetCallback("OnClick", function()
        if #RNToolsAutoInviteSave.keywords >= 5 then return end
        table.insert(RNToolsAutoInviteSave.keywords, { keyword = "", contains = false })
        RebuildKeywordRows()
    end)
    AddItem(addKwBtn, 24, 8, 200)
    tabRefs.addKwBtn = addKwBtn

    -- ── White / Blacklist ────────────────────────────────────────────
    AddItem(AIHeading("White / Blacklist"), 18, 4)

    local lstHint = AceGUI:Create("Label")
    lstHint:SetJustifyH("CENTER")
    lstHint:SetColor(0.60, 0.60, 0.60)
    lstHint:SetText("|cff20ff60Whitelisted|r: invited on any message (bypasses keyword check).  |cffff4444Blacklisted|r (unchecked): always ignored.")
    lstHint:SetWidth(CONTENT_W)
    AddItem(lstHint, lstHint.frame.height, 4)

    lstContent = AceGUI:Create("SimpleGroup")
    lstContent:SetLayout("List")
    lstContent.noAutoHeight = true
    lstContent:SetWidth(CONTENT_W)
    AddItem(lstContent, function() return #RNToolsAutoInviteSave.lists * ROW_H end, 6)

    local addLstBtn = AceGUI:Create("Button")
    addLstBtn:SetText("|cff33ff99+ Add Player|r")
    addLstBtn:SetCallback("OnClick", function()
        table.insert(RNToolsAutoInviteSave.lists, { playerName = "", isWhitelisted = true })
        RebuildListRows()
    end)
    AddItem(addLstBtn, 24, 4, 200)

    RebuildKeywordRows()
    RebuildListRows()
    Relayout()

    -- OnShow sync
    f:SetScript("OnShow", function()
        RNTools_AI_EnsureSave()
        tabRefs.enableCB:SetValue(RNToolsAutoInviteSave.isEnabled == true)
        tabRefs.guildCB:SetValue(RNToolsAutoInviteSave.listenGuild == true)
        tabRefs.respCB:SetValue(RNToolsAutoInviteSave.responseEnabled == true)
        tabRefs.respEB:SetText(RNToolsAutoInviteSave.responseText or "")
        RebuildKeywordRows()
        RebuildListRows()
    end)

    return f
end

-- Global toggle for Quick Access window
function RNTools_ToggleAutoInvite()
    RNToolsAutoInviteSave.isEnabled = not (RNToolsAutoInviteSave.isEnabled == true)
    if tabRefs.enableCB then tabRefs.enableCB:SetValue(RNToolsAutoInviteSave.isEnabled == true) end
    if RefreshStatusOverlay then RefreshStatusOverlay() end
end
