-- KiraUI-Plugin_EUINameplates.lua
-- EllesmereUI Nameplates tweaks. Active only when EllesmereUINameplates loaded.
--
-- EllesmereUINameplates exposes its namespace as _G.EllesmereNameplates_NS
-- (so we can reach ns.plates) and a global refresh _G._ENP_RefreshAllSettings.
--
-- Feature 1: show nameplate health as "# (%)" - rewrite "X | Y" to "X (Y)".
-- Feature 2: enemy name transparency - set plate.name region alpha.
-- Feature 3: custom target arrows ("Arrow 10") - draw our own EthricArrow10
--            texture flanking the target plate's health bar.
--            Set EllesmereUI's own Target Arrows to "None" to avoid doubles.

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

local ENP = _G.EllesmereNameplates_NS
local EUI = _G.EllesmereUI
local hasNP = (ENP ~= nil) and (EUI ~= nil)

ns.hasEUINP = hasNP
if not hasNP then return end

local hooksecurefunc = hooksecurefunc
local InCombatLockdown = InCombatLockdown
local CreateFrame = CreateFrame
local C_Timer = C_Timer
local pairs = pairs
local UnitExists = UnitExists
local UnitIsUnit = UnitIsUnit
local issecretvalue = issecretvalue

local ARROW_PATH = 'Interface\\AddOns\\KiraUI-Plugin\\Media\\EthricArrow10.tga'

----------------------------------------------------------------------
-- Settings (lazy-created on the shared KiraUIChangesDB)
----------------------------------------------------------------------
-- Defaults are file-level constants, not table literals built inside the
-- getters. GetHealthCfg runs inside the health-text hook - once per text
-- update, per visible nameplate - so a literal there would allocate a throwaway
-- table on the addon's single busiest path.
local HEALTH_DEFAULTS = { enable = false }
local NAME_DEFAULTS   = { enable = false, alpha = 50 }
local ARROW_DEFAULTS  = { enable = false, size = 24, x = -24, y = 0 }

local function GetHealthCfg() return ns.ModuleCfg('euiNpHealth', HEALTH_DEFAULTS) end
local function GetNameCfg()   return ns.ModuleCfg('euiNpName',   NAME_DEFAULTS) end
local function GetArrowCfg()  return ns.ModuleCfg('euiNpArrow',  ARROW_DEFAULTS) end

ns.EUINPGetHealthCfg = GetHealthCfg
ns.EUINPGetNameCfg   = GetNameCfg
ns.EUINPGetArrowCfg  = GetArrowCfg

----------------------------------------------------------------------
-- Refresh: re-apply all EllesmereUI nameplate settings (combat-deferred).
----------------------------------------------------------------------
local refreshPending = false
local refreshFrame = CreateFrame('Frame')
refreshFrame:SetScript('OnEvent', function(self)
	self:UnregisterEvent('PLAYER_REGEN_ENABLED')
	refreshPending = false
	local reload = _G._ENP_RefreshAllSettings
	if type(reload) == 'function' then pcall(reload) end
end)

local function RefreshNP()
	local reload = _G._ENP_RefreshAllSettings
	if type(reload) ~= 'function' then return end
	if InCombatLockdown() then
		if not refreshPending then
			refreshPending = true
			refreshFrame:RegisterEvent('PLAYER_REGEN_ENABLED')
		end
	else
		pcall(reload)
	end
end
ns.EUINPRefresh = RefreshNP

----------------------------------------------------------------------
-- Feature 2: enemy-name alpha. ns.plates holds the hostile plates.
----------------------------------------------------------------------
local function NameAlpha()
	local cfg = GetNameCfg()
	if cfg.enable then return (cfg.alpha or 50) / 100 end
	return 1
end

local function ApplyNameAlphaToPlate(plate)
	if plate and plate.name and plate.name.SetAlpha then
		plate.name:SetAlpha(NameAlpha())
	end
end

local function ApplyNameAlphaAll()
	if not ENP.plates then return end
	for _, plate in pairs(ENP.plates) do
		ApplyNameAlphaToPlate(plate)
	end
end
ns.EUINPApplyNameAlpha = ApplyNameAlphaAll

----------------------------------------------------------------------
-- Feature 1: hook a plate's combined health text so "X | Y" becomes "X (Y)".
-- The args may be secret (Midnight) - they are only ever passed straight back
-- into SetFormattedText (a C function that accepts secrets), never compared.
----------------------------------------------------------------------
local function HookHealthText(plate)
	local fs = plate and plate.hpText
	if not (fs and fs.SetFormattedText) or fs.__kiraNPHealthHook then return end
	fs.__kiraNPHealthHook = true
	hooksecurefunc(fs, 'SetFormattedText', function(self, fmt, a, b)
		if self.__kiraNPGuard then return end
		if fmt == '%s | %s' and GetHealthCfg().enable then
			self.__kiraNPGuard = true
			self:SetFormattedText('%s (%s)', a, b)
			self.__kiraNPGuard = false
		end
	end)
end

----------------------------------------------------------------------
-- Feature 3: custom target arrows. Two EthricArrow10 textures flank the
-- target plate's health bar; the right one is horizontally flipped via
-- texcoords.
----------------------------------------------------------------------
local arrowFrame, leftTex, rightTex

local function EnsureArrows()
	if arrowFrame then return end
	arrowFrame = CreateFrame('Frame', nil, _G.UIParent)
	arrowFrame:SetSize(1, 1)
	arrowFrame:Hide()
	leftTex = arrowFrame:CreateTexture(nil, 'OVERLAY')
	leftTex:SetTexture(ARROW_PATH)
	leftTex:SetTexCoord(0, 1, 0, 1)       -- left arrow: as-is (points toward bar)
	rightTex = arrowFrame:CreateTexture(nil, 'OVERLAY')
	rightTex:SetTexture(ARROW_PATH)
	rightTex:SetTexCoord(1, 0, 0, 1)      -- right arrow: horizontally flipped
end

local function FindTargetPlate()
	if not ENP.plates or not UnitExists('target') then return nil end
	for _, p in pairs(ENP.plates) do
		-- UnitIsUnit can return a SECRET boolean for enemy units in restricted
		-- content; secrecy check FIRST, never truthiness-test the raw result
		if p.unit then
			local same = UnitIsUnit(p.unit, 'target')
			if not (issecretvalue and issecretvalue(same)) and same then return p end
		end
	end
end

local function AttachArrows()
	EnsureArrows()
	local cfg = GetArrowCfg()
	if not cfg.enable then arrowFrame:Hide() return end
	local plate = FindTargetPlate()
	if not (plate and plate.health) then arrowFrame:Hide() return end

	arrowFrame:SetParent(plate)
	arrowFrame:ClearAllPoints()
	arrowFrame:SetAllPoints(plate.health)
	arrowFrame:SetFrameLevel((plate.health:GetFrameLevel() or 1) + 6)

	local sz = cfg.size or 16
	leftTex:SetSize(sz, sz)
	rightTex:SetSize(sz, sz)
	leftTex:ClearAllPoints()
	leftTex:SetPoint('RIGHT', plate.health, 'LEFT', -(cfg.x or 2), cfg.y or 0)
	rightTex:ClearAllPoints()
	rightTex:SetPoint('LEFT', plate.health, 'RIGHT', (cfg.x or 2), cfg.y or 0)
	arrowFrame:Show()
end
ns.EUINPApplyArrow = AttachArrows

----------------------------------------------------------------------
-- Catch plates as they appear (name alpha + health hook), and keep the target
-- arrows attached to the current target.
----------------------------------------------------------------------
local function OnPlateAdded(unit)
	local plate = ENP.plates and ENP.plates[unit]
	if not plate then return end
	ApplyNameAlphaToPlate(plate)
	HookHealthText(plate)
end

local watcher = CreateFrame('Frame')
watcher:RegisterEvent('NAME_PLATE_UNIT_ADDED')
watcher:RegisterEvent('NAME_PLATE_UNIT_REMOVED')
watcher:RegisterEvent('PLAYER_TARGET_CHANGED')
watcher:SetScript('OnEvent', function(_, event, unit)
	if event == 'NAME_PLATE_UNIT_ADDED' then
		OnPlateAdded(unit)
		C_Timer.After(0, function() OnPlateAdded(unit) end)
	end
	AttachArrows()
	C_Timer.After(0, AttachArrows)
end)

-- Apply to plates that already exist when we load (e.g. /reload mid-combat).
C_Timer.After(0, function()
	if not ENP.plates then return end
	for _, plate in pairs(ENP.plates) do
		ApplyNameAlphaToPlate(plate)
		HookHealthText(plate)
	end
	AttachArrows()
end)
