-- ============================================================================
-- Vamoose's Companion - Aura Lighting Service
-- Smooth portrait lighting transitions driven by player activity events
-- ============================================================================

VC = VC or {}

VC.AuraLighting = {}
local AL = VC.AuraLighting

-- ============================================================================
-- LIGHTING PRESETS
-- ============================================================================

-- Diffuse mirrors ambient for full color coverage (diffuse=specular, ambient=texture tint)
-- desaturation strips model color so tint dominates
local PRESETS = {
    default   = { diffuse = {0.8,  0.8,  0.8},  ambient = {0.6,  0.6,  0.6},  desat = 0   },
    swimming  = { diffuse = {0.15, 0.35, 1.4},  ambient = {0.15, 0.35, 1.4},  desat = 0.3 },
    lowhealth = { diffuse = {1.0,  0.1,  0.1},  ambient = {1.0,  0.1,  0.1},  desat = 0.4 },
    mounted   = { diffuse = {0.75, 0.7,  0.55}, ambient = {0.75, 0.7,  0.55}, desat = 0   },
    flying    = { diffuse = {0.35, 0.5,  1.2},  ambient = {0.35, 0.5,  1.2},  desat = 0.15},
    resting   = { diffuse = {0.8,  0.55, 0.25}, ambient = {0.8,  0.55, 0.25}, desat = 0   },
    dead      = { diffuse = {0.4,  0.4,  0.4},  ambient = {0.3,  0.3,  0.3},  desat = 1.0 },
}

-- ============================================================================
-- STATE
-- ============================================================================

local _d = PRESETS.default
local cur = { dr = _d.diffuse[1], dg = _d.diffuse[2], db = _d.diffuse[3],
              ar = _d.ambient[1], ag = _d.ambient[2], ab = _d.ambient[3] }
local tgt = { dr = _d.diffuse[1], dg = _d.diffuse[2], db = _d.diffuse[3],
              ar = _d.ambient[1], ag = _d.ambient[2], ab = _d.ambient[3] }

local lerpTicker  = nil
local pulseTicker = nil
local activeAura  = nil

-- ============================================================================
-- HELPERS
-- ============================================================================

local LERP_FACTOR = 1 / 16
local SNAP_THRESH = 0.005

local function getScene()
    return VC.Portrait and VC.Portrait.frame and VC.Portrait.frame.scene
end

local function applyToScene(scene)
    scene:SetLightDiffuseColor(cur.dr, cur.dg, cur.db)
    scene:SetLightAmbientColor(cur.ar, cur.ag, cur.ab)
end

local function applyExtras(scene, preset)
    if scene.actor and preset.desat then
        scene.actor:SetDesaturation(preset.desat)
    end
end

local function setTarget(preset)
    tgt.dr, tgt.dg, tgt.db = preset.diffuse[1], preset.diffuse[2], preset.diffuse[3]
    tgt.ar, tgt.ag, tgt.ab = preset.ambient[1], preset.ambient[2], preset.ambient[3]
end

local function stopLerp()
    if lerpTicker then lerpTicker:Cancel(); lerpTicker = nil end
end

local function stopPulse()
    if pulseTicker then pulseTicker:Cancel(); pulseTicker = nil end
end

local function startLerp()
    stopLerp()
    lerpTicker = C_Timer.NewTicker(0.03, function()
        local scene = getScene()
        if not scene then stopLerp(); return end

        cur.dr = cur.dr + (tgt.dr - cur.dr) * LERP_FACTOR
        cur.dg = cur.dg + (tgt.dg - cur.dg) * LERP_FACTOR
        cur.db = cur.db + (tgt.db - cur.db) * LERP_FACTOR
        cur.ar = cur.ar + (tgt.ar - cur.ar) * LERP_FACTOR
        cur.ag = cur.ag + (tgt.ag - cur.ag) * LERP_FACTOR
        cur.ab = cur.ab + (tgt.ab - cur.ab) * LERP_FACTOR

        if math.abs(cur.dr - tgt.dr) < SNAP_THRESH
        and math.abs(cur.dg - tgt.dg) < SNAP_THRESH
        and math.abs(cur.db - tgt.db) < SNAP_THRESH
        and math.abs(cur.ar - tgt.ar) < SNAP_THRESH
        and math.abs(cur.ag - tgt.ag) < SNAP_THRESH
        and math.abs(cur.ab - tgt.ab) < SNAP_THRESH then
            cur.dr, cur.dg, cur.db = tgt.dr, tgt.dg, tgt.db
            cur.ar, cur.ag, cur.ab = tgt.ar, tgt.ag, tgt.ab
            stopLerp()
        end

        applyToScene(scene)
    end)
end

-- ============================================================================
-- PUBLIC API
-- ============================================================================

function AL:GetActiveAura() return activeAura end

function AL:SetAura(auraName)
    local preset = PRESETS[auraName]
    if not preset then return end
    stopPulse()
    activeAura = auraName ~= "default" and auraName or nil
    setTarget(preset)
    local scene = getScene()
    if scene then applyExtras(scene, preset) end
    startLerp()
end

function AL:ClearAura() AL:SetAura("default") end

function AL:PulseAura(auraName, duration, cycles)
    local preset = PRESETS[auraName]
    if not preset then return end
    stopPulse()
    activeAura = auraName

    local halfCycle  = duration / (cycles * 2)
    local totalTicks = cycles * 2
    local tick       = 0
    local toPreset   = true

    setTarget(preset)
    startLerp()

    pulseTicker = C_Timer.NewTicker(halfCycle, function()
        tick = tick + 1
        toPreset = not toPreset
        setTarget(toPreset and preset or PRESETS.default)
        startLerp()

        if tick >= totalTicks then
            stopPulse()
            AL:SetAura("default")
        end
    end)
end

-- ============================================================================
-- INITIALIZE
-- ============================================================================

function AL:Initialize()
    VC.EventBus:Register("VC_ACTIVITY_CHANGED", function(payload)
        local activity = payload and payload.activity
        if not activity or not PRESETS[activity] then return end

        if payload.active then
            AL:SetAura(activity)
        elseif activeAura == activity then
            AL:ClearAura()
        end
    end)
end
