-- @author DoomTero 2026

require "ZomboRut/ZomboRutScene"
require "ZomboRut/ZR_SoundProfiles"

local ZomboRutScene   = require "ZomboRut/ZomboRutScene"
local ZR_SoundProfiles = require "ZomboRut/ZR_SoundProfiles"

local DEBUG = false
local function log(fmt, ...)
	if DEBUG then print(string.format("[ZR Sounds] " .. fmt, ...)) end
end

local active = {}
local tickCounter = 0

local function modEnabled()
	if not SandboxVars or not SandboxVars.ZomboRut then return true end
	return SandboxVars.ZomboRut.enableMod ~= false
end

local function pickFrom(pool)
	if not pool or #pool == 0 then return nil end
	return pool[ZombRand(#pool) + 1]
end

local function getStage(player)
	local sgan = player and player.getVariableString
		and player:getVariableString("sharedGrappleAnimNode") or ""
	if sgan == "" then return nil end
	if string.find(sgan, "Outro") then return "outro" end
	if string.find(sgan, "Main")  then return "main"  end
	if string.find(sgan, "Intro") then return "intro" end
	return nil
end

local function resolveMainPhaseIdx(mainStage, elapsed)
	if not mainStage.phases then return nil end
	local cumulative = 0
	for i, ph in ipairs(mainStage.phases) do
		if not ph.duration then return i end
		cumulative = cumulative + ph.duration
		if elapsed < cumulative then return i end
	end
	return #mainStage.phases
end

local function getActivePool(profile, st)
	local stageDef = profile.stages and profile.stages[st.stage]
	if not stageDef then return nil end

	if st.stage == "main" and stageDef.phases then
		local idx = resolveMainPhaseIdx(stageDef, tickCounter - (st.stageEnteredTick or tickCounter))
		if idx ~= st.phaseIdx then
			st.phaseIdx = idx
			st.phaseEnteredTick = tickCounter
			log("main phase → %d", idx)
		end
		local ph = stageDef.phases[idx]
		return ph and ph.pool or nil
	end

	return stageDef.pool
end

local function getPulseTicks(profile, stageName)
	local stageDef = profile.stages and profile.stages[stageName]
	if stageDef and stageDef.pulseTicks then return stageDef.pulseTicks end
	return profile.pulseTicks or 150
end

local function playVoice(player, suffix)
	if not suffix then return end
	pcall(function()
		local st = active[player]
		if st and st.lastSuffix and player.stopPlayerVoiceSound then
			player:stopPlayerVoiceSound(st.lastSuffix)
		end
		if player.playerVoiceSound then
			player:playerVoiceSound(suffix)
		end
		if st then st.lastSuffix = suffix end
	end)
end

local function resolveProfileForScene(sceneActive)
	local cfg = sceneActive and sceneActive.cfg
	local requested = cfg and cfg.soundProfile or "default"
	local profile, resolvedName = ZR_SoundProfiles.resolve(requested)
	return profile, resolvedName
end

ZomboRutScene.registerSceneStartCallback(function(player, sceneActive)
	if not modEnabled() then return end
	if not player then return end

	local profile, profileName = resolveProfileForScene(sceneActive)
	if not profile then
		log("scene-start: no profile resolvable, scene runs silent")
		return
	end

	active[player] = {
		profile          = profile,
		profileName      = profileName,
		stage            = "intro",
		stageEnteredTick = tickCounter,
		phaseIdx         = nil,
		phaseEnteredTick = tickCounter,
		nextPulseTick    = tickCounter + getPulseTicks(profile, "intro"),
		lastSuffix       = nil,
	}

	local startSuffix = profile.startSuffix
	if not startSuffix then
		local introStage = profile.stages and profile.stages.intro
		startSuffix = pickFrom(introStage and introStage.pool)
	end
	playVoice(player, startSuffix)
	log("scene-start vocal: %s (profile=%s, stage=intro)",
		tostring(startSuffix), profileName)
end)

ZomboRutScene.registerSceneEndCallback(function(player, _sceneActive, result)
	if not modEnabled() then return end
	if not player then return end

	local st = active[player]
	if st and result == "Released" then
		local endSuffix = st.profile.endSuffix
		if not endSuffix then
			local outroStage = st.profile.stages and st.profile.stages.outro
			endSuffix = pickFrom(outroStage and outroStage.pool)
		end
		playVoice(player, endSuffix)
		log("scene-end vocal: %s (profile=%s, result=Released)",
			tostring(endSuffix), st.profileName)
	end

	active[player] = nil
end)

local function onPlayerUpdate(player)
	tickCounter = tickCounter + 1

	local st = active[player]
	if not st then return end

	if not ZomboRutScene.isPlayerInScene(player) then
		active[player] = nil
		return
	end

	local detected = getStage(player)
	if detected and detected ~= st.stage then
		st.stage            = detected
		st.stageEnteredTick = tickCounter
		st.phaseIdx         = nil
		st.phaseEnteredTick = tickCounter
		log("stage transition → %s", detected)
		st.nextPulseTick = tickCounter + getPulseTicks(st.profile, st.stage)
	end

	if tickCounter >= st.nextPulseTick then
		local pool = getActivePool(st.profile, st)
		local suffix = pickFrom(pool)
		playVoice(player, suffix)
		st.nextPulseTick = tickCounter + getPulseTicks(st.profile, st.stage)
	end
end

Events.OnPlayerUpdate.Add(onPlayerUpdate)

log("ZR_SceneSounds loaded; profiles available: %s",
	table.concat(ZR_SoundProfiles.list(), ", "))
