-- New Applicants!! - Core Logic
-- Version: 1.1.5

local ADDON_NAME = "NewApplicants"
local NA = _G[ADDON_NAME] or {}
_G[ADDON_NAME] = NA

-- Default Settings
local defaults = {
    position = { point = "CENTER", x = 0, y = 200 },
    maxDisplay = 3,
    soundEnabled = true,
    selectedSound = "xylophone",
    showCards = true,
    playOnRaid = true,
    showCardsOnRaid = false,
    soundOnlyActive = false,
    cardOnlyActive = false,
    customSoundPath = "",
    showSpecIcon = true,
    backgroundAlpha = 0.8,
    scale = 1.0,
    isLocked = false
}

-- Raid Abbreviations
NA.RAID_ID_ABBR = {
    [16340] = "VS/DR/MQD",
    --[16531] = "DR",
    --[16342] = "MQD",
    [16178] = "MFO",
    [15522] = "LOU", 
    [14980] = "NP",
}

-- Initialize
function NA:Initialize()
    NewApplicantsDB = NewApplicantsDB or {}
    self.db = NewApplicantsDB
    
    -- Merge defaults
    for k, v in pairs(defaults) do
        if self.db[k] == nil then
            if type(v) == "table" then
                self.db[k] = {}
                for k2, v2 in pairs(v) do
                    self.db[k][k2] = v2
                end
            else
                self.db[k] = v
            end
        end
    end
    
    self.applicantDataList = {}
    self.lastApplicantCount = 0
    
    self:RegisterEvents()
end

-- Register Events
function NA:RegisterEvents()
    local eventFrame = CreateFrame("Frame")
    eventFrame:RegisterEvent("LFG_LIST_APPLICANT_UPDATED")
    eventFrame:RegisterEvent("LFG_LIST_APPLICANT_LIST_UPDATED")
    eventFrame:RegisterEvent("PLAYER_ENTERING_WORLD")

    eventFrame:SetScript("OnEvent", function(_, event)
        if event == "LFG_LIST_APPLICANT_UPDATED" or event == "LFG_LIST_APPLICANT_LIST_UPDATED" then
            NA:UpdateApplicants()
        elseif event == "PLAYER_ENTERING_WORLD" then
            C_Timer.After(1, function()
                NA:UpdateApplicants()
            end)
        end
    end)
    
    self.eventFrame = eventFrame
end

-- Update Applicants
function NA:UpdateApplicants()
    local applicants = C_LFGList.GetApplicants() or {}
    self.applicantDataList = {}
    
    local activeEntryInfo = C_LFGList.GetActiveEntryInfo()
    local activityID = activeEntryInfo and activeEntryInfo.activityID
    
    for i, applicantID in ipairs(applicants) do
        local applicantInfo = C_LFGList.GetApplicantInfo(applicantID)
        if applicantInfo and applicantInfo.applicationStatus == "applied" then
            local data = self:ProcessApplicantData(applicantID, applicantInfo.numMembers, i, activityID)
            if data then
                table.insert(self.applicantDataList, data)
            end
        end
    end
    
    local isRaidListing = false
    if activeEntryInfo and activeEntryInfo.activityIDs and activeEntryInfo.activityIDs[1] then
        local activityInfo = C_LFGList.GetActivityInfoTable(activeEntryInfo.activityIDs[1])
        if activityInfo and activityInfo.isCurrentRaidActivity then
            isRaidListing = true
        end
    end
    self.isRaidListing = isRaidListing
    self.hasActiveListing = activeEntryInfo ~= nil and UnitIsGroupLeader("player")

    -- Play sound if new applicants
    local soundBlocked = isRaidListing and not self.db.playOnRaid
    local soundActiveBlocked = self.db.soundOnlyActive and not self.hasActiveListing
    if self.db.soundEnabled and not soundBlocked and not soundActiveBlocked and #self.applicantDataList > self.lastApplicantCount then
        NA:PlayRingSound()
    end
    
    self.lastApplicantCount = #self.applicantDataList
    
    -- Update UI if it exists
    if self.UpdateDisplay then
        self:UpdateDisplay()
    end
end

-- Process Applicant Data
function NA:ProcessApplicantData(applicantID, numMem, applicantIndex, activityID)
    local name, class, localizedClass, _, itemLevel, _, tank, healer, damage, _, relationship, dungeonScore, _, factionGroup, _, specID =
        C_LFGList.GetApplicantMemberInfo(applicantID, 1)
    
    if not name then return nil end
    
    local charName, serverName = strsplit("-", name)
    serverName = serverName or GetRealmName()
    local fullName = charName .. "-" .. serverName
    
    local blizzardScore = 0
    if activityID and applicantIndex then
        blizzardScore = C_LFGList.GetApplicantDungeonScoreForListing(applicantID, applicantIndex, activityID) or 0
    end
    
    local finalDungeonScore = math.max(dungeonScore or 0, blizzardScore)    
    local activeFaction = (factionGroup == 1) and "alliance" or "horde"
    
    -- Get RIO data
    local rioScore = self:GetRioScore(fullName, activeFaction, finalDungeonScore)
    local rioMaxDungeon, rioMaxLevel, rioMaxChests = self:GetMaxDungeonInfo(fullName, activeFaction)
    local rioRoles = self:GetRioRoles(fullName, activeFaction)
    local raidProgress = self:GetRaidProgress(fullName, activeFaction)
    
    -- Collect group members (memberIdx 2+)
    local groupMembers = nil
    if numMem and numMem > 1 then
        groupMembers = {}
        for mi = 2, numMem do
            local mName, mClass, _, _, mItemLevel, _, mTank, mHealer, mDamage, _, _, _, _, _, _, mSpecID =
                C_LFGList.GetApplicantMemberInfo(applicantID, mi)
            if mName then
                local mCharName = strsplit("-", mName)
                table.insert(groupMembers, {
                    charName  = mCharName,
                    class     = mClass,
                    specID    = mSpecID,
                    itemLevel = mItemLevel or 0,
                    tank      = mTank,
                    healer    = mHealer,
                    damage    = mDamage,
                })
            end
        end
    end

    return {
        applicantID = applicantID,
        charName = charName,
        serverName = serverName,
        fullName = fullName,
        class = class,
        localizedClass = localizedClass,
        specID = specID,
        itemLevel = itemLevel,
        numMembers = numMem,
        tank = tank,
        healer = healer,
        damage = damage,
        relationship = relationship,
        dungeonScore = dungeonScore,
        factionGroup = factionGroup,
        activeFaction = activeFaction,
        rioScore = rioScore,
        rioMaxDungeon = rioMaxDungeon,
        rioMaxLevel = rioMaxLevel,
        rioMaxChests = rioMaxChests,
        rioRoles = rioRoles,
        raidProgress = raidProgress,
        groupMembers = groupMembers,
    }
end

-- RaiderIO Functions
function NA:GetRioScore(fullName, activeFaction, dungeonScore)
    local score = dungeonScore or 0
    
    if not RaiderIO then
        return score
    end
    
    local playerProfile = RaiderIO.GetProfile(fullName, activeFaction)
    if playerProfile and playerProfile.mythicKeystoneProfile then
        local currentScore = playerProfile.mythicKeystoneProfile.currentScore or 0
        if currentScore > score then
            score = currentScore
        end
    end
    
    return score
end

function NA:GetMaxDungeonInfo(fullName, activeFaction)
    if not RaiderIO then
        return "N/A", "", ""
    end
    
    local playerProfile = RaiderIO.GetProfile(fullName, activeFaction)
    local mplusProfile = playerProfile and playerProfile.mythicKeystoneProfile
    
    if mplusProfile and mplusProfile.maxDungeon and mplusProfile.maxDungeonLevel then
        local maxDungeonData = mplusProfile.maxDungeon
        local maxLevel = mplusProfile.maxDungeonLevel
        
        if maxDungeonData.shortNameLocale and maxLevel > 0 then
            local chests = 0
            local sortedDungeons = mplusProfile.sortedDungeons or {}
            for _, run in ipairs(sortedDungeons) do
                if run.dungeon and run.dungeon.id == maxDungeonData.id and run.level == maxLevel then
                    chests = run.chests
                    break
                end
            end
            return maxDungeonData.shortNameLocale, maxLevel, chests
        end
    end
    
    return "N/A", "", ""
end

function NA:GetRioRoles(fullName, activeFaction)
    if not RaiderIO then return {} end
    
    local playerProfile = RaiderIO.GetProfile(fullName, activeFaction)
    local rioRoles = playerProfile and playerProfile.mythicKeystoneProfile 
        and playerProfile.mythicKeystoneProfile.mplusCurrent 
        and playerProfile.mythicKeystoneProfile.mplusCurrent.roles
    
    if type(rioRoles) ~= "table" then return {} end
    
    local specs = {}
    for i = 1, math.min(2, #rioRoles) do
        local roleInfo = rioRoles[i]
        if type(roleInfo) == "table" and roleInfo[1] and roleInfo[2] then
            table.insert(specs, { role = roleInfo[1], spec = roleInfo[2] })
        end
    end
    return specs
end

function NA:GetRaidProgress(fullName, activeFaction)
    if not RaiderIO then return nil end
    
    local progress = 0
    local difficulty = 0
    local bossCount = 0
    local raidId = nil
    
    local playerProfile = RaiderIO.GetProfile(fullName, activeFaction)
    if playerProfile and playerProfile.raidProfile and playerProfile.raidProfile.sortedProgress then
        local profileProgress = playerProfile.raidProfile.sortedProgress
        
        for i = 1, #profileProgress do
            if profileProgress[i].isMainProgress ~= true then
                local tempProg = profileProgress[i].progress
                
                local isSameDifficulty = tempProg.difficulty == difficulty
                local isProgUpgrade = tempProg.progressCount > progress and isSameDifficulty
                local isDiffUpgrade = tempProg.difficulty > difficulty
                local isNotObsolete = profileProgress[i].obsolete ~= true
                
                if (isProgUpgrade or isDiffUpgrade) and isNotObsolete then
                    progress = tempProg.progressCount or 0
                    bossCount = (tempProg.raid and tempProg.raid.bossCount) or 0
                    difficulty = tempProg.difficulty or 1
                    raidId = (tempProg.raid and (tempProg.raid.id or tempProg.raid.zoneId)) or tempProg.raidId
                end
            end
        end
    end
    
    if progress > 0 then
        return {
            progress = progress,
            bossCount = bossCount,
            difficulty = difficulty,
            raidId = raidId
        }
    end
    
    return nil
end

-- Specialization ID Lookup { name, icon, texCoord }
-- texCoord = { left, right, top, bottom } for New_Applicaants_classicon.png (2048x2048, 256x256 per icon, 8 cols)
-- index = row*8 + col (0-based), UV = index%8/8, (index%8+1)/8, floor(index/8)/8, (floor(index/8)+1)/8
local function _tc(idx)
    local col = idx % 8
    local row = math.floor(idx / 8)
    return { col/8, (col+1)/8, row/8, (row+1)/8 }
end

NA.SPEC_INFO = {
    -- Death Knight   row0: 0=Blood 1=Frost 2=Unholy
    [250] = { name = "Blood",        icon = 135770,   texCoord = _tc(0)  },
    [251] = { name = "Frost",        icon = 135773,   texCoord = _tc(1)  },
    [252] = { name = "Unholy",       icon = 135775,   texCoord = _tc(2)  },
    -- Druid          row0: 3=Balance 4=Feral 5=Guardian 6=Restoration
    [102] = { name = "Balance",      icon = 136096,   texCoord = _tc(3)  },
    [103] = { name = "Feral",        icon = 132115,   texCoord = _tc(4)  },
    [104] = { name = "Guardian",     icon = 132276,   texCoord = _tc(5)  },
    [105] = { name = "Restoration",  icon = 136041,   texCoord = _tc(6)  },
    -- Hunter         row0: 7=Beast Mastery  row1: 8=Marksmanship 9=Survival
    [253] = { name = "Beast Mastery",icon = 461112,   texCoord = _tc(7)  },
    [254] = { name = "Marksmanship", icon = 236179,   texCoord = _tc(8)  },
    [255] = { name = "Survival",     icon = 461113,   texCoord = _tc(9)  },
    -- Mage           row1: 10=Arcane 11=Fire 12=Frost
    [62]  = { name = "Arcane",       icon = 135932,   texCoord = _tc(10) },
    [63]  = { name = "Fire",         icon = 135810,   texCoord = _tc(11) },
    [64]  = { name = "Frost",        icon = 135846,   texCoord = _tc(12) },
    -- Monk           row1: 13=Brewmaster 14=Mistweaver 15=Windwalker
    [268] = { name = "Brewmaster",   icon = 608951,   texCoord = _tc(13) },
    [269] = { name = "Windwalker",   icon = 608952,   texCoord = _tc(15) },
    [270] = { name = "Mistweaver",   icon = 608953,   texCoord = _tc(14) },
    -- Paladin        row2: 16=Holy 17=Protection 18=Retribution
    [65]  = { name = "Holy",         icon = 135920,   texCoord = _tc(16) },
    [66]  = { name = "Protection",   icon = 236264,   texCoord = _tc(17) },
    [70]  = { name = "Retribution",  icon = 135873,   texCoord = _tc(18) },
    -- Priest         row2: 19=Discipline 20=Holy 21=Shadow
    [256] = { name = "Discipline",   icon = 135940,   texCoord = _tc(19) },
    [257] = { name = "Holy",         icon = 237542,   texCoord = _tc(20) },
    [258] = { name = "Shadow",       icon = 136207,   texCoord = _tc(21) },
    -- Rogue          row3: 24=Subtlety  row6: 48=Outlaw 49=Assassination
    [261] = { name = "Subtlety",     icon = 132320,   texCoord = _tc(24) },
    [260] = { name = "Outlaw",       icon = 236286,   texCoord = _tc(48) },
    [259] = { name = "Assassination",icon = 236270,   texCoord = _tc(49) },
    -- Shaman         row3: 25=Elemental 26=Enhancement 27=Restoration
    [262] = { name = "Elemental",    icon = 136048,   texCoord = _tc(25) },
    [263] = { name = "Enhancement",  icon = 237581,   texCoord = _tc(26) },
    [264] = { name = "Restoration",  icon = 136052,   texCoord = _tc(27) },
    -- Warlock        row3: 28=Affliction 29=Demonology 30=Destruction
    [265] = { name = "Affliction",   icon = 136145,   texCoord = _tc(28) },
    [266] = { name = "Demonology",   icon = 136172,   texCoord = _tc(29) },
    [267] = { name = "Destruction",  icon = 136186,   texCoord = _tc(30) },
    -- Warrior        row3: 31=Arms  row4: 32=Fury 33=Protection
    [71]  = { name = "Arms",         icon = 132355,   texCoord = _tc(31) },
    [72]  = { name = "Fury",         icon = 132347,   texCoord = _tc(32) },
    [73]  = { name = "Protection",   icon = 132341,   texCoord = _tc(33) },
    -- Demon Hunter   row4: 34=Havoc 35=Vengeance
    [577] = { name = "Havoc",        icon = 1247264,  texCoord = _tc(34) },
    [581] = { name = "Vengeance",    icon = 1247265,  texCoord = _tc(35) },
    -- Evoker         row4: 36=Devastation 37=Preservation 38=Augmentation  row5: 40=Devourer
    [1467]= { name = "Devastation",  icon = 4511811,  texCoord = _tc(36) },
    [1468]= { name = "Preservation", icon = 4511812,  texCoord = _tc(37) },
    [1473]= { name = "Augmentation", icon = 5198700,  texCoord = _tc(38) },
    [1480]= { name = "Devourer",     icon = 7455386,  texCoord = _tc(40) },
}

function NA:GetSpecName(specID)
    local info = self.SPEC_INFO[specID]
    return info and info.name or "Unknown"
end

function NA:GetSpecIcon(specID)
    local info = self.SPEC_INFO[specID]
    return info and info.icon or nil
end

function NA:GetSpecTexCoord(specID)
    local info = self.SPEC_INFO[specID]
    return info and info.texCoord or nil
end

-- Helper Functions
function NA:GetScoreColor(score)
    local r, g, b = 1, 1, 1
    if RaiderIO then
        r, g, b = RaiderIO.GetScoreColor(score)
    else
        local color = C_ChallengeMode.GetDungeonScoreRarityColor(score)
        if color then
            r, g, b = color:GetRGB()
        end
    end
    return r, g, b
end

function NA:GetDifficultyColor(difficulty)
    local colors = {
        [1] = { r = 0, g = 0.5, b = 1 },      -- Normal (blue)
        [2] = { r = 0.78, g = 0, b = 1 },     -- Heroic (purple)
        [3] = { r = 1, g = 0.5, b = 0 },      -- Mythic (orange)
    }
    return colors[difficulty] or { r = 1, g = 1, b = 1 }
end

function NA:GetDifficultyName(difficulty)
    local L = self.L or {}
    local names = {
        [1] = L["Normal"] or "N",
        [2] = L["Heroic"] or "H",
        [3] = L["Mythic"] or "M",
    }
    return names[difficulty] or (L["Unknown"] or "LFR")
end

function NA:GetRoleAtlas(spec, role)
    local alias = (spec or "") .. (role or "")
    local atlasMap = {
        fulltank = "UI-LFG-RoleIcon-Tank",
        fulldps = "UI-LFG-RoleIcon-DPS",
        fullhealer = "UI-LFG-RoleIcon-Healer",
        partialtank = "UI-LFG-RoleIcon-Tank-Disabled",
        partialdps = "UI-LFG-RoleIcon-DPS-Disabled",
        partialhealer = "UI-LFG-RoleIcon-Healer-Disabled",
    }
    return atlasMap[alias]
end

function NA:GetClassFrameTexture(class)
    local classLower = string.lower(class or "")
    local texturePath = "Interface\\AddOns\\NewApplicants\\Textures\\Frame_" .. classLower .. ".png"
    return texturePath
end

function NA:OpenLFGFrame()
    PVEFrame_ToggleFrame("GroupFinderFrame")
    
    local function gotoPremade()
        if GroupFinderFrame and GroupFinderFrameGroupButton3 and GroupFinderFrameGroupButton3:IsEnabled() then
            GroupFinderFrameGroupButton3:Click()
            return true
        end
        return false
    end
    
    if not gotoPremade() then
        local attempts = 0
        local ticker
        ticker = C_Timer.NewTicker(0.05, function()
            attempts = attempts + 1
            if gotoPremade() or attempts >= 20 then
                ticker:Cancel()
            end
        end)
    end
end

-- Test Data
function NA:LoadTestData()
    local function makeEntry(charName, serverName, class, localizedClass, ilvl, members, tank, healer, dmg, faction, rio, dung, lvl, chests, roles, raid, specID, groupMembers)
        return {
            applicantID = 0,
            charName = charName, serverName = serverName,
            fullName = charName .. "-" .. serverName,
            class = class, localizedClass = localizedClass,
            itemLevel = ilvl, numMembers = members,
            tank = tank, healer = healer, damage = dmg,
            factionGroup = faction == "horde" and 0 or 1,
            activeFaction = faction,
            rioScore = rio,
            rioMaxDungeon = dung, rioMaxLevel = lvl, rioMaxChests = chests,
            rioRoles = roles, raidProgress = raid,
            specID = specID,
            groupMembers = groupMembers,  -- additional members: { charName, class, specID, itemLevel, tank, healer, damage }
        }
    end
    self.applicantDataList = {
        makeEntry("TestDPS",      "Realm", "MAGE",         "Mage",         639.5, 1, false, false, true,  "horde",    3250, "GB",  12, 2, {{ spec="full", role="dps" }},  { progress=6, bossCount=8, difficulty=3, raidId=16340 }, 63,  nil),
        makeEntry("GroupLeader",  "Realm", "PALADIN",      "Paladin",      625.0, 3, true,  false, false, "alliance", 1870, "NW",  9,  1, {{ spec="full", role="tank" }}, { progress=3, bossCount=8, difficulty=2, raidId=16178 }, 66,
            {
                { charName="HealBot",   class="PRIEST",  specID=257, itemLevel=620, tank=false, healer=true,  damage=false },  -- Holy Priest
                { charName="BoomBoom",  class="DRUID",   specID=102, itemLevel=618, tank=false, healer=false, damage=true  },  -- Balance Druid
            }),
        makeEntry("LongNameGuy",  "AVeryLongServerName", "WARRIOR", "Warrior", 610.0, 4, false, false, true, "horde", 0, "N/A", 0, 0, {}, nil, 72,
            {
                { charName="SideKick",     class="ROGUE",   specID=259, itemLevel=605, tank=false, healer=false, damage=true  },
                { charName="Abcdefghijkl", class="MAGE",    specID=63,  itemLevel=610, tank=false, healer=false, damage=true  },
                { charName="Verylongname", class="PRIEST",  specID=257, itemLevel=608, tank=false, healer=true,  damage=false },
            }),
        makeEntry("HolyPriest",   "Realm", "PRIEST",       "Priest",       632.0, 1, false, true,  false, "alliance", 2100, "SV",  10, 2, {{ spec="full", role="healer" }}, { progress=5, bossCount=8, difficulty=3, raidId=16340 }, 257, nil),
        makeEntry("BoomkinDruid", "Realm", "DRUID",        "Druid",        618.0, 1, false, false, true,  "horde",    980,  "AA",  7,  1, {{ spec="full", role="dps" }},  { progress=2, bossCount=8, difficulty=2, raidId=16178 }, 102, nil),
        makeEntry("AssRogue",     "Realm", "ROGUE",        "Rogue",        644.0, 1, false, false, true,  "horde",    3800, "GB",  13, 3, {{ spec="full", role="dps" }},  { progress=8, bossCount=8, difficulty=3, raidId=16340 }, 259, nil),
        makeEntry("FrostDK",      "Realm", "DEATHKNIGHT",  "Death Knight", 628.0, 1, true,  false, false, "alliance", 1540, "BRH", 8,  1, {{ spec="full", role="tank" }}, { progress=4, bossCount=8, difficulty=2, raidId=16178 }, 250, nil),
        makeEntry("EleShaman",    "Realm", "SHAMAN",       "Shaman",       635.0, 1, false, false, true,  "horde",    2750, "NW",  11, 2, {{ spec="full", role="dps" }},  { progress=7, bossCount=8, difficulty=3, raidId=16340 }, 262, nil),
        makeEntry("AfflWarlock",  "Realm", "WARLOCK",      "Warlock",      621.0, 1, false, false, true,  "alliance", 1200, "SV",  8,  1, {{ spec="full", role="dps" }},  { progress=3, bossCount=8, difficulty=2, raidId=16178 }, 265, nil),
        makeEntry("BeastHunter",  "Realm", "HUNTER",       "Hunter",       641.0, 1, false, false, true,  "horde",    3100, "GB",  12, 2, {{ spec="full", role="dps" }},  { progress=6, bossCount=8, difficulty=3, raidId=16340 }, 253, nil),
    }
    self.isRaidListing = false
    if self.UpdateDisplay then
        self:UpdateDisplay()
    end

    local testSounds = { "Xylophone (Default)", "Alarm Bell", "Chime", "Gong", "Whistle",
                         "Drum Beat", "Fanfare", "Horn", "Ping", "Thunder" }
    self.testSoundList = {}
    for i, name in ipairs(testSounds) do
        self.testSoundList[i] = { id = name, label = name, path = "" }
    end
    print("|cFFFF6B6BNew Applicants!!|r Test card is loaded.")
end

-- Slash commands
SLASH_NEWAPPLICANT1 = "/na"
SlashCmdList["NEWAPPLICANT"] = function(msg)
    msg = string.lower(msg or "")
    if msg == "test" then
        NA:LoadTestData()
    else
        NA:ShowSettingsPanel()
    end
end

-- Initialize on load
local initFrame = CreateFrame("Frame")
initFrame:RegisterEvent("ADDON_LOADED")
initFrame:SetScript("OnEvent", function(self, _, addon)
    if addon == ADDON_NAME then
        NA:Initialize()
        self:UnregisterEvent("ADDON_LOADED")
    end
end)
