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:PlayerDashboard/doc

local p     = {}
local cargo = mw.ext.cargo
local html  = mw.html
local lang  = mw.getContentLanguage()

-- ── Helpers ──────────────────────────────────────────────────────

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

local function fmtCurrency(n)
    if not n or n == 0 then return "—" end
    local num = math.floor(tonumber(n) or 0)
    local s   = tostring(num)
    if #s <= 3 then return "₹ " .. s end
    local result    = s:sub(-3)
    local remaining = s:sub(1, -4)
    while #remaining > 2 do
        result    = remaining:sub(-2) .. "," .. result
        remaining = remaining:sub(1, -3)
    end
    return "₹ " .. remaining .. "," .. result
end

local function fmtDate(d)
    if not d or d == "" then return "?" end
    return lang:formatDate("M y", d)
end

local function getTierClass(tier)
    if not tier then return "tier-def" end
    local t = tier:lower()
    if t:find("s") and t:find("tier") then return "tier-s" end
    if t:find("a") and t:find("tier") then return "tier-a" end
    if t:find("b") and t:find("tier") then return "tier-b" end
    if t:find("c") and t:find("tier") then return "tier-c" end
    return "tier-def"
end

local FLAGS = {
    india="🇮🇳", bangladesh="🇧🇩", pakistan="🇵🇰", nepal="🇳🇵",
    srilanka="🇱🇰", myanmar="🇲🇲", indonesia="🇮🇩", thailand="🇹🇭",
    malaysia="🇲🇾", philippines="🇵🇭", vietnam="🇻🇳", singapore="🇸🇬",
    china="🇨🇳", japan="🇯🇵", korea="🇰🇷", usa="🇺🇸",
    uk="🇬🇧", germany="🇩🇪", brazil="🇧🇷", australia="🇦🇺"
}
local function getFlag(country)
    if not country or country == "" then return "" end
    local key = country:lower():gsub("%s+",""):gsub("-","")
    return (FLAGS[key] or "") .. " "
end

local function statusStyle(s)
    s = (s or ""):lower()
    if s == "active"   then return "#4ade80","rgba(34,197,94,.2)",  "rgba(34,197,94,.35)"   end
    if s == "inactive" then return "#fbbf24","rgba(251,191,36,.2)", "rgba(251,191,36,.35)"  end
    if s == "retired"  then return "#94a3b8","rgba(148,163,184,.2)","rgba(148,163,184,.35)" end
    if s == "banned"   then return "#f87171","rgba(239,68,68,.2)",  "rgba(239,68,68,.35)"   end
    return "#94a3b8","rgba(148,163,184,.2)","rgba(148,163,184,.35)"
end

-- ── Team info + logo helpers ──────────────────────────────────────

local teamInfoCache = {}
local function getTeamInfo(teamName)
    if not teamName or teamName == "" then return nil end
    if teamInfoCache[teamName] then return teamInfoCache[teamName] end
    local rows = cargo.query("Teams",
        "display_name,image,image_dark",
        { where = "name='" .. esc(teamName) .. "'", limit = 1 })
    local result = (rows and #rows > 0) and rows[1] or nil
    teamInfoCache[teamName] = result
    return result
end

-- Outputs both light + dark logo, CSS switches via logo-lightmode/logo-darkmode
local function teamLogoWikitext(ti, size)
    if not ti then return nil end
    size = size or "26px"
    local light = (ti.image      and ti.image      ~= "") and ti.image      or nil
    local dark  = (ti.image_dark and ti.image_dark ~= "") and ti.image_dark or light
    if not light then return nil end
    return "[[File:"..light.."|"..size.."|link=|class=pd-team-logo-img logo-lightmode]]"
        .. "[[File:"..dark .."|"..size.."|link=|class=pd-team-logo-img logo-darkmode]]"
end

-- Place badge matching achievements table style
local function renderPlaceBadge(td, placeNum)
    local badgeClass = "place-def"
    local placeText  = tostring(placeNum) .. "th"
    if placeNum == 1 then badgeClass = "place-1"; placeText = "1st"
    elseif placeNum == 2 then badgeClass = "place-2"; placeText = "2nd"
    elseif placeNum == 3 then badgeClass = "place-3"; placeText = "3rd"
    end
    td:tag('span'):addClass('place-badge ' .. badgeClass):wikitext(placeText)
end

-- ── Main ─────────────────────────────────────────────────────────

function p.main(frame)
    local pageName = mw.title.getCurrentTitle().text
    local sqlPage  = esc(pageName)

    -- ── 1. Player row ─────────────────────────────────────────────
    local pRows = cargo.query("Players",
        "id,real_name,image,current_team,nationality,status,game,"
        .. "birth_date,role,instagram,youtube,twitter,discord,facebook",
        { where = "_pageName='" .. sqlPage .. "'", limit = 1 })
    local pl          = (pRows and #pRows > 0) and pRows[1] or {}
    local displayName = pl.id or mw.title.getCurrentTitle().subpageText
    local sqlName     = esc(displayName)

    -- ── 2. Krafton rank ───────────────────────────────────────────
    local rankVal = nil
    local rRows = cargo.query("Krafton_Rankings", "rank", {
        where = string.format(
            "(name='%s' OR name='%s') AND type='Player'", sqlPage, sqlName),
        limit = 1 })
    if rRows and #rRows > 0 then rankVal = "#" .. rRows[1].rank end

    -- ── 3. All tournaments player appeared in ─────────────────────
    local participation = cargo.query("Tournament_Teams",
        "tournament,team,p1,p2,p3,p4,p5,p6",
        { where = string.format(
            "p1='%s' OR p1='%s' OR p2='%s' OR p2='%s' OR "
            .."p3='%s' OR p3='%s' OR p4='%s' OR p4='%s' OR "
            .."p5='%s' OR p5='%s' OR p6='%s' OR p6='%s'",
            sqlPage,sqlName, sqlPage,sqlName, sqlPage,sqlName,
            sqlPage,sqlName, sqlPage,sqlName, sqlPage,sqlName),
        limit = 500 })
    participation = participation or {}

    -- Tournament -> team lookup (for individual awards team column)
    local tournTeamMap = {}
    for _, r in ipairs(participation) do
        if r.tournament and r.team and not tournTeamMap[r.tournament] then
            tournTeamMap[r.tournament] = r.team
        end
    end

    -- ── 4. Teammates ──────────────────────────────────────────────
    -- Count how many tournaments each co-player appeared in with this player
    -- Skip coach/analyst — only p1-p6. Skip the player himself.
    local teammateCount = {}
    local slots = {"p1","p2","p3","p4","p5","p6"}
    for _, r in ipairs(participation) do
        for _, slot in ipairs(slots) do
            local name = r[slot]
            if name and name ~= "" and name ~= pageName and name ~= displayName then
                teammateCount[name] = (teammateCount[name] or 0) + 1
            end
        end
    end

    -- Sort teammates by frequency desc, take top 10
    local teammates = {}
    for name, count in pairs(teammateCount) do
        table.insert(teammates, { name = name, count = count })
    end
    table.sort(teammates, function(a, b) return a.count > b.count end)
    if #teammates > 10 then
        local trimmed = {}
        for i = 1, 10 do trimmed[i] = teammates[i] end
        teammates = trimmed
    end

    -- ── 5. Earnings ───────────────────────────────────────────────
    local indEarnings, teamEarnings = 0, 0

    local eRows = cargo.query("PrizeMoney", "SUM(prize)=total", {
        where = string.format(
            "(player='%s' OR player='%s') AND player != ''", sqlPage, sqlName) })
    if eRows and #eRows > 0 then
        indEarnings = tonumber(eRows[1].total) or 0
    end

    if #participation > 0 then
        local conds = {}
        for _, r in ipairs(participation) do
            if r.tournament and r.team then
                table.insert(conds, string.format(
                    "(tournament='%s' AND team='%s')",
                    esc(r.tournament), esc(r.team)))
            end
        end
        if #conds > 0 then
            local tPrizes = cargo.query("PrizeMoney", "SUM(prize)=team_total", {
                where = "(" .. table.concat(conds, " OR ") .. ")"
                    .. " AND (player='' OR player IS NULL)" })
            if tPrizes and #tPrizes > 0 then
                teamEarnings = tonumber(tPrizes[1].team_total) or 0
            end
        end
    end
    local totalEarnings = indEarnings + teamEarnings

    -- ── 6. Team history ───────────────────────────────────────────
    local teamHist = cargo.query("Player_Former_Teams",
        "team,join_date,leave_date",
        { where = "player_id='" .. sqlPage .. "'",
          orderBy = "join_date DESC", limit = 20 })
    if not teamHist or #teamHist == 0 then
        teamHist = cargo.query("Player_Former_Teams",
            "team,join_date,leave_date",
            { where = "player_id='" .. sqlName .. "'",
              orderBy = "join_date DESC", limit = 20 })
    end
    teamHist = teamHist or {}

    -- ── 7a. Individual awards ─────────────────────────────────────
    local indAwards = cargo.query("PrizeMoney",
        "tournament,award,prize",
        { where = string.format(
            "(player='%s' OR player='%s') AND player != ''",
            sqlPage, sqlName),
          orderBy = "tournament DESC", limit = 100 })
    indAwards = indAwards or {}

    -- ── 7b. Team tournament results ───────────────────────────────
    local teamResults = {}
    local seenKey     = {}

    if #participation > 0 then
        local pairs_list = {}
        for _, r in ipairs(participation) do
            if r.tournament and r.team then
                local k = r.tournament .. "|" .. r.team
                if not seenKey[k] then
                    seenKey[k] = true
                    table.insert(pairs_list, { tournament = r.tournament, team = r.team })
                end
            end
        end

        if #pairs_list > 0 then
            local ttConds = {}
            for _, r in ipairs(pairs_list) do
                table.insert(ttConds, string.format(
                    "(tournament='%s' AND team='%s')",
                    esc(r.tournament), esc(r.team)))
            end
            local whereStr = table.concat(ttConds, " OR ")

            local placeIndex = {}
            local placeRows  = cargo.query("Tournament_Results",
                "tournament,team,place",
                { where = whereStr, limit = 500 })
            if placeRows then
                for _, row in ipairs(placeRows) do
                    placeIndex[row.tournament .. "|" .. row.team] = tonumber(row.place)
                end
            end

            local prizeIndex = {}
            local prizeRows  = cargo.query("PrizeMoney",
                "tournament,team,prize,placement,award",
                { where = "(" .. whereStr .. ")"
                    .. " AND (player='' OR player IS NULL)",
                  limit = 500 })
            if prizeRows then
                for _, row in ipairs(prizeRows) do
                    local k        = row.tournament .. "|" .. row.team
                    local newPrize = tonumber(row.prize) or 0
                    local existing = prizeIndex[k]
                    if not existing or newPrize > (existing.prize or 0) then
                        prizeIndex[k] = {
                            prize     = newPrize,
                            placement = row.placement or "",
                            award     = row.award     or "",
                        }
                    end
                end
            end

            for _, r in ipairs(pairs_list) do
                local k      = r.tournament .. "|" .. r.team
                local pr     = prizeIndex[k]
                local pl_num = placeIndex[k]
                if not pl_num and pr and pr.placement ~= "" then
                    local n = pr.placement:match("(%d+)")
                    if n then pl_num = tonumber(n) end
                end
                table.insert(teamResults, {
                    tournament = r.tournament,
                    team       = r.team,
                    place      = pl_num,
                    award      = (pr and pr.award ~= "" and pr.award) or nil,
                    prize      = (pr and pr.prize  > 0  and pr.prize) or 0,
                })
            end
        end
    end

    table.sort(teamResults, function(a, b)
        if a.place and b.place then return a.place < b.place end
        if a.place  then return true  end
        if b.place  then return false end
        return (a.tournament or "") > (b.tournament or "")
    end)

    -- ── 8. Tournament metadata ────────────────────────────────────
    local allTournNames = {}
    local tournSeen     = {}
    local function addTourn(name)
        if name and name ~= "" and not tournSeen[name] then
            tournSeen[name] = true
            table.insert(allTournNames, "'" .. esc(name) .. "'")
        end
    end
    for _, a in ipairs(indAwards)   do addTourn(a.tournament) end
    for _, t in ipairs(teamResults) do addTourn(t.tournament) end

    local tournMeta = {}
    if #allTournNames > 0 then
        local metaRows = cargo.query("Tournaments", "name,tier,end_date", {
            where = "name IN (" .. table.concat(allTournNames, ",") .. ")",
            limit = 200 })
        if metaRows then
            for _, row in ipairs(metaRows) do
                tournMeta[row.name] = { tier = row.tier, date = row.end_date }
            end
        end
    end

    -- ── BUILD HTML ────────────────────────────────────────────────

    local sc, sbg, sborder = statusStyle(pl.status)
    local initials = displayName:sub(1, 2):upper()
    local root     = html.create('div'):addClass('pd-dashboard')

    -- ════ HERO ════
    -- Layout: [photo] [name + realname + tags] [earnings — hidden on mobile]
    -- Krafton rank is NO LONGER here — moved to left info panel

    local hero = root:tag('div'):addClass('pd-hero')

    -- Photo
    local photo = hero:tag('div'):addClass('pd-hero-photo')
    if pl.image and pl.image ~= "" then
        photo:wikitext("[[File:" .. pl.image .. "|120px|link=|class=pd-hero-img]]")
    else
        photo:wikitext(initials)
    end

    -- Name + tags (no rank chip here anymore)
    local heroInfo = hero:tag('div'):addClass('pd-hero-info')
    heroInfo:tag('div'):addClass('pd-hero-name'):wikitext(displayName)
    if pl.real_name and pl.real_name ~= "" then
        heroInfo:tag('div'):addClass('pd-hero-realname'):wikitext(pl.real_name)
    end
    local tags = heroInfo:tag('div'):addClass('pd-hero-tags')
    if pl.status and pl.status ~= "" then
        tags:tag('span'):addClass('pd-tag')
            :css('color', sc):css('background', sbg)
            :css('border', '1px solid ' .. sborder)
            :wikitext('● ' .. pl.status)
    end
    if pl.role and pl.role ~= "" then
        tags:tag('span'):addClass('pd-tag pd-tag-role'):wikitext(pl.role)
    end
    if pl.game and pl.game ~= "" then
        tags:tag('span'):addClass('pd-tag pd-tag-game'):wikitext(pl.game)
    end

    -- Earnings: right side on desktop, separate row on mobile via CSS
    -- pd-earnings-total wraps label+number so flex can split total vs breakdown
    if totalEarnings > 0 then
        local er = hero:tag('div'):addClass('pd-hero-right')
        local et = er:tag('div'):addClass('pd-earnings-total')
        et:tag('div'):addClass('pd-earnings-label'):wikitext('Approx. Earnings')
        et:tag('div'):addClass('pd-earnings-val'):wikitext(fmtCurrency(totalEarnings))
        local bd = er:tag('div'):addClass('pd-earnings-breakdown')
        bd:tag('div')
            :tag('div'):addClass('pd-earn-sub-label'):wikitext('Team'):done()
            :tag('div'):addClass('pd-earn-sub-val'):wikitext(fmtCurrency(teamEarnings))
        bd:tag('div')
            :tag('div'):addClass('pd-earn-sub-label'):wikitext('Individual'):done()
            :tag('div'):addClass('pd-earn-sub-val'):wikitext(fmtCurrency(indEarnings))
    end

    -- ════ BODY ════

    local body = root:tag('div'):addClass('pd-body')
    local left = body:tag('div'):addClass('pd-left')
    left:tag('div'):addClass('pd-section-title'):wikitext('Player Info')

    local function infoRow(label, val)
        if not val or val == "" then return end
        left:tag('div'):addClass('pd-info-row')
            :tag('span'):addClass('pd-info-label'):wikitext(label):done()
            :tag('span'):addClass('pd-info-val'):wikitext(val)
    end

    infoRow('Real Name', pl.real_name)
    if pl.birth_date and pl.birth_date ~= "" then
        infoRow('Born', lang:formatDate("d F Y", pl.birth_date))
    end
    if pl.nationality and pl.nationality ~= "" then
        infoRow('Nationality', getFlag(pl.nationality) .. pl.nationality)
    end
    infoRow('Role', pl.role)
    infoRow('Game', pl.game)

    -- Current team: logo + display_name
    if pl.current_team and pl.current_team ~= "" then
        local ti          = getTeamInfo(pl.current_team)
        local teamDisplay = (ti and ti.display_name and ti.display_name ~= "")
            and ti.display_name or pl.current_team
        local logoHtml    = teamLogoWikitext(ti, "22px")
        local row         = left:tag('div'):addClass('pd-info-row')
        row:tag('span'):addClass('pd-info-label'):wikitext('Current Team')
        local wrap = row:tag('span'):addClass('pd-info-val')
            :tag('span'):addClass('pd-current-team')
        if logoHtml then wrap:wikitext(logoHtml) end
        wrap:tag('span'):addClass('pd-current-team-name')
            :wikitext("[[" .. pl.current_team .. "|" .. teamDisplay .. "]]")
    end

    -- Krafton rank now sits naturally in the info panel
    if rankVal then infoRow('Krafton Rank', rankVal) end

    -- Socials
    local socials = {
        { k='instagram', file='Icon_instagram.png', base='https://instagram.com/' },
        { k='twitter',   file='Icon_twitter.png',   base='https://twitter.com/'   },
        { k='youtube',   file='Icon_youtube.png',   base='https://youtube.com/'   },
        { k='discord',   file='Icon_discord.png',   base='https://discord.gg/'    },
        { k='facebook',  file='Icon_facebook.png',  base='https://facebook.com/'  },
    }
    local socDiv = left:tag('div'):addClass('pd-socials')
    for _, s in ipairs(socials) do
        local v = pl[s.k]
        if v and v ~= "" then
            local url = v:match("^https?://") and v or (s.base .. v)
            socDiv:wikitext("[[File:" .. s.file
                .. "|32px|link=" .. url .. "|class=social-img]]")
        end
    end

    -- Team history
    if #teamHist > 0 then
        left:tag('div'):addClass('pd-section-title pd-section-gap')
            :wikitext('Team History')
        for _, h in ipairs(teamHist) do
            local ti       = getTeamInfo(h.team or "")
            local logoHtml = teamLogoWikitext(ti, "24px")
            local tr       = left:tag('div'):addClass('pd-team-row')
            if logoHtml then
                tr:wikitext(logoHtml)
            else
                tr:tag('div'):addClass('pd-team-logo-fb')
                    :wikitext((h.team or "?"):sub(1, 3):upper())
            end
            tr:tag('div'):addClass('pd-team-name')
                :wikitext("[[" .. (h.team or "") .. "]]")
            tr:tag('div'):addClass('pd-team-dates')
                :wikitext(fmtDate(h.join_date) .. " – "
                    .. (h.leave_date and h.leave_date ~= ""
                        and fmtDate(h.leave_date) or "Now"))
        end
    end

    -- ── Teammates section ─────────────────────────────────────────
    -- Players who shared the most tournaments with this player
    -- Source: Tournament_Teams p1-p6, coach/analyst excluded
    if #teammates > 0 then
        left:tag('div'):addClass('pd-section-title pd-section-gap')
            :wikitext('Played With')
        for _, tm in ipairs(teammates) do
            local tr = left:tag('div'):addClass('pd-team-row')
            tr:tag('div'):addClass('pd-teammate-name')
                :wikitext("[[" .. tm.name .. "]]")
            tr:tag('div'):addClass('pd-teammate-count')
                :wikitext(tostring(tm.count)
                    .. (tm.count == 1 and " tournament" or " tournaments"))
        end
    end

    -- ════ RIGHT PANEL ════

    local right = body:tag('div'):addClass('pd-right')

    local function renderTierCell(td, tier)
        if tier and tier ~= "" then
            td:addClass('ac-tier')
              :tag('span'):addClass('tier-badge ' .. getTierClass(tier)):wikitext(tier)
        else
            td:wikitext("—")
        end
    end

    local function renderDateCell(td, date)
        td:addClass('ac-date'):wikitext(date and date ~= "" and date or "—")
    end

    -- Individual Awards
    if #indAwards > 0 then
        local sec = right:tag('div'):addClass('pd-prize-section')
        sec:tag('div'):addClass('pd-table-title'):wikitext('🏆 Individual Awards')
        local tbl = sec:tag('table'):addClass('pd-tourn-table')
        local hdr = tbl:tag('tr')
        hdr:tag('th'):wikitext('Date')
        hdr:tag('th'):wikitext('Tier')
        hdr:tag('th'):wikitext('Tournament')
        hdr:tag('th'):wikitext('Team')
        hdr:tag('th'):wikitext('Award')
        hdr:tag('th'):css('text-align','right'):wikitext('Prize')

        for _, a in ipairs(indAwards) do
            local meta = tournMeta[a.tournament or ""] or {}
            local tr   = tbl:tag('tr')
            renderDateCell(tr:tag('td'), meta.date)
            renderTierCell(tr:tag('td'), meta.tier)
            tr:tag('td'):css('font-weight','600')
                :wikitext("[[" .. (a.tournament or "") .. "]]")
            local playerTeam = tournTeamMap[a.tournament or ""]
            tr:tag('td'):wikitext(playerTeam and ("[[" .. playerTeam .. "]]") or "—")
            tr:tag('td'):tag('span'):addClass('pd-award-badge')
                :wikitext(a.award and a.award ~= "" and a.award or "Award")
            tr:tag('td'):addClass('ac-prize')
                :wikitext(tonumber(a.prize) and tonumber(a.prize) > 0
                    and fmtCurrency(tonumber(a.prize)) or "—")
        end
    end

    -- Team Tournament Results
    local sec2 = right:tag('div'):addClass('pd-prize-section')
    sec2:tag('div'):addClass('pd-table-title'):wikitext('🎖 Team Tournament Results')

    if #teamResults > 0 then
        local tbl2 = sec2:tag('table'):addClass('pd-tourn-table')
        local hdr2 = tbl2:tag('tr')
        hdr2:tag('th'):wikitext('Date')
        hdr2:tag('th'):wikitext('Tier')
        hdr2:tag('th'):wikitext('Tournament')
        hdr2:tag('th'):wikitext('Team')
        hdr2:tag('th'):addClass('ac-place'):wikitext('Place/Award')
        hdr2:tag('th'):css('text-align','right'):wikitext('Prize')

        for _, t in ipairs(teamResults) do
            local meta = tournMeta[t.tournament or ""] or {}
            local tr2  = tbl2:tag('tr')
            renderDateCell(tr2:tag('td'), meta.date)
            renderTierCell(tr2:tag('td'), meta.tier)
            tr2:tag('td'):css('font-weight','600')
                :wikitext("[[" .. t.tournament .. "]]")
            tr2:tag('td'):wikitext("[[" .. t.team .. "]]")

            local pTd = tr2:tag('td'):addClass('ac-place')
            if t.place then
                renderPlaceBadge(pTd, t.place)
            elseif t.award then
                pTd:tag('span'):addClass('pd-award-badge'):wikitext(t.award)
            else
                pTd:wikitext("—")
            end

            tr2:tag('td'):addClass('ac-prize')
                :wikitext(t.prize > 0 and fmtCurrency(t.prize) or "—")
        end
    else
        sec2:tag('div'):addClass('pd-empty')
            :wikitext('No tournament data found.')
    end

    right:tag('div'):addClass('pd-stats-placeholder')
        :tag('b'):wikitext('📊 Performance Stats'):done()
        :wikitext('Coming soon — per-match stats will appear here.')

    return tostring(root)
end

return p