--core/profile.lua, profile access helpers (backend).
--the default profile schema lives in init.lua (it must exist before CreateNewAddOn
--runs there). this module provides typed read/write access to the live profile.

local _ = nil
local addonName, private = ...

local detailsFramework = DetailsFramework

private.Core = private.Core or {}

--constants
--the role-based profiles created by default (one per group role); all three start from the default
--settings the first time DF loads them.
local DEFAULT_ROLE_PROFILES = {"tank", "damager", "healer"}

--maps a WoW group-role token (from GetSpecializationRole) to the matching default profile name.
local ROLE_TO_PROFILE = {
	TANK = "tank",
	DAMAGER = "damager",
	HEALER = "healer",
}

---@class FPSPP_Profile
local Profile = {}
private.Core.Profile = Profile

--frame used only to receive PLAYER_SPECIALIZATION_CHANGED so the active profile can follow the spec role.
local specializationEventFrame = CreateFrame("Frame")

--returns the live profile table.
---@return profile
function Profile:Get()
	return private.addon.profile
end

--reads a value from the profile by dotted path, e.g. "ui.disable_minimap".
---@param path string
---@return any
function Profile:GetValue(path)
	return detailsFramework.table.getfrompath(self:Get(), path)
end

--reads a boolean profile toggle. only a real boolean true enables behavior; this keeps stale
--saved values such as 0 or "false" from acting enabled while the checkbox appears disabled.
---@param path string
---@return boolean
function Profile:GetBool(path)
	return self:GetValue(path) == true
end

--writes a value to the profile by dotted path.
---@param path string
---@param value any
function Profile:SetValue(path, value)
	detailsFramework.table.setfrompath(self:Get(), path, value)
end

--writes a profile toggle as a real boolean.
---@param path string
---@param value any
function Profile:SetBool(path, value)
	self:SetValue(path, value == true)
end

--switches to another profile by name, creating it if missing; fires OnProfileChanged.
---@param profileName string
function Profile:Switch(profileName)
	detailsFramework.SavedVars.SetProfile(private.addon, profileName)
end

--returns the name of the profile the current character is using.
---@return string
function Profile:GetCurrentName()
	return detailsFramework.SavedVars.GetCurrentProfileName(private.addon)
end

--returns a sorted list of every saved profile name (used to populate the profile-selection dropdown).
---@return string[]
function Profile:GetNames()
	local names = {}
	local savedVariables = detailsFramework.SavedVars.GetSavedVariables(private.addon)
	for profileName in pairs(savedVariables.profiles) do
		names[#names + 1] = profileName
	end

	table.sort(names)
	return names
end

--ensures the default role profiles exist in the saved variables so each character can pick one
--(e.g. a damager spec uses the ship frames while a healer spec keeps a different raid addon). a
--freshly created entry is an empty table; DF fills it with the default settings the first time it is
--loaded, and it shows up in the profile-management dropdown. called once from OnLoad.
function Profile:EnsureDefaultProfiles()
	local savedVariables = detailsFramework.SavedVars.GetSavedVariables(private.addon)
	for index, profileName in ipairs(DEFAULT_ROLE_PROFILES) do
		if not savedVariables.profiles[profileName] then
			savedVariables.profiles[profileName] = {}
		end
	end
end

--account-wide (NOT per-profile): whether to auto-switch profiles by spec role. stored in the saved-
--variables root so it stays consistent regardless of the active profile (keeping it inside a profile
--would be circular, since switching profiles would change the flag). defaults to true when unset.
---@return boolean
function Profile:IsAutoSwitchByRole()
	local value = detailsFramework.SavedVars.GetSavedVariables(private.addon).auto_switch_by_role
	--nil means "never set" -> default on; this is the documented default, not a hidden bug.
	if value == nil then
		return true
	end

	return value
end

--sets the account-wide auto-switch-by-role flag.
---@param value boolean
function Profile:SetAutoSwitchByRole(value)
	detailsFramework.SavedVars.GetSavedVariables(private.addon).auto_switch_by_role = value
end

--returns the default profile name matching the player's current spec role, or nil if it can't be
--resolved (no spec yet at low level, or a role with no mapping).
local getRoleProfileName = function()
	local specialization = detailsFramework.GetSpecialization()
	if not specialization then
		return nil
	end

	local role = detailsFramework.GetSpecializationRole(specialization)
	return ROLE_TO_PROFILE[role]
end

--switches this character to the profile matching its current spec role -- but only when auto-switch
--is enabled, a role profile resolves, and it isn't already the active profile. switching never does
--secure work directly (the optimizer defers that), and spec changes can't happen in combat anyway.
function Profile:ApplyRoleProfile()
	if not self:IsAutoSwitchByRole() then
		return
	end

	local roleProfileName = getRoleProfileName()
	if not roleProfileName then
		return
	end

	--already on the right profile -> nothing to do (avoids a redundant switch on every spec event).
	if detailsFramework.SavedVars.GetCurrentProfileName(private.addon) == roleProfileName then
		return
	end

	self:Switch(roleProfileName)
end

--registers the spec-change handler and applies the role profile once for the spec the player logged
--in with. called once from OnInit.
function Profile:Initialize()
	specializationEventFrame:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED")
	--keep the handler short: re-apply the role profile (it no-ops when already correct).
	specializationEventFrame:SetScript("OnEvent", function()
		Profile:ApplyRoleProfile()
	end)

	self:ApplyRoleProfile()
end
