Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

Documentation for this module may be created at Module:Team/doc

local p = {}
local cargo = mw.ext.cargo
local html = mw.html
local currentTitle = mw.title.getCurrentTitle()

local function sqlEscape(s)
    if not s then return "" end
    s = s:gsub("\\", "\\\\")
    s = s:gsub("'", "\\'")
    return s
end

-- ============================================================
-- HELPER: Argument Fetcher
-- ============================================================
local function getArgs(frame)
    local args = {}
    if frame:getParent() then
        for k, v in pairs(frame:getParent().args) do args[k] = v end
    end
    for k, v in pairs(frame.args) do args[k] = v end
    return args
end

-- ============================================================
-- HELPER: Sanitize Strings
-- ============================================================
local function clean(s)
    if not s then return nil end
    local cleaned = s:gsub("[\r\n]", ""):gsub("^%s*(.-)%s*$", "%1")
    if cleaned == "" then return nil end
    return cleaned
end

-- ============================================================
-- HELPER: Tier Class (MATCHES TOURNAMENT MODULE EXACTLY)
-- ============================================================
local function getTierClass(tier)
    if not tier then return "tier-def" end
    local t = tier:lower()
    -- Loose matching to catch "A Tier", "A-Tier", "Tier A"
    if string.find(t, "s") and string.find(t, "tier") then return "tier-s" end
    if string.find(t, "a") and string.find(t, "tier") then return "tier-a" end
    if string.find(t, "b") and string.find(t, "tier") then return "tier-b" end
    if string.find(t, "c") and string.find(t, "tier") then return "tier-c" end
    return "tier-def"
end

local function formatCurrency(amount)
    if not amount then return "0" end
    local n = math.floor(tonumber(amount) or 0)
    local s = tostring(n)
    local last3 = s:sub(-3)
    local rest = s:sub(1, -4)
    local formattedRest = rest:reverse():gsub("(%d%d)", "%1,"):reverse()
    if formattedRest ~= "" then last3 = "," .. last3 end
    return '<span class="indian-currency">₹ ' .. formattedRest .. last3 .. '</span>'
end

local function getSocials(args)
    local container = html.create('div'):addClass('fib-socials')
    local hasSocials = false
    local platforms = {
        {arg='instagram', file='Icon_instagram.png', base='https://www.instagram.com/'},
        {arg='twitter',   file='Icon_twitter.png',   base='https://twitter.com/'},
        {arg='youtube',   file='Icon_youtube.png',   base='https://www.youtube.com/'},
        {arg='discord',   file='Icon_discord.png',   base='https://discord.gg/'},
        {arg='facebook',  file='Icon_facebook.png',  base='https://www.facebook.com/'},
        {arg='website',   file='Icon_website.png'}
    }
    for _, p in ipairs(platforms) do
        local val = args[p.arg]
        if val and val ~= "" then
            hasSocials = true
            local link
            if p.arg == 'website' then
                link = val
            elseif val:match("^https?://") then
                link = val
            else
                link = p.base .. val
            end
            container:wikitext('[[File:' .. p.file .. '|24px|link=' .. link .. '|class=social-img]]')
        end
    end

    if hasSocials then
        return tostring(container)
    end
    return ""
end

local function getInfoboxLogo(teamName, image, imageDark)
    local lightFile = (image ~= "" and image) or (teamName .. '.png')
    local darkFile = (imageDark ~= "" and imageDark) or (teamName .. '_dark.png')
    local hasLight = mw.title.new('File:' .. lightFile).exists
    local hasDark = mw.title.new('File:' .. darkFile).exists
    local container = html.create('div'):addClass('fib-image')
    local lSpan = container:tag('span'):addClass('logo-lightmode')
    if hasLight then lSpan:wikitext('[[File:' .. lightFile .. '|220px]]') else lSpan:wikitext('[[File:Shield_team.png|180px]]') end
    local dSpan = container:tag('span'):addClass('logo-darkmode')
    if hasDark then dSpan:wikitext('[[File:' .. darkFile .. '|220px]]') elseif hasLight then dSpan:wikitext('[[File:' .. lightFile .. '|220px]]') else dSpan:wikitext('[[File:Shield_team_dark.png|180px]]') end
    return tostring(container)
end

-- ============================================================
-- MAIN 1: TEAM INFOBOX
-- ============================================================
function p.infobox(frame)
    local args = getArgs(frame)
    
    -- 1. Identity Logic
    -- 'team' is the KEY for Database lookups (The Page Name, e.g., "Team SouL")
    local team = clean(args.name) or currentTitle.subpageText
    
    -- 'displayTitle' is for the UI Header (e.g., "iQOO SouL")
    -- If display_name is empty, fallback to the Page Name
    local displayTitle = clean(args.display_name) or team

    local root = html.create('div'):addClass('flat-infobox')
    
    -- 2. Header (Uses Display Name)
    root:tag('div'):addClass('fib-header')
        :tag('div'):addClass('fib-title'):wikitext(displayTitle)
    
    -- 3. Logo (Uses Page Name for filenames usually, or manual override)
    root:wikitext(getInfoboxLogo(team, clean(args.image), clean(args.image_dark)))
    
    -- 4. Earnings Query (Always uses Page Name to match Roster/Prizes)
    local earnings = "0"
    if cargo and cargo.query then
        local results = cargo.query("PrizeMoney", "SUM(prize)=total", { where = "team = '" .. sqlEscape(team) .. "' AND (player='' OR player IS NULL)" })
        if results and #results > 0 and results[1].total then earnings = results[1].total end
    end
    
    -- 5. Rank Query
    local rankVal = "Unranked"
    if cargo and cargo.query then
        local rResults = cargo.query("Krafton_Rankings", "rank", { where = "name = '" .. sqlEscape(team) .. "' AND type='Team'", limit = 1 })
        if rResults and #rResults > 0 then rankVal = "#" .. rResults[1].rank end
    end
    
    local statusColor = "#333"
    local statusText = clean(args.status) or "Active"
    if args.status then
        local s = args.status:lower()
        if s == "active" then statusColor = "#16a34a" elseif s == "inactive" or s == "disbanded" then statusColor = "#dc2626" end
    end

    -- 6. Grids
    local grid1 = root:tag('div'):addClass('fib-grid')
    grid1:tag('div'):addClass('fib-cell'):tag('div'):addClass('fib-label-sm'):wikitext('Status'):done():tag('div'):addClass('fib-value-sm'):css('color', statusColor):wikitext(statusText):done()
    grid1:tag('div'):addClass('fib-cell'):tag('div'):addClass('fib-label-sm'):wikitext('Krafton Rank'):done():tag('div'):addClass('fib-value-sm'):wikitext(rankVal):done()

    local grid2 = root:tag('div'):addClass('fib-grid')
    grid2:tag('div'):addClass('fib-cell'):tag('div'):addClass('fib-label-sm'):wikitext('Country'):done():tag('div'):addClass('fib-value-sm'):wikitext(clean(args.country) or 'TBD'):done()
    grid2:tag('div'):addClass('fib-cell'):tag('div'):addClass('fib-label-sm'):wikitext('Tag'):done():tag('div'):addClass('fib-value-sm'):wikitext(clean(args.short_code) or '-'):done()

    if earnings and tonumber(earnings) and tonumber(earnings) > 0 then
        root:tag('div'):addClass('fib-prize'):tag('div'):addClass('fib-label-sm'):wikitext('Total Earnings'):done():tag('div'):addClass('fib-prize-val'):wikitext(formatCurrency(earnings)):done()
    end
    
    -- 7. List Info
    local list = root:tag('div'):addClass('fib-list')
    local function addRow(label, value)
        if value and value ~= "" then list:tag('div'):addClass('fib-row'):tag('div'):addClass('fib-label'):wikitext(label):done():tag('div'):addClass('fib-data'):wikitext(value):done() end
    end
    
    -- Note: 'game' arg is ignored here, so it won't show in the UI
    
    if args.sponsors then addRow('Sponsors', args.sponsors:gsub(",", "<br>")) end
    root:wikitext(getSocials(args))
    return tostring(root)
end

-- ============================================================
-- MAIN 2: HYBRID ROSTER (SORTED BY ROLE)
-- ============================================================
function p.roster(frame)
    local args = getArgs(frame)
    local team = clean(args.team) or currentTitle.subpageText
    
    -- 1. Fetch Database Players
    local dbPlayers = {}
    if cargo and cargo.query then
        local tables = "Players"
        local fields = "id, real_name, role, nationality, image, _pageName"
        local where = "current_team = '" .. sqlEscape(team) .. "' AND status='Active'"
        local results = cargo.query(tables, fields, { where = where })
        
        for _, row in ipairs(results) do
            dbPlayers[row.id] = {
                id = row.id,
                name = row.real_name,
                role = row.role,
                flag = clean(row.nationality),
                image = clean(row.image),
                link = row._pageName,
                source = "auto"
            }
        end
    end
    
    -- 2. Process Manual Inputs
    local finalRoster = {}
    for _, pData in pairs(dbPlayers) do table.insert(finalRoster, pData) end
    for i = 1, 10 do
        local mid = clean(args['player' .. i])
        if mid and mid ~= "" then
            if not dbPlayers[mid] then
                table.insert(finalRoster, {
                    id = mid,
                    name = clean(args['name' .. i]) or "",
                    role = clean(args['role' .. i]) or "Player",
                    flag = clean(args['flag' .. i]) or "India",
                    image = clean(args['image' .. i]) or "", 
                    link = mid, 
                    source = "manual"
                })
            end
        end
    end
    
    -- 3. Define Role Hierarchy & Colors
local roleConfig = {
        { key = "igl",      sort = 1, color = "#eab308" }, -- Gold
        { key = "filter",   sort = 2, color = "#14b8a6" }, -- Teal (New Position)
        { key = "entry",    sort = 3, color = "#f97316" }, -- Orange
        { key = "assault",  sort = 4, color = "#3b82f6" }, -- Blue
        { key = "fragger",  sort = 4, color = "#3b82f6" }, -- Blue
        { key = "support",  sort = 5, color = "#22c55e" }, -- Green
        { key = "medic",    sort = 5, color = "#22c55e" }, -- Green
        { key = "scout",    sort = 6, color = "#14b8a6" }, -- Teal
        { key = "sniper",   sort = 7, color = "#ef4444" }, -- Red
        { key = "coach",    sort = 8, color = "#a855f7" }, -- Purple
        { key = "analyst",  sort = 9, color = "#a855f7" }, -- Purple
        { key = "manager",  sort = 10, color = "#1f2937" } -- Dark Grey
    }
    
    -- 4. Assign Sort Priority & Color
    for _, player in ipairs(finalRoster) do
        local r = (player.role or ""):lower()
        player.sortOrder = 99
        player.color = "#64748b" -- Gray
        
        for _, config in ipairs(roleConfig) do
            if r:find(config.key) then
                player.sortOrder = config.sort
                player.color = config.color
                break 
            end
        end
    end
    
    -- 5. Sort
    table.sort(finalRoster, function(a, b)
        if a.sortOrder ~= b.sortOrder then
            return a.sortOrder < b.sortOrder 
        else
            return (a.id or ""):lower() < (b.id or ""):lower() 
        end
    end)
    
    -- 6. Render
    local grid = html.create('div'):addClass('hero-roster-grid')
    
    for _, player in ipairs(finalRoster) do
        local card = grid:tag('div'):addClass('hero-player-card')
        
        -- Header
        local header = card:tag('div'):addClass('hero-card-image')
        if player.image and player.image ~= "" then
            header:wikitext('[[File:' .. player.image .. '|link=' .. player.link .. ']]')
        else
            header:wikitext('[[File:Player_Placeholder.png|link=' .. player.link .. ']]')
        end
        
        -- Body
        local body = card:tag('div'):addClass('hero-card-body')
        
        body:tag('div'):addClass('hero-role-pill')
            :css('background-color', player.color)
            :wikitext(player.role or "Player")
            
        body:tag('div'):addClass('hero-player-id'):wikitext('[[' .. player.link .. '|' .. player.id .. ']]')
        if player.name and player.name ~= "" then
            body:tag('div'):addClass('hero-player-name'):wikitext(player.name)
        end
        
        if player.flag then
            local safeFlag = player.flag:gsub(" ", "_") 
            local flagFile = "Flag_" .. safeFlag .. ".png"
            body:tag('div'):addClass('hero-player-flag'):wikitext('[[File:' .. flagFile .. '|link=]] ' .. player.flag)
        end
    end
    
    return tostring(grid)
end

-- ============================================================
-- MAIN 3: FORMER PLAYERS LIST (NO JOIN + FLAT UI DESIGN)
-- ============================================================
function p.formerPlayers(frame)
    local args = getArgs(frame)
    local team = clean(args.team) or currentTitle.subpageText
    
    -- STEP 1: Get History
    local history = cargo.query(
        "Player_Former_Teams", 
        "player_id, role, join_date, leave_date", 
        {
            where = "team = '" .. sqlEscape(team) .. "' AND leave_date != ''",
            orderBy = "leave_date DESC",
            limit = 100
        }
    )
    
    if not history or #history == 0 then
        return '<div style="font-style:italic; color:#64748b; padding:10px;">No former players recorded.</div>'
    end
    
    -- STEP 2: Get Page Names
    local pageNames = {}
    for i, row in ipairs(history) do
        local pName = row.player_id
        if pName and pName ~= "" then table.insert(pageNames, "'" .. sqlEscape(pName) .. "'") end
    end
    
    -- STEP 3: Get Details
    local playerDetails = {}
    if #pageNames > 0 then
        local details = cargo.query("Players", "_pageName, real_name, id", { where = "_pageName IN (" .. table.concat(pageNames, ",") .. ")" })
        if details then
            for _, d in ipairs(details) do
                playerDetails[d._pageName] = { realName = d.real_name, shortID = d.id }
            end
        end
    end
    
    -- STEP 4: Render
    local tbl = html.create('table'):addClass('flat-data-table sortable')
    local h = tbl:tag('tr')
    h:tag('th'):wikitext('ID'); h:tag('th'):wikitext('Name'); h:tag('th'):wikitext('Role'); h:tag('th'):wikitext('Join Date'); h:tag('th'):wikitext('Leave Date')
    
    for _, row in ipairs(history) do
        local pName = row.player_id
        local details = playerDetails[pName] or {}
        local displayID = details.shortID
        if not displayID or displayID == "" then displayID = pName:gsub("^.*/", "") end
        
        local tr = tbl:tag('tr')
        tr:tag('td'):addClass('t-id'):wikitext('[[' .. pName .. '|' .. displayID .. ']]')
        tr:tag('td'):wikitext(details.realName or "-")
        
        local rCell = tr:tag('td')
        if row.role and row.role ~= "" then rCell:tag('span'):addClass('t-role'):wikitext(row.role) else rCell:wikitext("-") end
        
        tr:tag('td'):addClass('t-date'):wikitext(row.join_date or "?")
        tr:tag('td'):addClass('t-date'):wikitext(row.leave_date or "?")
    end
    
    return tostring(tbl)
end

-- ============================================================
-- MAIN 4: HISTORY
-- ============================================================
function p.history(frame)
    local args = getArgs(frame)
    local team = clean(args.team) or mw.title.getCurrentTitle().subpageText
    local results = cargo.query("StageStandings", "tournament, stage, totalpts, matchesplayed, wwcd, elimpts, lastmatchrank, result", {
        where = "team = '" .. sqlEscape(team) .. "'",
        orderBy = "tournament DESC",
        limit = 50
    })
    
    local root = html.create('div')
    root:wikitext('== Tournament Statistics ==')
    
    if #results > 0 then
        local tbl = root:tag('table'):addClass('modern-history-table')
        local thead = tbl:tag('thead'):tag('tr')
        thead:tag('th'):wikitext('Tournament'); thead:tag('th'):wikitext('Stage'); thead:tag('th'):wikitext('Rank')
        thead:tag('th'):wikitext('MP'); thead:tag('th'):wikitext('WWCD'); thead:tag('th'):wikitext('Elims'); thead:tag('th'):wikitext('Pts')
        
        for _, row in ipairs(results) do
            local tr = tbl:tag('tr')
            local res = (row.result or ""):lower()
            if res == "q" or res == "qualified" then tr:css('background-color', '#E9FFF0')
            elseif res == "e" or res == "eliminated" then tr:css('background-color', '#FFE9E9') end
            
            tr:tag('td'):css('font-weight','bold'):wikitext('[[' .. row.tournament .. ']]')
            tr:tag('td'):wikitext(row.stage)
            tr:tag('td'):css('font-weight','bold'):css('text-align','center'):wikitext('#' .. (row.lastmatchrank or '?'))
            tr:tag('td'):css('text-align','center'):wikitext(row.matchesplayed)
            tr:tag('td'):css('text-align','center'):wikitext(row.wwcd)
            tr:tag('td'):css('text-align','center'):wikitext(row.elimpts)
            tr:tag('td'):css('text-align','center'):css('font-weight','800'):wikitext(row.totalpts)
        end
    else root:wikitext("''No tournament data found for this team.''") end
    return tostring(root)
end

-- ============================================================
-- MAIN 5: TEAM ACHIEVEMENTS (Matches CSS & Logic)
-- ============================================================
function p.achievements(frame)
    local args = getArgs(frame)
    local team = clean(args.team) or currentTitle.subpageText
    
    -- STEP 1: Get Placements & Prizes
    local results = cargo.query(
        "PrizeMoney", 
        "tournament, placement, prize, award", 
        {
            where = "team = '" .. sqlEscape(team) .. "' AND (player='' OR player IS NULL)",
            orderBy = "prize DESC", 
            limit = 50
        }
    )
    
    local root = html.create('div')

    if not results or #results == 0 then
        root:wikitext('<div style="font-style:italic; color:#64748b; padding:10px;">No achievements recorded.</div>')
        return tostring(root)
    end
    
    -- STEP 2: Collect Tournament Names
    local tourneyNames = {}
    for _, row in ipairs(results) do
        if row.tournament and row.tournament ~= "" then
            table.insert(tourneyNames, "'" .. sqlEscape(row.tournament) .. "'")
        end
    end
    
    -- STEP 3: Fetch Date & Tier
    local tDetails = {}
    if #tourneyNames > 0 then
        local tResults = cargo.query("Tournaments", "name, end_date, tier", { where = "name IN (" .. table.concat(tourneyNames, ",") .. ")" })
        if tResults then
            for _, t in ipairs(tResults) do tDetails[t.name] = { date = t.end_date, tier = t.tier } end
        end
    end
    
    -- STEP 4: Render Table
    local tbl = root:tag('table'):addClass('achievements-table sortable')
    
    local h = tbl:tag('tr')
    h:tag('th'):wikitext('Date')
    h:tag('th'):wikitext('Tier')
    h:tag('th'):wikitext('Tournament')
    h:tag('th'):wikitext('Place')
    h:tag('th'):css('text-align', 'right'):wikitext('Prize')
    
    for _, row in ipairs(results) do
        local tr = tbl:tag('tr')
        local info = tDetails[row.tournament] or {}
        
        tr:tag('td'):addClass('ac-date'):wikitext(info.date or "-")
        
        -- UPDATED TIER CELL LOGIC (Using unified helper)
        local tierCell = tr:tag('td'):addClass('ac-tier')
        local tierClass = getTierClass(info.tier) -- Uses the new helper
        
        if info.tier and info.tier ~= "" then 
            tierCell:tag('span'):addClass('tier-badge ' .. tierClass):wikitext(info.tier) 
        else 
            tierCell:wikitext("-") 
        end
        
        tr:tag('td'):css('font-weight', '600'):wikitext('[[' .. row.tournament .. ']]')
        
        local pCell = tr:tag('td'):addClass('ac-place')
        local rank = tonumber(row.placement) or 99
        local badgeClass = "place-def"
        local placeText = row.placement or "-"
        if rank == 1 then badgeClass = "place-1"; placeText = "1st"
        elseif rank == 2 then badgeClass = "place-2"; placeText = "2nd"
        elseif rank == 3 then badgeClass = "place-3"; placeText = "3rd"
        elseif placeText:match("^%d+$") then placeText = placeText .. "th"
        end
        pCell:tag('span'):addClass('place-badge ' .. badgeClass):wikitext(placeText)
        
        tr:tag('td'):addClass('ac-prize'):wikitext(formatCurrency(row.prize))
    end
    
    return tostring(root)
end

return p