-- 异界之战(DB)
local LuaMongo = _G.lua_mongo
local DB = require("common.DB")
local AnotherWorldBattleDefine = require("anotherWorldBattle.AnotherWorldBattleDefine")
local AnotherWorldBattleConfig = require("excel.anotherWorldBattle")


local dbUpdate = {_id=nil}
local dbUpdateField = {}


-- db
AnotherWorldBattleData = {
    -- lastRoundStartTime = 0, -- 上一轮活动开启时间。实际为当前轮活动开始时间, 已有正式数据, 变量名不改了.
    -- stage = 1,  -- 当前阶段
    -- joinUnionArr = {
    --     { unionId = "", serverId = 123, power = 9999},
    -- },
    -- groupArray = {
    --     [1] = {unionId1, unionId2, unionId3, unionId4, unionId5, unionId6, unionId7, unionId8},
    --     [2] = {unionId9, unionId10,...},
    -- },

    -- unionList = {
    --     [unionId1] = {
    --         serverId = 123,   -- 公会所在服务器Id
    --         power = 9999,
    --         name = "111",
    --         baseCityId = 123, -- 出生城池
    --         baseCityStartTime = nil, -- 出生城的开始占领时间
    --         occupCityList = { -- 城池列表(包括曾占领过, 当前成功占领, 当前只占领过一个或多个据点)
    --             [cityId1] = {
    --                 isOccupy = false, --当前是否占领
    --                 occupyPointList = { --占领本城池中的据点列表
    --                     [pointId1] = { playerUuid = "",  occupyTimeArr = { {startTime1, endTime1}, {startTime2, endTime2}}, },
    --                 },
    --             },
    --         },
    --         gatherInfo = { --集结信息
    --             gatherTime = 0,
    --             gatherCity = 123,
    --         },
    --     },
    -- },

    -- playerList = {
    --     [playerUuid] = {
    --         name = "",
    --         lv = 200,
    --         head = 1234,
    --         headFrame = 1234,
    --         power = 999,
    --         challengeTimes = 10,
    --         lastTime = nil, -- 上一次更新的时间戳
    --         unionId = "",
    --         heroList = {
    --             [cityId] = {
    --                 [pointIdx] = {
    --                     {
    --                         heroUuid = "sdgjdjdj",
    --                         heroStar = 111,
    --                         heroLevel = 111,
    --                         heroCamp =  1,
    --                         heroBody = 111,
    --                         heroIcon = 111,
    --                         heroPower = 11111,
    --                         heroId = 111,
    --                         heroQuality = 1,
    --                     },
    --                 },
    --             },

    --         }
    --     },
    -- }
}

-- 所有分组的城池信息, 启动服务器后生成, 用于快速获取城池信息
Group_2_CityList = {
    -- [groupId] = {
    --     [cityId] = {
    --         occupyUnion = {  -- 占领公会信息, 没有公会占领时为nil
    --             serverId = 124,
    --             unionId = "123",
    --         },

    --         pointArr = { -- 城池中所有据点信息, 没人占领时, unionId, playerUuid为nil
    --             {unionId = nil, playerUuid = nil, },
    --             {unionId = nil, playerUuid = nil, },
    --         }
    --     },
    -- },
}

-- 所有分组的公会排行榜, 启动服务器后生成
Group_2_UnionRankList = {}

-- 所有分组的玩家排行榜, 启动服务器后生成
Group_2_PlayerRankList = {}

-- 区服Id - 公会列表 映射表
Server_2_UnionArr = {
    -- [serverId] = {unionId1, unionId2},
}

-- 公会 - 所属分组 映射表
Union_2_Group = {
    -- [unionId] = groupId,
}


local function initData()
    AnotherWorldBattleData.lastRoundStartTime = 0
    AnotherWorldBattleData.stage = AnotherWorldBattleDefine.AB_STATE_END
    LuaMongo.insert(DB.db_anotherWorldBattle, AnotherWorldBattleData)
end

local function loadData()
    LuaMongo.find(DB.db_anotherWorldBattle)

    local data = {}
    if LuaMongo.next(data) then
        AnotherWorldBattleData = data
    else
        initData()
    end
end

-- 修改db单个字段
local function updateValue(key, value)
	if not key then return end

    if value then
		dbUpdateField["$set"] = {[key]=value}
		dbUpdateField["$unset"] = nil
	else
		dbUpdateField["$set"] = nil
		dbUpdateField["$unset"] = {[key]=1}
	end

	dbUpdate._id = AnotherWorldBattleData._id
	LuaMongo.update(DB.db_anotherWorldBattle, dbUpdate, dbUpdateField)
end

-- 生成城池数据
local function genCityList(net, unionIdArr)
    local function getUnionByCityId(targetCityId)
        for _, unionId in ipairs(unionIdArr) do
            local unionInfo = AnotherWorldBattleData.unionList[unionId]
            -- 其他城池
            if unionInfo and unionInfo.occupCityList then
                for occupyCityId, occupyCityinfo in pairs(unionInfo.occupCityList) do
                    if occupyCityId == targetCityId and occupyCityinfo.isOccupy then
                        return unionId
                    end
                end
            end

            -- 出生城池
            if unionInfo and unionInfo.baseCityId == targetCityId then
                return unionId
            end
        end
        return nil
    end


    local function getPointOccupyPlayer(targetCityId, targetPointIdx)
        for _, unionId in ipairs(unionIdArr) do
            local unionInfo = AnotherWorldBattleData.unionList[unionId]
            if unionInfo and unionInfo.occupCityList and unionInfo.occupCityList[targetCityId] then
                local pointList = unionInfo.occupCityList[targetCityId].occupyPointList
                if pointList and pointList[targetPointIdx] and pointList[targetPointIdx].playerUuid then
                    return unionId, pointList[targetPointIdx].playerUuid
                end
            end
        end
        return nil, nil
    end


    for cityId, cityCfg in ipairs(AnotherWorldBattleConfig.city) do
        local cityEntry = { pointArr = {} }
        net[cityId] = cityEntry

        local unionId = getUnionByCityId(cityId)
        if unionId then
            cityEntry.occupyUnion = unionId
        end

        local pointArr = cityEntry.pointArr
        for i=1, AnotherWorldBattleDefine.AB_POINT_MAX_NUM do
            local unionId1, playerUuid = getPointOccupyPlayer(cityId, i)
            if unionId1 and playerUuid then
                pointArr[i] = {
                    unionId = unionId1,
                    playerUuid = playerUuid
                }
            else
                pointArr[i] = {}
            end
        end
    end
end

-- 工会排行榜
-- 目前最多8条
local function createGuildRank()
    local guilds = {}      -- id -> guildRec（永久表，不删）
    local rank   = {}      -- 1..#guilds 复用数组
    local dirty  = true    -- 脏标记：true表示rank需要重新计算

    local function cmp(a, b)
        if a.occupyCityNum ~= b.occupyCityNum then
            return a.occupyCityNum > b.occupyCityNum
        end
        if a.occupyPointNum ~= b.occupyPointNum then
            return a.occupyPointNum > b.occupyPointNum
        end
        return a.power > b.power
    end

    local function updateGuild(id, name, occupyCityNum, occupyPointNum, power)
        local g = guilds[id]
        if not g then
            g = { id = id, name = name or "",
                occupyCityNum = occupyCityNum or 0,
                occupyPointNum = occupyPointNum or 0,
                power = power or 0 }
            guilds[id] = g
        else
            -- 原地更新，保持table引用
            g.occupyCityNum = occupyCityNum or g.occupyCityNum
            g.occupyPointNum = occupyPointNum or g.occupyPointNum
            g.power = power or g.power
            if name then g.name = name end
        end

        -- 标记rank缓存失效（数据量小，但统一设计模式）
        dirty = true
    end

    -- 内部函数：按需重新排序
    local function sortIfNeeded()
        if not dirty then return end

        -- 全量收集到rank（复用数组）
        local n = 0
        for _, v in pairs(guilds) do
            n = n + 1
            rank[n] = v
        end

        -- 排序
        if n > 1 then
            table.sort(rank, cmp)
        end

        -- 清除尾部残留数据
        local rankMax = AnotherWorldBattleDefine.AB_RANK_MAX_NUM
        for i = rankMax + 1, #rank do
            rank[i] = nil
        end

        dirty = false
    end

    local function getRankList()
        -- 确保排行已更新
        sortIfNeeded()

        -- 构建返回列表
        local ret = {}
        for i = 1, #rank do
            local g = rank[i]
            ret[i] = {
                rank = i, guildId = g.id, name = g.name,
                occupyCityNum = g.occupyCityNum,
                occupyPointNum = g.occupyPointNum,
                power = g.power
            }
        end
        return ret
    end

    local function getGuildDetail(id)
        return guilds[id]
    end

    return {
        updateGuild = updateGuild,
        getRankList = getRankList, 
        getGuildDetail = getGuildDetail
    }
end

-- 玩家排行榜
-- 当前每个公会最多25人，8个公会为一组，最多200人
local function createPlayerRank()
    local players     = {}              -- id -> rec（永久存在，不删）
    local rank        = {}              -- 已排序的前RANK_MAX名，复用
    local tmp         = {}              -- 临时排序数组，预分配
    local dirty       = true            -- 脏标记：true表示rank需要重新计算

    local function cmp(a, b)
        if a.pointNum ~= b.pointNum then return a.pointNum > b.pointNum end
        if a.pointAllWeight ~= b.pointAllWeight then return a.pointAllWeight > b.pointAllWeight end
        return a.power > b.power
    end

    -- 预分配tmp容量，避免运行时动态扩容
    for i = 1, 500 do tmp[i] = nil end

    local function updatePlayer(id, name, power, pNum, pWeight)
        -- 更新或创建玩家记录
        local rec = players[id]
        if not rec then
            rec = { id = id, name = name or "", power = power or 0,
                    pointNum = pNum or 0, pointAllWeight = pWeight or 0 }
            players[id] = rec
        else
            -- 原地更新，保持table引用不变
            rec.power = power
            if pNum ~= nil then rec.pointNum = pNum end
            if pWeight ~= nil then rec.pointAllWeight = pWeight end
            if name then rec.name = name end
        end

        -- 标记rank缓存失效
        dirty = true
    end

    -- 内部函数：按需重新排序
    local function sortIfNeeded()
        if not dirty then return end

        -- 收集所有玩家记录到tmp（仅复制引用）
        local n = 0
        for _, v in pairs(players) do
            n = n + 1
            tmp[n] = v
        end

        -- 排序
        if n > 1 then
            table.sort(tmp, cmp)
        end

        -- 将前RANK_MAX名复制到rank（复用数组）
        local rankMax = AnotherWorldBattleDefine.AB_RANK_MAX_NUM
        local oldRankLen = #rank
        for i = 1, rankMax do
            rank[i] = tmp[i]  -- 若i>n，则赋值为nil，自动缩小rank长度
        end

        -- 清除rank数组尾部残留的旧数据
        for i = rankMax + 1, oldRankLen do
            rank[i] = nil
        end

        dirty = false
    end

    local function getRankList()
        -- 确保排行已更新
        sortIfNeeded()

        -- 构建返回列表
        local ret = {}
        for i = 1, #rank do
            local p = rank[i]
            ret[i] = { rank = i, playerId = p.id, name = p.name, power = p.power,
                        pointNum = p.pointNum, pointAllWeight = p.pointAllWeight }
        end
        return ret
    end

    local function getPlayerDetail(id)
        return players[id]
    end

    return {
        updatePlayer    = updatePlayer,
        getRankList     = getRankList,
        getPlayerDetail = getPlayerDetail,
    }
end

-- 生成公会排行榜
local function genGroupUnionRankList(unionRank, unionIdArr)
    local function getUnionRankVal(union)
        local occupyCityNum, occupyPointNum, power = 0, 0, 0
        power = union.power or 0

        for _, v in pairs(union.occupCityList or {}) do
            if v.isOccupy then
                occupyCityNum = occupyCityNum + 1
            end

            for _, pointInfo in pairs(v.occupyPointList or {}) do
                if pointInfo and pointInfo.playerUuid then
                    occupyPointNum = occupyPointNum + 1
                end
            end
        end

        return occupyCityNum, occupyPointNum, power
    end

    for _, unionId in ipairs(unionIdArr) do
        local unionData = AnotherWorldBattleData.unionList[unionId]
        if unionData then
            local occupyCityNum, occupyPointNum, power = getUnionRankVal(unionData)
            unionRank.updateGuild(unionId, unionData.name, occupyCityNum, occupyPointNum, power)
        end
    end
end

-- 生成个人排行榜
local function genGroupPlayerRankList(playerRank, unionIdArr, playerListData)
    local function insertPlayerRank(unionId)
        if not playerListData then
            return
        end

        for playerUuid, playerInfo in pairs(playerListData) do
            if playerInfo.unionId == unionId then
                local pointNum, pointAllWeight = 0, 0
                for cityId, pointList in pairs(playerInfo.heroList or {}) do
                    for _, _ in pairs(pointList) do
                        pointNum = pointNum + 1
                        local cityCfg = AnotherWorldBattleConfig.city[cityId]
                        pointAllWeight = pointAllWeight + (cityCfg and cityCfg.pointWeight or 0)
                    end
                end

                playerRank.updatePlayer(playerUuid, playerInfo.name, playerInfo.power, pointNum, pointAllWeight)
            end
        end
    end

    for _, unionId in ipairs(unionIdArr) do
        insertPlayerRank(unionId)
    end

end

-- 生成 "公会-公会所属分组Id" 映射表
local function genUnion2GroupList(groupId, unionIdArr)
    for _, unionId in ipairs(unionIdArr) do
        Union_2_Group[unionId] = groupId
    end
end

-- 生成 "serverId - 公会Array" 映射表
local function genServer2UnionArr(unionRank)
    local rankList = unionRank.getRankList()
    for _, rankInfo in ipairs(rankList) do
        local unionId = rankInfo.guildId
        local unionInfo = AnotherWorldBattleData.unionList[unionId]
        if unionInfo then
            local serverId = unionInfo.serverId
            Server_2_UnionArr[serverId] = Server_2_UnionArr[serverId] or {}
            Server_2_UnionArr[serverId][#Server_2_UnionArr[serverId]+1] = unionId
        end
    end
end

-- 生成各组的缓存数据
local function genGroupCache()
    if not AnotherWorldBattleData.groupArray then
        return
    end

    local playerListData = AnotherWorldBattleData.playerList

    for groupId, unionIdArr in ipairs(AnotherWorldBattleData.groupArray) do
        -- 城池
        Group_2_CityList[groupId] = {}
        genCityList(Group_2_CityList[groupId], unionIdArr)

        -- 公会排行榜
        Group_2_UnionRankList[groupId] = createGuildRank()
        genGroupUnionRankList(Group_2_UnionRankList[groupId], unionIdArr)

        -- "serverId -- unionIdArray" 映射表
        genServer2UnionArr(Group_2_UnionRankList[groupId])

        -- 个人排行榜
        Group_2_PlayerRankList[groupId] = createPlayerRank()
        genGroupPlayerRankList(Group_2_PlayerRankList[groupId], unionIdArr, playerListData)

        -- "公会-公会所属分组"  映射表
        genUnion2GroupList(groupId, unionIdArr)
    end
end


function initAfterStart()
    if _G.is_middle ~= true then return end

    loadData()

    genGroupCache()
end

-- 重置数据
function ResetData()
    Group_2_CityList = {}
    Group_2_UnionRankList = {}
    Group_2_PlayerRankList = {}
    Union_2_Group = {}
    Server_2_UnionArr = {}

    AnotherWorldBattleData.lastRoundStartTime = os.time()
    -- AnotherWorldBattleData.stage = AnotherWorldBattleDefine.AB_STATE_JOIN
    AnotherWorldBattleData.joinUnionArr = nil
    AnotherWorldBattleData.groupArray = nil
    AnotherWorldBattleData.unionList = nil
    AnotherWorldBattleData.playerList = nil

    dbUpdate._id = AnotherWorldBattleData._id
    LuaMongo.update(DB.db_anotherWorldBattle, dbUpdate, AnotherWorldBattleData)
end



-- 获取公会所在分组的Id
function GetUnionGroupId(unionId)
    return Union_2_Group[unionId]
end

-- 当自己公会没有参赛时, 获取本服第一个公会所在分组Id
function GetGroupIdByServerId(serverId)
    local unionArr = Server_2_UnionArr[serverId]
    if unionArr and unionArr[1] then
        local unionId = unionArr[1]
        return GetUnionGroupId(unionId)
    end
end




-- 获取活动当前阶段
function GetStage()
    return AnotherWorldBattleData.stage
end
-- 更新活动当前阶段
function UpdateStage(newStage)
    AnotherWorldBattleData.stage = newStage
    updateValue("stage", newStage)

    if newStage == AnotherWorldBattleDefine.AB_STATE_BATTLE then
        genGroupCache()
    end
end


-- 获取活动上一轮的开始时间
function GetLastRoundStartTime()
    return AnotherWorldBattleData.lastRoundStartTime
end
function UpdateLastRoundStartTime(newTime)
    AnotherWorldBattleData.lastRoundStartTime = newTime
    updateValue("lastRoundStartTime", newTime)
end


-- 获取参赛公会列表
function GetJoinUnionArr()
    return AnotherWorldBattleData.joinUnionArr
end
-- 更新参赛公会列表
function UpdateJoinUnionArr(newJoinUnionArr)
    AnotherWorldBattleData.joinUnionArr = newJoinUnionArr
    updateValue("joinUnionArr", newJoinUnionArr)
end


-- 获取分组列表
function GetGroupArray()
    return AnotherWorldBattleData.groupArray
end
-- 更新分组列表
function UpdateGroupArray(newGroupArray)
    AnotherWorldBattleData.groupArray = newGroupArray
    updateValue("groupArray", newGroupArray)
end



-- 获取公会名字
function GetUnionName(unionId)
    return AnotherWorldBattleData.unionList and AnotherWorldBattleData.unionList[unionId].name or ""
end
-- 获取某个公会数据
function GetUnionData(unionId)
    return AnotherWorldBattleData.unionList and AnotherWorldBattleData.unionList[unionId]
end
-- 更新某个公会数据
function UpdateUnionData(unionId, newUnionData)
    if not AnotherWorldBattleData.unionList then
        AnotherWorldBattleData.unionList = {}
        -- updateValue("unionList", AnotherWorldBattleData.unionList)
    end

    AnotherWorldBattleData.unionList[unionId] = newUnionData
    updateValue("unionList"..".".. unionId, newUnionData)
end

-- 获取公会列表
function GetUnionList()
    return AnotherWorldBattleData.unionList
end




-- 获取玩家名字
function GetPlayerName(playerUuid)
    local playerListData = AnotherWorldBattleData.playerList
    if playerListData and playerListData[playerUuid] then
        return playerListData[playerUuid].name or ""
    end

    return ""
end
-- 获取玩家数据
function GetPlayerData(playerUuid)
    return AnotherWorldBattleData.playerList and AnotherWorldBattleData.playerList[playerUuid]
end
-- 更新玩家数据
function UpdatePlayerData(playerUuid, newPlayerData)
    if not AnotherWorldBattleData.playerList then
        AnotherWorldBattleData.playerList = {}
        -- updateValue("playerList", AnotherWorldBattleData.playerList)
    end
    AnotherWorldBattleData.playerList[playerUuid] = newPlayerData
    updateValue("playerList".. "." .. playerUuid, newPlayerData)
end
-- 获取公会列表
function GetPlayerList()
    return AnotherWorldBattleData.playerList
end


-- 获取城池数据
function GetCityData(groupId, cityId)
    if Group_2_CityList[groupId] then
        return Group_2_CityList[groupId][cityId]
    end
end
-- 更新城池数据
function UpdateCityData(groupId, cityId, newCityData)
    if Group_2_CityList[groupId] then
        Group_2_CityList[groupId][cityId] = newCityData
    end
end

-- 获取所有城池数据
function GetCityListByGroupId(groupId)
    return Group_2_CityList[groupId]
end
-- 更新所有城池数据
function UpdateCityList(groupId, newCityList)
    Group_2_CityList[groupId] = newCityList
end




-- 获取公会排行数据
function GetUnionRankList(groupId)
    local unionRank = Group_2_UnionRankList[groupId]
    if unionRank then
        return unionRank.getRankList()
    end
end
-- 更新公会的排行榜数据, cityVal, pointVal为变化的值
function UpdateUnionRankList(groupId, unionId, cityVal, pointVal, newPower)
    local unionRank = Group_2_UnionRankList[groupId]
    if unionRank then
        local unionRankInfo = unionRank.getGuildDetail(unionId)
        unionRankInfo.occupyCityNum = unionRankInfo.occupyCityNum + cityVal
        unionRankInfo.occupyPointNum = unionRankInfo.occupyPointNum + pointVal
        if newPower then
            unionRankInfo.power = newPower
        end
        unionRank.updateGuild(unionId, unionRankInfo.name, unionRankInfo.occupyCityNum, unionRankInfo.occupyPointNum, unionRankInfo.power)
    end
end



-- 生成玩家用于排名的数据
local function genPlayerRankVal(playerUuid)
    local playerListData = AnotherWorldBattleData.playerList
    local name, power, pointNum, pointAllWeight = "", 0, 0, 0

    if playerListData and playerListData[playerUuid] then
        power = playerListData[playerUuid].power or 0
        name = playerListData[playerUuid].name or ""

        for cityId, pointList in pairs(playerListData[playerUuid].heroList or {}) do
            for _, _ in pairs(pointList) do
                pointNum = pointNum + 1
                local cityCfg = AnotherWorldBattleConfig.city[cityId]
                pointAllWeight = pointAllWeight + (cityCfg and cityCfg.pointWeight or 0)
            end
        end
    end

    return name, power, pointNum, pointAllWeight
end
-- 获取玩家排行数据
function GetPlayerRankList(groupId)
    local playerRank = Group_2_PlayerRankList[groupId]
    if playerRank then
        return playerRank.getRankList()
    end
end
-- 更新玩家排行数据, pointVal, pointWeightVal为变化的值
function UpdatePlayerRankList(groupId, playerUuid, pointVal, pointWeightVal, newPower)
    local playerRank = Group_2_PlayerRankList[groupId]
    if playerRank then
        local playerRankData = playerRank.getPlayerDetail(playerUuid)
        if not playerRankData then
            playerRankData = {}
            local name, power, pointNum, pointAllWeight = genPlayerRankVal(playerUuid)
            playerRankData.name = name
            playerRankData.power = power
            playerRankData.pointNum = pointNum
            playerRankData.pointAllWeight = pointAllWeight
        else
            playerRankData.pointNum = playerRankData.pointNum + pointVal
            playerRankData.pointAllWeight = playerRankData.pointAllWeight + pointWeightVal
            if newPower then
                playerRankData.power = newPower
            end
        end
        playerRank.updatePlayer(playerUuid, playerRankData.name, playerRankData.power, playerRankData.pointNum, playerRankData.pointAllWeight)
    end
end