--[[ Core/ChatLinks.lua - blueprint hyperlink integration (spec F5).
     ALL hyperlink format assumptions live in this file (spec 8.8).
     Spike Q6: link type "housingblueprint", code = 28-char base64.

     1) Alt-click any [Housing Blueprint] link -> inspect it in BlueprintBazaar
        (plain click keeps Blizzard's own behavior untouched).
     2) Raw share codes pasted as plain text in chat are auto-linkified -
        validated first via IsShareCodeValid (local, ~0.1ms, spike Q4).
     3) UI.InspectCode(code): ad-hoc detail view for codes outside the library. ]]

local _, ns = ...
local ChatLinks = {}
ns.ChatLinks = ChatLinks
ChatLinks.LINK_TYPE = "housingblueprint"

ns.UI = ns.UI or {}
local UI = ns.UI

-- Enum verified from live payloads (PTR 2026-07-30): 1=interior, 3=house, 4=exterior.
local TYPE_ENUM = { [1] = "interior", [3] = "house", [4] = "exterior" }

--- Ad-hoc inspect: fetch live contents, then open the detail pane.
function UI.InspectCode(code)
  code = (code or ""):gsub("%s+", "")
  if code == "" then return end
  print("|cffc9a227BlueprintBazaar|r looking up shared blueprint...")
  ns.API.Request(code, function(ok, res)
    if not ok then
      print("|cffc9a227BlueprintBazaar|r could not fetch that code (" .. tostring(res) .. ").")
      return
    end
    if not (UI.frame and UI.frame:IsShown()) then UI.Toggle() end
    local entry = {
      id = "shared:" .. code,
      code = code,
      title = "Shared Blueprint",
      -- no author: this code came from outside the library, so claiming a
      -- credit line ("by shared code") would be noise at best
      type = TYPE_ENUM[ns.API.TypeOf and ns.API.TypeOf(code) or 0],
      description = "Opened from a share code - not in the curated library. Score is live from the server.",
    }
    ns.bus:Fire("SCORE_INVALIDATE", entry.id)
    if UI.ShowDetail then UI.ShowDetail(entry) end
  end)
end

-- 1) Alt-click hook (secure post-hook; never blocks Blizzard's handler)
hooksecurefunc("SetItemRef", function(link)
  if type(link) ~= "string" then return end
  local code = link:match("^housingblueprint:(.+)$")
  if code and IsAltKeyDown() then
    UI.InspectCode(code)
  end
end)

-- 2) Linkify raw codes in chat text
--
-- Do NOT hardcode a length or padding. PTR codes were 28 chars ending in "==";
-- the first live codes (11 Aug) are 24 chars with no padding at all. Blizzard
-- can change this again. So: accept anything base64-shaped in a plausible
-- range and let IsShareCodeValid - which is local and costs ~0.1ms - be the
-- judge. That call is the only thing that actually knows.
local MIN_CODE, MAX_CODE = 16, 64

local function linkify(msg)
  if msg:find("|Hhousingblueprint:", 1, true) then return msg end
  local changed = false
  local out = msg:gsub("%S+", function(w)
    if #w >= MIN_CODE and #w <= MAX_CODE and w:match("^[A-Za-z0-9+/=]+$")
      and ns.API.IsValid(w) then
      changed = true
      return "|cff66bbff|Hhousingblueprint:" .. w .. "|h[Housing Blueprint]|h|r"
    end
  end)
  if changed then return out end
  return msg
end

local function filter(_, _, msg, ...)
  if type(msg) ~= "string" then return end
  if not ns.Guard.Has("links") then return end   -- API drift: leave chat alone
  local new = linkify(msg)
  if new ~= msg then return false, new, ... end
end

for _, ev in ipairs({
  "CHAT_MSG_SAY", "CHAT_MSG_YELL", "CHAT_MSG_GUILD", "CHAT_MSG_OFFICER",
  "CHAT_MSG_PARTY", "CHAT_MSG_PARTY_LEADER", "CHAT_MSG_RAID", "CHAT_MSG_RAID_LEADER",
  "CHAT_MSG_INSTANCE_CHAT", "CHAT_MSG_INSTANCE_CHAT_LEADER",
  "CHAT_MSG_WHISPER", "CHAT_MSG_WHISPER_INFORM", "CHAT_MSG_CHANNEL",
}) do
  ChatFrame_AddMessageEventFilter(ev, filter)
end
