local addonName, ns = ...

local SlickUI = {}
ns.SlickUI = SlickUI

SlickUI.Name = addonName
SlickUI.Version = C_AddOns.GetAddOnMetadata(addonName, "Version") or "0.1.0"
SlickUI.Modules = {}
SlickUI.ModuleOrder = {}
SlickUI.UnitFrames = {}

local function CreateDisplayDefaults(isPlayer)
	if isPlayer then
		return {
			topLeft = "level", topCenter = "name", topRight = "healthPercent",
			middleLeft = "healthCurrent", middleCenter = "none", middleRight = "healthMissing",
			bottomLeft = "status", bottomCenter = "powerValues", bottomRight = "powerPercent",
		}
	end
	return {
		topLeft = "name", topCenter = "none", topRight = "healthPercent",
		middleLeft = "none", middleCenter = "none", middleRight = "none",
		bottomLeft = "level", bottomCenter = "none", bottomRight = "status",
	}
end

local function CreateTargetDisplayDefaults()
	local defaults = CreateDisplayDefaults(false)
	defaults.middleCenter = "classification"
	return defaults
end

local function CreateDisplayFontDefaults(isPlayer)
	return {
		topLeft = 9, topCenter = isPlayer and 10 or 9, topRight = 9,
		middleLeft = 9, middleCenter = 9, middleRight = 9,
		bottomLeft = 9, bottomCenter = 9, bottomRight = 9,
	}
end

local function CreateAuraDefaults(unit)
	local compact = unit == "pet" or unit == "targettarget"
	return {
		buffs = {
			enabled = unit ~= "targettarget" and unit ~= "boss",
			knownSpellsOnly = false,
			includeConsumables = true,
			position = "ABOVE",
			iconSize = compact and 18 or 24,
			spacing = 3,
			maxIcons = compact and 5 or 8,
		},
		debuffs = {
			enabled = unit ~= "targettarget" and unit ~= "boss",
			position = unit == "player" and "ABOVE" or "BELOW",
			iconSize = compact and 18 or 24,
			spacing = 3,
			maxIcons = compact and 5 or 8,
		},
	}
end

SlickUI.Defaults = {
	locked = true,
	editMode = {
		snapToGrid = true,
		snapToFrames = true,
		gridSize = 20,
	},
	externalFrames = {
		quickMoveEnabled = false,
		positions = {},
	},
	showCastbars = true,
	scale = 1,
	optionsPage = "general",
	errorCatcher = {
		enabled = true,
		sound = true,
		unread = 0,
		history = {},
	},
	alerts = {
		groundHazard = {
			enabled = true,
			sound = true,
		},
		cleanse = {
			enabled = true,
			sound = true,
			onlyWhenReady = true,
		},
		cooldowns = {
			enabled = true,
			sound = true,
			duration = 3.5,
			spellsBySpec = {},
			specMigrationComplete = false,
		},
	},
	queueStatus = {
		enabled = true,
		position = { x = 0, y = -170 },
		proposalID = nil,
		proposalExpiresAt = nil,
	},
	systemFonts = {
		enabled = true,
		general = true,
		chat = true,
		tooltipsAndQuests = true,
		numbersAndCombat = true,
		sizeOffset = 0,
	},
	goldLedger = {
		enabled = true,
		showTracker = true,
		viewMode = "transactions",
		characterScope = "current",
		maxEntries = 1000,
		entries = {},
		trackerPosition = { x = 0, y = 180 },
	},
	vendorAutomation = {
		sellGreys = false,
		autoRepair = false,
		repairFunds = "personal",
	},
	xpTracker = {
		enabled = true,
		windowPosition = { x = 0, y = 180 },
	},
	actionBars = {
		enabled = true,
		visibilityInitialized = false,
		hideHotkeys = false,
		hideMacroNames = true,
		hideCounts = false,
		selectedBar = "main",
		bars = {
			main = { shown = true, buttonSize = 38, spacing = 4, buttonsPerRow = 12, scale = 1, opacity = 1 },
			bottomLeft = { shown = true, buttonSize = 38, spacing = 4, buttonsPerRow = 12, scale = 1, opacity = 1 },
			bottomRight = { shown = true, buttonSize = 38, spacing = 4, buttonsPerRow = 12, scale = 1, opacity = 1 },
			right = { shown = true, buttonSize = 38, spacing = 4, buttonsPerRow = 1, scale = 1, opacity = 1 },
			left = { shown = true, buttonSize = 38, spacing = 4, buttonsPerRow = 1, scale = 1, opacity = 1 },
			bar5 = { shown = true, buttonSize = 38, spacing = 4, buttonsPerRow = 12, scale = 1, opacity = 1 },
			bar6 = { shown = true, buttonSize = 38, spacing = 4, buttonsPerRow = 12, scale = 1, opacity = 1 },
			bar7 = { shown = true, buttonSize = 38, spacing = 4, buttonsPerRow = 12, scale = 1, opacity = 1 },
			pet = { shown = true, buttonSize = 32, spacing = 4, buttonsPerRow = 10, scale = 1, opacity = 1 },
		},
	},
	informationBars = {
		adjustScreen = true,
		snap = 20,
		bars = {
			top1 = { enabled = true, locked = true, height = 26, opacity = 0.88 },
			top2 = { enabled = false, locked = true, height = 24, opacity = 0.88 },
			bottom1 = { enabled = false, locked = true, height = 26, opacity = 0.88 },
			bottom2 = { enabled = false, locked = true, height = 24, opacity = 0.88 },
		},
		blocks = {
			location = { enabled = true, bar = "top1", x = 20 },
			xp = { enabled = true, bar = "top1", x = 200 },
			gold = { enabled = true, bar = "top1", x = 400 },
			bags = { enabled = true, bar = "top1", x = 540 },
			durability = { enabled = true, bar = "top1", x = 660 },
			performance = { enabled = true, bar = "top1", x = 820 },
			clock = { enabled = true, bar = "top1", x = 1020 },
			memory = { enabled = false, bar = "top1", x = 1180 },
			speed = { enabled = false, bar = "top1", x = 1320 },
			windowMove = { enabled = false, bar = "top1", x = 1460 },
		},
	},
	unitFrames = {
		classColors = true,
		personalSpacing = 40,
		rowSpacing = 18,
		frameDisplays = {
			player = CreateDisplayDefaults(true),
			target = CreateTargetDisplayDefaults(),
			focus = CreateDisplayDefaults(false),
			pet = CreateDisplayDefaults(false),
			targettarget = CreateDisplayDefaults(false),
			boss = CreateDisplayDefaults(false),
			group = CreateDisplayDefaults(false),
		},
		frameDisplayFontSizes = {
			player = CreateDisplayFontDefaults(true),
			target = CreateDisplayFontDefaults(false),
			focus = CreateDisplayFontDefaults(false),
			pet = CreateDisplayFontDefaults(false),
			targettarget = CreateDisplayFontDefaults(false),
			boss = CreateDisplayFontDefaults(false),
			group = CreateDisplayFontDefaults(false),
		},
		auras = {
			player = CreateAuraDefaults("player"),
			target = CreateAuraDefaults("target"),
			focus = CreateAuraDefaults("focus"),
			pet = CreateAuraDefaults("pet"),
			targettarget = CreateAuraDefaults("targettarget"),
			boss = CreateAuraDefaults("boss"),
		},
		frames = {
			player = { enabled = true, width = 240, height = 48, scale = 1, powerHeight = 9 },
			target = { enabled = true, width = 240, height = 48, scale = 1, powerHeight = 9 },
			focus = { enabled = true, width = 190, height = 36, scale = 1, powerHeight = 6 },
			pet = { enabled = true, width = 110, height = 28, scale = 1, powerHeight = 4 },
			targettarget = { enabled = true, width = 110, height = 28, scale = 1, powerHeight = 4 },
			boss = { enabled = true, width = 190, height = 36, scale = 1, powerHeight = 6, spacing = 8 },
		},
		group = {
			partyEnabled = true,
			raidEnabled = true,
			hideBlizzardFrames = true,
			mainTankEnabled = true,
			mainAssistEnabled = true,
			showIncomingHealing = true,
			showAbsorbShields = true,
			showHealAbsorbs = true,
			capOverhealing = true,
			showDispellableDebuffs = true,
			adaptive = true,
			vertical = false,
			showSolo = false,
			anchor = "CENTER",
			classColors = true,
			width = 120,
			height = 30,
			powerHeight = 4,
			spacing = 4,
			membersPerRow = 8,
			minimumScale = 0.58,
			previewCount = 5,
		},
	},
	nameplates = {
		enabled = true,
		showEnemies = true,
		showFriendlyPlayers = false,
		showFriendlyNPCs = false,
		width = 150,
		height = 16,
		scale = 1,
		fontSize = 11,
		showCastbar = true,
		castbarHeight = 12,
		showTargetHighlight = true,
		enlargeTarget = true,
		targetScale = 1.2,
		showThreat = true,
		showClassification = true,
		showQuest = true,
		stackNameplates = true,
		display = {
			left = "classification",
			center = "none",
			right = "healthPercent",
		},
		horizontalSpacing = 0.8,
		verticalSpacing = 1.1,
	},
	minimap = {
		hide = false,
		minimapPos = 220,
	},
	positions = {
		player = { x = -170, y = -205 },
		target = { x = 170, y = -205 },
		focus = { x = 170, y = -275 },
		pet = { x = -235, y = -258 },
		targettarget = { x = 235, y = -258 },
		boss = { x = 500, y = 120 },
		group = { x = 0, y = -225 },
		mainTank = { x = -520, y = -225 },
		mainAssist = { x = 520, y = -225 },
		actionBarMain = { x = 0, y = -310 },
		actionBarBottomLeft = { x = 0, y = -354 },
		actionBarBottomRight = { x = 0, y = -398 },
		actionBarRight = { x = 760, y = -30 },
		actionBarLeft = { x = 716, y = -30 },
		actionBar5 = { x = 0, y = -442 },
		actionBar6 = { x = 0, y = -486 },
		actionBar7 = { x = 0, y = -530 },
		actionBarPet = { x = 0, y = -266 },
	},
}

local function CopyDefaults(source, destination)
	for key, value in pairs(source) do
		if type(value) == "table" then
			if type(destination[key]) ~= "table" then
				destination[key] = {}
			end

			CopyDefaults(value, destination[key])
		elseif destination[key] == nil then
			destination[key] = value
		end
	end
end

function SlickUI:Print(message)
	DEFAULT_CHAT_FRAME:AddMessage("|cffffc44dSlickUI:|r " .. tostring(message))
end

function SlickUI:IsAvailableOutOfCombat(actionName)
	if InCombatLockdown() then
		self:Print((actionName or "That action") .. " is unavailable during combat.")
		return false
	end

	return true
end

function SlickUI:InitializeDatabase()
	if type(SlickUIDB) ~= "table" then
		SlickUIDB = {}
	end

	local unitFrameDB = type(SlickUIDB.unitFrames) == "table" and SlickUIDB.unitFrames or nil
	local migratePlayerDisplay = unitFrameDB and type(unitFrameDB.frameDisplays) ~= "table"
	local legacyDisplay = migratePlayerDisplay and unitFrameDB.playerDisplay or nil
	local legacyFontSizes = migratePlayerDisplay and unitFrameDB.playerDisplayFontSizes or nil
	local legacyAuraFilters = {}
	if unitFrameDB and type(unitFrameDB.auras) == "table" then
		for unit, auraSettings in pairs(unitFrameDB.auras) do
			local buffs = type(auraSettings) == "table" and auraSettings.buffs or nil
			if type(buffs) == "table" and buffs.knownSpellsOnly == nil and buffs.onlyMine ~= nil then
				legacyAuraFilters[unit] = buffs.onlyMine == true
			end
		end
	end

	CopyDefaults(self.Defaults, SlickUIDB)
	if SlickUIDB.targetClassificationCentered ~= true then
		SlickUIDB.unitFrames.frameDisplays.target.middleCenter = "classification"
		SlickUIDB.targetClassificationCentered = true
	end
	for unit, enabled in pairs(legacyAuraFilters) do
		if SlickUIDB.unitFrames.auras[unit] then
			SlickUIDB.unitFrames.auras[unit].buffs.knownSpellsOnly = enabled
		end
	end
	if migratePlayerDisplay then
		if type(legacyDisplay) == "table" then
			for slotKey, value in pairs(legacyDisplay) do
				SlickUIDB.unitFrames.frameDisplays.player[slotKey] = value
			end
		end
		if type(legacyFontSizes) == "table" then
			for slotKey, value in pairs(legacyFontSizes) do
				SlickUIDB.unitFrames.frameDisplayFontSizes.player[slotKey] = value
			end
		end
	end
	if SlickUIDB.positions.group and SlickUIDB.positions.group.x == 0 and SlickUIDB.positions.group.y == -315 then
		SlickUIDB.positions.group.y = -225
	end
	self.DB = SlickUIDB
end

function SlickUI:RegisterModule(name, module)
	if self.Modules[name] == nil then
		table.insert(self.ModuleOrder, name)
	end
	self.Modules[name] = module
end

SLASH_SLICKUIRELOAD1 = "/rl"
SlashCmdList.SLICKUIRELOAD = function()
	ReloadUI()
end

local EventFrame = CreateFrame("Frame")
EventFrame:RegisterEvent("ADDON_LOADED")
EventFrame:SetScript("OnEvent", function(_, event, loadedAddon)
	if event ~= "ADDON_LOADED" or loadedAddon ~= addonName then
		return
	end

	SlickUI:InitializeDatabase()
	-- Initialize in TOC registration order. Several secure-frame modules depend
	-- on their foundations being ready first; hash-table iteration can change
	-- when an unrelated module is added and produce protected-action taint.
	for _, moduleName in ipairs(SlickUI.ModuleOrder) do
		local module = SlickUI.Modules[moduleName]
		if type(module.Initialize) == "function" then
			module:Initialize()
		end
	end
	SlickUI:Print("v" .. SlickUI.Version .. " loaded. Type /slick for options.")
end)
