-- KiraUI-Plugin_AutoGossip.lua
-- Pick a known gossip option the moment the window opens.
--
-- Built for the handful of dungeon NPCs whose menu has exactly one answer you
-- ever give - the disguise NPC in instance 2813, where "I'm ready for my
-- disguise" is the only reason anyone talks to him - and where the
-- open-read-click-close round trip is pure delay.
--
-- WHY BY ID AND NOTHING ELSE
--
-- Options are matched on gossipOptionID, never on text and never on position:
--
--   * Text is unreliable twice over. Inside a key it can come back SECRET and
--     unreadable, and on a non-English client it is a different string
--     entirely. A text match would silently stop working for half the people
--     who use it.
--   * Position is worse. orderIndex 0 is whatever that NPC happens to list
--     first, and menus grow options as quests are picked up - "I'm ready for
--     my disguise" today can be "Abandon your quest?" tomorrow. Auto-clicking
--     a position means auto-clicking whatever moved into it.
--
-- The ID is stable, unique to one option on one NPC, and means an entry in
-- this list can only ever fire on the exact option it was added for.
--
-- WHAT THIS DELIBERATELY DOES NOT DO
--
-- It never passes `confirmed`. Some options raise a second "are you sure?"
-- box, and that box exists because the action behind it is one you cannot
-- take back. Answering the menu for you is a convenience; answering the
-- warning about it as well is how an addon spends someone's currency or
-- abandons someone's quest. The first click is automated, the confirmation
-- stays yours.

local addonName, ns = ...
local _G = _G

local CreateFrame = CreateFrame
local issecretvalue = issecretvalue
local pairs, ipairs, type, pcall, tonumber = pairs, ipairs, type, pcall, tonumber

ns.hasAutoGossip = true

local function secret(v) return issecretvalue and issecretvalue(v) end

----------------------------------------------------------------------
-- Settings
----------------------------------------------------------------------
-- Read off instance 2813 with /kiragossip:
--   131567  "I'm ready for my disguise"
--   131502
-- Ships ON with both seeded, because a feature that has to be configured
-- before it does anything is a feature nobody turns on.
local DEFAULTS = {
	enable = true,
	options = { 131567, 131502 },
	instanceOnly = true,  -- stay silent at ordinary world NPCs
	announce = true,
}

-- repair a saved list that an older build left as a non-table
local function Migrate(cfg)
	if type(cfg.options) ~= 'table' then cfg.options = {} end
end

local function GetCfg() return ns.ModuleCfg('autoGossip', DEFAULTS, Migrate) end
ns.AGGetCfg = GetCfg

local function Msg(text) print('|cffff7d0aKiraGossip|r ' .. text) end

----------------------------------------------------------------------
-- Where are we
--
-- An unreadable instance type counts as "yes, we might be in one". Being
-- wrong that way costs a selection you asked for at a world NPC; being wrong
-- the other way means the feature quietly does nothing in the one place it
-- was written for.
----------------------------------------------------------------------
local function InInstance()
	if not GetCfg().instanceOnly then return true end
	local f = _G.IsInInstance
	if not f then return true end
	local ok, inside = pcall(f)
	if not ok then return true end
	if secret(inside) then return true end
	return inside and true or false
end

----------------------------------------------------------------------
-- Selection
----------------------------------------------------------------------
-- Per-option cooldown. If a selection does not take - the option is greyed
-- out, a quest is missing, the server refuses - the menu re-shows with the
-- same option still on it, and without this the addon would click it forever.
local COOLDOWN = 5
local lastPick = {}

local function WantedSet(cfg)
	local set = {}
	for _, id in ipairs(cfg.options) do
		local n = tonumber(id)
		if n then set[n] = true end
	end
	return set
end

local function TrySelect()
	local cfg = GetCfg()
	if not cfg.enable then return end
	if not InInstance() then return end

	local wanted = WantedSet(cfg)
	if not next(wanted) then return end

	local CG = _G.C_GossipInfo
	if not (CG and CG.GetOptions and CG.SelectOption) then return end

	local ok, opts = pcall(CG.GetOptions)
	if not ok or type(opts) ~= 'table' then return end

	local now = _G.GetTime and _G.GetTime() or 0

	for _, o in ipairs(opts) do
		local id = o.gossipOptionID
		if type(id) == 'number' and not secret(id) and wanted[id] then
			if lastPick[id] and (now - lastPick[id]) < COOLDOWN then
				return   -- just tried this one; it did not take
			end
			lastPick[id] = now
			-- one argument only: no text, and never `confirmed`
			local okS = pcall(CG.SelectOption, id)
			if cfg.announce then
				local nm = o.name
				if secret(nm) or type(nm) ~= 'string' then nm = 'option ' .. id end
				if okS then
					Msg('selected: ' .. nm)
				else
					Msg('could not select option ' .. id)
				end
			end
			return   -- one selection per menu, deliberately
		end
	end
end

ns.AGTrySelect = TrySelect

----------------------------------------------------------------------
-- Events
----------------------------------------------------------------------
local evt = CreateFrame('Frame')
pcall(evt.RegisterEvent, evt, 'GOSSIP_SHOW')
evt:SetScript('OnEvent', function()
	-- one frame later: the option list is not necessarily populated at the
	-- instant GOSSIP_SHOW fires, and selecting from inside the event itself
	-- fights the frame that is still building
	_G.C_Timer.After(0, function() pcall(TrySelect) end)
end)

----------------------------------------------------------------------
-- Diagnostic
----------------------------------------------------------------------
_G.SLASH_KIRAAUTOGOSSIP1 = '/kiraautogossip'
_G.SlashCmdList.KIRAAUTOGOSSIP = function()
	local cfg = GetCfg()
	Msg(('enabled: %s | only inside instances: %s'):format(
		cfg.enable and 'yes' or 'NO', cfg.instanceOnly and 'yes' or 'no'))
	if #cfg.options == 0 then
		Msg('no options configured. Talk to the NPC, run /kiragossip, and add the ID it prints.')
		return
	end
	Msg('auto-selecting these gossipOptionIDs:')
	for _, id in ipairs(cfg.options) do
		Msg('  ' .. tostring(id))
	end
end
