local addonName, RF = ...

-------------------------------------------------------------------------------
-- TeamManager: CRUD, composition analysis, validation for teams
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-- Generate unique 8-char hex ID
-------------------------------------------------------------------------------
function RF:GenerateTeamID()
    local t = time()
    local r = math.random(0, 0xFFFF)
    return format("%04x%04x", bit.band(t, 0xFFFF), r)
end

-------------------------------------------------------------------------------
-- Officer check (shared helper)
-------------------------------------------------------------------------------
function RF:IsOfficer()
    if IsGuildLeader and IsGuildLeader() then return true end
    if C_GuildInfo and C_GuildInfo.CanEditOfficerNote and C_GuildInfo.CanEditOfficerNote() then return true end
    return false
end

-------------------------------------------------------------------------------
-- Officer check for an arbitrary guild member (used by SyncEngine to gate
-- incoming officer-only messages). rankIndex is 0-based as returned by
-- GetGuildRosterInfo/RosterCache; rank 0 is always the Guild Master.
-- GuildControlGetRankFlags is 1-based and flag #12 is "Edit Officer Notes",
-- matching the same permission RF:IsOfficer() checks for the local player.
-------------------------------------------------------------------------------
function RF:IsRankOfficer(rankIndex)
    if rankIndex == nil then return false end
    if rankIndex == 0 then return true end
    if C_GuildInfo and C_GuildInfo.GuildControlGetRankFlags then
        local flags = C_GuildInfo.GuildControlGetRankFlags(rankIndex + 1)
        if flags and flags[12] then return true end
    end
    return false
end

function RF:CanEditTeam(teamID)
    local team = RF:GetTeam(teamID)
    if not team then return false end
    if not team.officerOnly then return true end
    return RF:IsOfficer()
end

-------------------------------------------------------------------------------
-- CRUD
-------------------------------------------------------------------------------
function RF:CreateTeam(name, teamType, opts)
    local guildDB = RF:GetGuildDB()
    if not guildDB then return nil end

    local teams = guildDB.teams
    -- Enforce max
    local count = 0
    for _ in pairs(teams) do count = count + 1 end
    if count >= RF.MAX_TEAMS then
        RF:Print("Maximum of " .. RF.MAX_TEAMS .. " teams reached.")
        return nil
    end

    opts = opts or {}

    -- Strip colons: team name/custom label are sent as fields in the
    -- "TNEW:teamID:name:teamType:maxSize:ts:customLabel:officerFlag" sync
    -- message. An unescaped colon shifts every field after it for receivers.
    name = (name or ""):gsub(":", " ")
    local customTypeLabel = opts.customTypeLabel
    if customTypeLabel then
        customTypeLabel = customTypeLabel:gsub(":", " ")
    end

    local teamID = RF:GenerateTeamID()
    local maxSize = opts.maxSize
    if not maxSize then
        maxSize = 25
        for _, tt in ipairs(RF.TEAM_TYPES) do
            if tt.key == teamType then maxSize = tt.maxSize; break end
        end
    end

    local now = time()
    teams[teamID] = {
        name = name,
        teamType = teamType,
        maxSize = maxSize,
        officerOnly = opts.officerOnly and true or false,
        customTypeLabel = customTypeLabel,
        createdBy = UnitName("player"),
        createdAt = now,
        updatedAt = now,
        members = {},
        memberTimestamps = {},
    }
    guildDB.teamTimestamps[teamID] = now

    RF:AddLog("TEAM", "Created team: " .. name)
    RF:FireCallbacks("TEAMS_CHANGED")
    return teamID
end

function RF:DeleteTeam(teamID)
    local guildDB = RF:GetGuildDB()
    if not guildDB then return end

    local team = guildDB.teams[teamID]
    if not team then return end

    local teamName = team.name
    guildDB.teams[teamID] = nil
    guildDB.teamTimestamps[teamID] = time()
    guildDB.attendance[teamID] = nil

    RF:AddLog("TEAM", "Deleted team: " .. teamName)
    RF:FireCallbacks("TEAMS_CHANGED")
end

function RF:SetTeamOfficerOnly(teamID, value)
    local guildDB = RF:GetGuildDB()
    if not guildDB then return end

    local team = guildDB.teams[teamID]
    if not team then return end

    team.officerOnly = value and true or false
    team.updatedAt = time()
    guildDB.teamTimestamps[teamID] = team.updatedAt

    RF:AddLog("TEAM", (value and "Locked" or "Unlocked") .. " team: " .. team.name)
    RF:FireCallbacks("TEAMS_CHANGED")
end

function RF:RenameTeam(teamID, newName)
    local guildDB = RF:GetGuildDB()
    if not guildDB then return end

    local team = guildDB.teams[teamID]
    if not team then return end

    local oldName = team.name
    team.name = (newName or ""):gsub(":", " ")
    team.updatedAt = time()
    guildDB.teamTimestamps[teamID] = team.updatedAt

    RF:AddLog("TEAM", "Renamed team: " .. oldName .. " -> " .. newName)
    RF:FireCallbacks("TEAMS_CHANGED")
end

function RF:GetTeam(teamID)
    local guildDB = RF:GetGuildDB()
    if not guildDB then return nil end
    return guildDB.teams[teamID]
end

function RF:GetAllTeams()
    local guildDB = RF:GetGuildDB()
    if not guildDB then return {} end
    return guildDB.teams
end

function RF:GetTeamsSorted()
    local guildDB = RF:GetGuildDB()
    if not guildDB then return {} end

    local result = {}
    for id, team in pairs(guildDB.teams) do
        tinsert(result, { id = id, team = team })
    end
    table.sort(result, function(a, b) return a.team.name < b.team.name end)
    return result
end

-------------------------------------------------------------------------------
-- Member Management
-------------------------------------------------------------------------------
function RF:AddTeamMember(teamID, memberName, role)
    local guildDB = RF:GetGuildDB()
    if not guildDB then return false end

    local team = guildDB.teams[teamID]
    if not team then return false end

    -- Already on team
    if team.members[memberName] then return false end

    local now = time()
    team.members[memberName] = { role = role or "DPS", addedAt = now }
    team.memberTimestamps[memberName] = now
    team.updatedAt = now
    guildDB.teamTimestamps[teamID] = now

    RF:FireCallbacks("TEAMS_CHANGED")
    return true
end

function RF:RemoveTeamMember(teamID, memberName)
    local guildDB = RF:GetGuildDB()
    if not guildDB then return false end

    local team = guildDB.teams[teamID]
    if not team then return false end
    if not team.members[memberName] then return false end

    local now = time()
    team.members[memberName] = nil
    team.memberTimestamps[memberName] = now
    team.updatedAt = now
    guildDB.teamTimestamps[teamID] = now

    RF:FireCallbacks("TEAMS_CHANGED")
    return true
end

function RF:SetTeamMemberRole(teamID, memberName, role)
    local guildDB = RF:GetGuildDB()
    if not guildDB then return false end

    local team = guildDB.teams[teamID]
    if not team then return false end
    if not team.members[memberName] then return false end

    local now = time()
    team.members[memberName].role = role
    team.memberTimestamps[memberName] = now
    team.updatedAt = now
    guildDB.teamTimestamps[teamID] = now

    RF:FireCallbacks("TEAMS_CHANGED")
    return true
end

-------------------------------------------------------------------------------
-- Analysis
-------------------------------------------------------------------------------
function RF:GetTeamComposition(teamID)
    local team = RF:GetTeam(teamID)
    if not team then return nil end

    local comp = { TANK = 0, HEALER = 0, DPS = 0, FLEX = 0, total = 0, maxSize = team.maxSize, classCounts = {}, online = 0 }
    for name, info in pairs(team.members) do
        comp.total = comp.total + 1
        local role = info.role or "DPS"
        comp[role] = (comp[role] or 0) + 1

        -- Class count
        local member = RF:GetMemberByName(name)
        if member then
            local cf = member.classFile or "UNKNOWN"
            comp.classCounts[cf] = (comp.classCounts[cf] or 0) + 1
            if member.isOnline then
                comp.online = comp.online + 1
            end
        end
    end
    return comp
end

function RF:GetMemberTeams(memberName)
    local guildDB = RF:GetGuildDB()
    if not guildDB then return {} end

    local result = {}
    for id, team in pairs(guildDB.teams) do
        if team.members[memberName] then
            tinsert(result, { teamID = id, teamName = team.name, role = team.members[memberName].role })
        end
    end
    return result
end

function RF:ValidateTeamComp(teamID)
    local team = RF:GetTeam(teamID)
    if not team then return { warnings = {} } end

    local comp = RF:GetTeamComposition(teamID)
    local ideals = RF.TEAM_COMP_IDEALS[team.teamType] or {}
    local warnings = {}

    if ideals.TANK and comp.TANK < ideals.TANK then
        tinsert(warnings, format("Need %d more tank%s", ideals.TANK - comp.TANK, ideals.TANK - comp.TANK > 1 and "s" or ""))
    end
    if ideals.HEALER and comp.HEALER < ideals.HEALER then
        tinsert(warnings, format("Need %d more healer%s", ideals.HEALER - comp.HEALER, ideals.HEALER - comp.HEALER > 1 and "s" or ""))
    end
    if ideals.DPS and comp.DPS < ideals.DPS then
        tinsert(warnings, format("Need %d more DPS", ideals.DPS - comp.DPS))
    end
    if comp.total > team.maxSize then
        tinsert(warnings, format("Over capacity: %d/%d", comp.total, team.maxSize))
    end

    return { warnings = warnings }
end

function RF:GetUnassignedMembers(teamID)
    local team = RF:GetTeam(teamID)
    if not team then return {} end

    local result = {}
    for _, entry in ipairs(RF.rosterData or {}) do
        if not team.members[entry.name] then
            tinsert(result, entry)
        end
    end
    return result
end

function RF:DuplicateTeam(teamID)
    local guildDB = RF:GetGuildDB()
    if not guildDB then return nil end

    local team = guildDB.teams[teamID]
    if not team then return nil end

    -- Pass maxSize/officerOnly/customTypeLabel through CreateTeam so the
    -- very first TNEW broadcast already carries the full, correct data
    -- instead of being corrected by untransmitted local-only writes.
    local newID = RF:CreateTeam(team.name .. " (Copy)", team.teamType, {
        maxSize = team.maxSize,
        officerOnly = team.officerOnly,
        customTypeLabel = team.customTypeLabel,
    })
    if not newID then return nil end

    -- Route member copies through AddTeamMember so each one is broadcast
    -- (TMEM) to other guild members instead of staying purely local.
    for name, info in pairs(team.members) do
        RF:AddTeamMember(newID, name, info.role)
    end

    RF:FireCallbacks("TEAMS_CHANGED")
    return newID
end

function RF:ExportTeamRoster(teamID)
    local team = RF:GetTeam(teamID)
    if not team then return "" end

    local comp = RF:GetTeamComposition(teamID)
    local lines = {
        team.name,
        format("Type: %s | Members: %d/%d", team.customTypeLabel or team.teamType, comp.total, team.maxSize),
        format("Tanks: %d | Healers: %d | DPS: %d | Flex: %d", comp.TANK, comp.HEALER, comp.DPS, comp.FLEX),
        "---",
    }

    -- Sort members by role then name
    local sorted = {}
    for name, info in pairs(team.members) do
        tinsert(sorted, { name = name, role = info.role })
    end
    local roleOrder = { TANK = 1, HEALER = 2, DPS = 3, FLEX = 4 }
    table.sort(sorted, function(a, b)
        local ra, rb = roleOrder[a.role] or 9, roleOrder[b.role] or 9
        if ra ~= rb then return ra < rb end
        return a.name < b.name
    end)

    for _, m in ipairs(sorted) do
        local member = RF:GetMemberByName(m.name)
        local className = member and member.classDisplayName or ""
        tinsert(lines, format("[%s] %s - %s", m.role, m.name, className))
    end

    return table.concat(lines, "\n")
end

-------------------------------------------------------------------------------
-- Test Data: Populate teams from current roster for solo testing
-- Usage: /rf testteams
-------------------------------------------------------------------------------
function RF:PopulateTestTeams()
    local roster = RF.rosterData
    if not roster or #roster == 0 then
        RF:Print("No roster data available. Open the addon first and wait for roster to load.")
        return
    end

    -- Shuffle roster for variety
    local shuffled = {}
    for _, e in ipairs(roster) do tinsert(shuffled, e) end
    for i = #shuffled, 2, -1 do
        local j = math.random(1, i)
        shuffled[i], shuffled[j] = shuffled[j], shuffled[i]
    end

    local idx = 1
    local function pickMembers(count)
        local members = {}
        for i = 1, count do
            if idx > #shuffled then idx = 1 end
            tinsert(members, shuffled[idx])
            idx = idx + 1
        end
        return members
    end

    local roles = { "TANK", "HEALER", "DPS", "FLEX" }

    -- Create sample teams
    local teamDefs = {
        { name = "Mythic Progression", type = "raid",      count = math.min(18, #shuffled) },
        { name = "Sunday Flex",        type = "raidflex",   count = math.min(22, #shuffled) },
        { name = "M+ Push Group",      type = "mythicplus", count = 5 },
        { name = "RBG Squad",          type = "pvp",        count = math.min(10, #shuffled) },
        { name = "Arena 3s",           type = "arena3",     count = 3 },
    }

    local created = 0
    for _, def in ipairs(teamDefs) do
        local teamID = RF:CreateTeam(def.name, def.type)
        if teamID then
            created = created + 1
            local members = pickMembers(def.count)
            for i, member in ipairs(members) do
                -- Assign roles based on position for realism
                local role
                if def.type == "mythicplus" then
                    if i == 1 then role = "TANK"
                    elseif i == 2 then role = "HEALER"
                    else role = "DPS" end
                elseif def.type == "raid" or def.type == "raidflex" then
                    if i <= 2 then role = "TANK"
                    elseif i <= 6 then role = "HEALER"
                    else role = "DPS" end
                elseif def.type == "pvp" then
                    if i == 1 then role = "TANK"
                    elseif i <= 4 then role = "HEALER"
                    else role = "DPS" end
                else
                    role = roles[math.random(1, 4)]
                end
                RF:AddTeamMember(teamID, member.name, role)
            end
        end
    end

    RF:Print(format("Created %d test teams with members from your guild roster.", created))
    RF:FireCallbacks("TEAMS_CHANGED")
end
