--[[
============================================================================
Enhanced Pet Tracker  -  Modules/SourceInfo.lua
============================================================================
Turns a PetData record into the human-readable "how do I get this pet"
shown in the detail panel and tooltip.

SourceInfo:Describe(speciesID, rec) -> table:
  headline   short source label
  howto      source-aware action sentence
  dropRate   "10.0%" / "Guaranteed" / nil
  rateKnown  boolean
  hasLocation boolean   (a map waypoint is available)
  removed    boolean
  removedText string or nil
============================================================================
]]--

local ADDON_NAME, NS = ...
local EPT = NS.EPT
if not EPT then return end

EPT.SourceInfo = EPT.SourceInfo or {}
local SI = EPT.SourceInfo

local HEADLINE = {
    B = "Boss Drop",    R = "Rare Creature",  V = "Vendor",
    P = "PvP Reward",   C = "Crafted",        F = "Reputation Reward",
    H = "World Event",  K = "Black Market",   W = "Wild Pet",
    M = "Promotional",  Q = "Quest",          A = "Achievement",
    ["?"] = "Special",
}

-- v15.40.0: zones a wild pet is found in. ETB.ZonePets is keyed
-- [uiMapID]={[speciesID]=packedXY}; we invert it once to speciesID -> {mapIDs}
-- and resolve names via C_Map.GetMapInfo. Falls back to PetData's single
-- capture map (rec.cm) when ZonePets has no entry.
local _wildZones  -- lazy reverse index
local function buildWildZoneIndex()
    _wildZones = {}
    local ETB = NS.ETB
    local zp = ETB and ETB.ZonePets
    if not zp then return end
    if ETB._ZonePetsLoader then local _ = zp[0] end   -- trigger lazy load (reassigns ETB.ZonePets)
    zp = ETB.ZonePets
    for mapID, species in pairs(zp) do
        if type(species) == "table" then
            for sid in pairs(species) do
                local t = _wildZones[sid]
                if not t then t = {}; _wildZones[sid] = t end
                t[#t + 1] = mapID
            end
        end
    end
end
local function wildZoneNames(speciesID, fallbackMap)
    if not _wildZones then buildWildZoneIndex() end
    local maps = _wildZones[speciesID]
    if (not maps or #maps == 0) and fallbackMap then maps = { fallbackMap } end
    if not maps or #maps == 0 then return nil end
    local seen, names = {}, {}
    for i = 1, #maps do
        local info = C_Map and C_Map.GetMapInfo and C_Map.GetMapInfo(maps[i])
        local nm = info and info.name
        if nm and nm ~= "" and not seen[nm] then seen[nm] = true; names[#names + 1] = nm end
    end
    if #names == 0 then return nil end
    table.sort(names)
    local shown = {}
    for i = 1, math.min(5, #names) do shown[i] = names[i] end
    local txt = table.concat(shown, ", ")
    if #names > 5 then txt = txt .. string.format(", +%d more", #names - 5) end
    return txt
end

local function howToGet(speciesID, rec)
    local src = rec.src or "?"
    if rec.u == 3 then return "Available from the in-game Store" end
    local NPC = EPT.NPCNames

    if src == "W" then
        local zones = wildZoneNames(speciesID, rec.cm)
        if zones then return "Found in the wild — catch it in: " .. zones end
        return "Found in the wild — catch it in a pet battle"
    elseif src == "B" then
        local boss = rec.n and NPC and NPC:Get(rec.n)
        return boss and ("Drops from " .. boss)
            or "Drops from a dungeon or raid boss"
    elseif src == "R" then
        local mob = rec.n and NPC and NPC:Get(rec.n)
        return mob and ("Drops from " .. mob)
            or "Drops from a rare creature"
    elseif src == "V" then
        return "Purchased from a vendor"
    elseif src == "P" then
        return "Earned through PvP"
    elseif src == "C" then
        return "Crafted by a profession"
    elseif src == "F" then
        return "Earned through faction reputation"
    elseif src == "H" then
        return "Obtained during a holiday or world event"
    elseif src == "K" then
        return "Bought from the Black Market Auction House"
    elseif src == "M" then
        return "From a promotion, the in-game shop, or the TCG"
    elseif src == "Q" then
        return "Earned by completing a quest"
    elseif src == "A" then
        return "Awarded for completing an achievement"
    end
    return "Source varies"
end

-- Some pets have a primary source (vendor, boss, etc.) but also rotate onto
-- the Black Market Auction House. rec.bm flags those; we append a note rather
-- than relabel the primary source.
local function bmahNote(rec)
    if rec.bm then return "Also appears on the Black Market Auction House" end
    return nil
end

local function dropRate(rec)
    if not rec.dr then return nil, false end
    if rec.dr >= 100 then return "Guaranteed drop", true end
    return string.format("%.2f%% drop chance", rec.dr), true
end

function SI:Describe(speciesID, rec)
    if not rec then return nil end
    local rateText, rateKnown = dropRate(rec)
    return {
        headline    = HEADLINE[rec.src or "?"] or "Special",
        howto       = howToGet(speciesID, rec),
        bmah        = bmahNote(rec),
        dropRate    = rateText,
        rateKnown   = rateKnown,
        hasLocation = (rec.cm ~= nil and rec.cx ~= nil),
        removed     = (rec.u == 1 or rec.u == 2),
        removedText = (rec.u == 2 and "No longer obtainable \226\128\148 removed from the game")
            or (rec.u == 1 and "Not implemented \226\128\148 cannot be obtained") or nil,
    }
end

function SI:ShortLine(speciesID, rec)
    if not rec then return "" end
    if rec.u == 1 or rec.u == 2 then return "|cffff8800removed|r" end
    local tag = self:SourceTag(rec)
    if rec.dr then
        return tag .. " |cffaaaaaa" .. self:FormatRate(rec.dr) .. "|r"
    end
    return tag
end

-- Short, colour-coded source label shown on every row and tooltip line so the
-- source (Wild / Boss / Vendor / Crafted / Store / ...) is unmistakable at a
-- glance, independent of the grouping header.
local TAG = {
    B = "|cffff7777Boss|r",   R = "|cffffbb55Rare|r",    V = "|cff77dd77Vendor|r",
    P = "|cff7799ffPvP|r",    C = "|cffffaa44Craft|r",   F = "|cff55ccffRep|r",
    H = "|cffff88ccEvent|r",  K = "|cffbb88ffBMAH|r",    W = "|cff88dd88Wild|r",
    M = "|cff66ccffStore|r",  Q = "|cffffdd55Quest|r",   A = "|cffffcc00Achiev|r",
    ["?"] = "|cff999999Other|r",
}
function SI:SourceTag(rec)
    if rec and rec.u == 3 then return TAG.M end   -- store/real-money
    return TAG[(rec and rec.src) or "?"] or TAG["?"]
end

-- Drop-rate text: keeps precision for the very rare ones (a 1-in-5000 world
-- drop is 0.02%%, not "0.0%%").
function SI:FormatRate(dr)
    if not dr then return nil end
    if dr >= 100 then return "100%" end
    if dr >= 1   then return string.format("%.1f%%", dr) end
    return string.format("%.2f%%", dr)
end
