--[[
    Cerebro - Database Migrations
    Extracted from Database.lua (Phase 2a decomposition)

    MigrateDatabase function and all version-bump migration cases.
    Depends on Database/Schema.lua being loaded first (Schema.lua
    defines the defaults table consumed by InitDatabase, which sets
    db.global before MigrateDatabase runs).

    @module Database.Migrations
    @version 0.12.0
]]--

local AddonName = "Cerebro"
local Cerebro = LibStub("AceAddon-3.0"):GetAddon(AddonName)

--[[=============================================
	Database Migrations
===============================================]]

function Cerebro:MigrateDatabase()
	local currentVersion = self.db.global.dbVersion or 0

	if currentVersion < 1 then
		self:Debug("Migrating database to version 1...")
		-- Initial version, nothing to migrate
		self.db.global.dbVersion = 1
	end

	if currentVersion < 2 then
		self:Debug("Migrating database to version 2 (Guild Scope)...")

		-- Infer scope from existing player data if possible
		local inferredScope = self:InferGuildFromPlayers()
		if inferredScope and inferredScope.realm then
			self.db.global.scope = {
				guildGUID = nil,  -- Cannot infer GUID from player names
				guildName = inferredScope.guildName,
				realm = inferredScope.realm,
				setBy = "Migration-v2",
				setAt = time(),
				migrated = true,  -- Flag for audit trail
			}
			-- Note: Ledger may not be initialized yet during migration
			-- Audit log will be added in Core.lua after initialization
			self:Debug(string.format("Scope inferred: %s-%s",
				inferredScope.guildName or "?", inferredScope.realm or "?"))
		end

		self.db.global.dbVersion = 2
	end

	if currentVersion < 3 then
		self:Debug("Migrating database to version 3 (LWW lastUpdate)...")

		-- Add lastUpdate to all existing players for Last-Write-Wins sync
		-- Use GetServerTime if available, fallback to time()
		local serverTime = GetServerTime and GetServerTime() or time()
		local migratedCount = 0

		for name, data in pairs(self.db.global.players or {}) do
			if not data.lastUpdate then
				data.lastUpdate = serverTime
				migratedCount = migratedCount + 1
			end
		end

		if migratedCount > 0 then
			self:Debug(string.format("Added lastUpdate to %d player records", migratedCount))
		end

		self.db.global.dbVersion = 3
	end

	if currentVersion < 4 then
		-- v0.8.4: Reset debug mode to false for all users
		-- Previously, officer's debug setting was being synced to raiders accidentally
		-- This migration ensures everyone starts clean with debug OFF
		if self.db.global.config and self.db.global.config.debug then
			self.db.global.config.debug = false
			self:Print("Debug mode has been turned OFF (use /cb debug to re-enable)")
		end

		self.db.global.dbVersion = 4
	end

	if currentVersion < 5 then
		self:Debug("Migrating database to version 5 (Attendance Tracking)...")

		-- Initialize seasonBossKills and seasonID if not present
		if not self.db.global.seasonBossKills then
			self.db.global.seasonBossKills = 0
		end
		if not self.db.global.seasonID then
			self.db.global.seasonID = 0
		end

		-- Add coreAdmittedAt to existing Core players
		-- Set to current time so they aren't penalized for pre-feature bosses
		local migratedCount = 0
		for name, data in pairs(self.db.global.players or {}) do
			if not data.coreAdmittedAt and data.status == "Core" then
				data.coreAdmittedAt = time()
				migratedCount = migratedCount + 1
			end
			-- Ensure attendance.full exists
			if data.attendance and not data.attendance.full then
				data.attendance.full = 0
			end
		end

		if migratedCount > 0 then
			self:Debug(string.format("Added coreAdmittedAt to %d Core players", migratedCount))
		end

		self.db.global.dbVersion = 5
	end

	if currentVersion < 6 then
		self:Debug("Migrating database to version 6 (Epoch Tagging)...")

		-- Add createdEpoch to all existing players for stale record detection
		-- Records pre-migration are assigned current epoch since they're known-good
		local currentEpoch = self.db.global.resetEpoch or 0
		local migratedCount = 0

		for name, data in pairs(self.db.global.players or {}) do
			if not data.createdEpoch then
				data.createdEpoch = currentEpoch
				migratedCount = migratedCount + 1
			end
		end

		if migratedCount > 0 then
			self:Debug(string.format("Added createdEpoch to %d player records", migratedCount))
		end

		self.db.global.dbVersion = 6
	end

	if currentVersion < 7 then
		self:Debug("Migrating database to version 7 (Delta Sync v2)...")

		-- Add rev and fieldRev to existing players for field-level sync
		-- rev = lastUpdate (or 0) to seed initial revision
		-- fieldRev = {} (empty, will be populated on first mutation)
		local migratedCount = 0

		for name, data in pairs(self.db.global.players or {}) do
			if not data.rev then
				data.rev = data.lastUpdate or 0
				data.fieldRev = {}
				migratedCount = migratedCount + 1
			end
		end

		-- Initialize syncState if not present (AceDB handles defaults, but be explicit)
		if not self.db.global.syncState then
			self.db.global.syncState = {
				nextRev = 0,
				nextSeq = 0,
				lastFullSyncAt = 0,
				dirtyPlayers = {},
				dirtyPurgesAt = 0,
				dirtyMplusAt = 0,
				pendingSyncs = {},
				maxPendingSyncs = 5,
			}
		end

		if migratedCount > 0 then
			self:Debug(string.format("Added rev/fieldRev to %d player records", migratedCount))
		end

		self.db.global.dbVersion = 7
	end

	if currentVersion < 8 then
		self:Debug("Migrating database to version 8 (Communication V2)...")

		-- Reset v1 sync protocol metadata (preserve all player data)
		-- This prevents stale v1 sync state from interfering with v2
		local ss = self.db.global.syncState or {}
		ss.peerStatus = {}
		ss.lastBroadcastSeq = 0
		ss.lastBroadcastAt = 0
		ss.dirtyPlayers = {}
		ss.pendingSyncs = {}
		self.db.global.syncState = ss

		-- Initialize v2 sync state
		self.db.global.syncStateV2 = {
			peerHashes = {},      -- { [playerName] = { rosterHash, configHash, lastSeen } }
			isStale = false,      -- True when local state diverges from officer
			lastHeartbeatSent = 0,
			lastSnapshotReceived = 0,
		}

		self:Debug("Communication V2 sync state initialized, v1 metadata reset")

		self.db.global.dbVersion = 8
	end

	-- TODO(v0.10.25+): Remove this migration once all users have updated past v0.10.24.
	-- After that point, no installs will have the old requireConsumables key.
	if currentVersion < 9 then
		self:Debug("Migrating database to version 9 (Prep schema cleanup)...")

		-- Remove orphaned requireConsumables key from prep config.
		-- v0.10.24 replaced this single toggle with per-category toggles:
		-- requireFood, requireFlask, requireAugmentRune, requireVantusRune, requireWeaponEnhancement
		local prepCfg = self.db.global.config and self.db.global.config.prep
		if prepCfg and prepCfg.requireConsumables ~= nil then
			prepCfg.requireConsumables = nil
			self:Debug("Removed orphaned requireConsumables key from prep config")
		end

		-- Remove orphaned prepAwardAmount (replaced by per-category EP values)
		if prepCfg and prepCfg.prepAwardAmount ~= nil then
			prepCfg.prepAwardAmount = nil
			self:Debug("Removed orphaned prepAwardAmount key from prep config")
		end

		self.db.global.dbVersion = 9
	end
end

-- Infer most common realm from existing player keys
function Cerebro:InferGuildFromPlayers()
	local realmCounts = {}
	local totalPlayers = 0

	for playerName, _ in pairs(self.db.global.players or {}) do
		local _, realm = strsplit("-", playerName)
		if realm then
			-- Normalize realm for consistent counting
			local normalizedRealm = self:NormalizeRealmString(realm)
			if normalizedRealm then
				realmCounts[normalizedRealm] = (realmCounts[normalizedRealm] or 0) + 1
				totalPlayers = totalPlayers + 1
			end
		end
	end

	if totalPlayers == 0 then return nil end

	-- Find most common realm
	local topRealm, topCount = nil, 0
	for realm, count in pairs(realmCounts) do
		if count > topCount then
			topRealm = realm
			topCount = count
		end
	end

	-- Get guild name from current character if in guild
	local guildName = nil
	if IsInGuild and IsInGuild() then
		guildName = GetGuildInfo("player")
	end

	return {
		guildName = guildName,  -- May be nil if not in guild during migration
		realm = topRealm,       -- Already normalized
	}
end
