local addonName, TOAST = ...

-- Local-only observability for the Crew State beta.  It reads the live
-- backend state but never sends anything until the explicit refresh button
-- is clicked.

local frame
local rows = {}
local traceFrame
local traceRows = {}
local tracePage = 1
local TRACE_ROWS_PER_PAGE = 16

local TEAL = { 0, 0.92, 0.79 }
local GOLD = { 0.88, 0.75, 0.36 }
local MUTED = { 0.65, 0.65, 0.65 }

local function Age(timestamp)
    timestamp = tonumber(timestamp)
    if not timestamp or timestamp <= 0 then return "never" end
    local seconds = math.max(0, GetServerTime() - timestamp)
    if seconds < 60 then return "now" end
    if seconds < 3600 then return math.floor(seconds / 60) .. "m ago" end
    return math.floor(seconds / 3600) .. "h ago"
end

local function SetRow(index, label, value, color)
    local row = rows[index]
    if not row then return end
    row.label:SetText(label)
    row.value:SetText(value)
    color = color or { 1, 1, 1 }
    row.value:SetTextColor(color[1], color[2], color[3], 1)
end

local function Refresh()
    if not frame or not TOAST.GetCrewCommsStatus then return end
    local state = TOAST.GetCrewCommsStatus()
    local bridge = state.bridgeTarget
        and (state.bridgeTarget .. (state.bridgeWaiting and " (waiting ACK)" or " (ready)"))
        or "idle"
    local transfers = string.format("%d incoming / %d outgoing / %d expected",
        state.incomingTransfers, state.outgoingTransfers, state.expectedPackages)
    local cacheSync = string.format("attempt %s  |  success %s",
        Age(state.lastCacheSyncAttemptAt), Age(state.lastCacheSyncSuccessAt))

    SetRow(1, "Roster role", state.role, state.role == "EXTERNAL" and GOLD or TEAL)
    local transport = state.guildTransport and "Tide ready"
        or state.guildName and (state.guildName .. " (not Tide)") or "not in Tide"
    SetRow(2, "Guild transport", transport, state.guildTransport and TEAL or GOLD)
    SetRow(3, "Community", state.inCommunity and "known" or "not readable yet",
        state.inCommunity and TEAL or GOLD)
    SetRow(4, "Outbound queue", tostring(state.queuePending) .. " pending")
    SetRow(5, "Broker bridge", bridge, state.bridgeTarget and GOLD or MUTED)
    SetRow(6, "Transfers", transfers)
    SetRow(7, "Claims / harvest", string.format("%d claims  |  %d targets", state.activeClaims, state.harvestTargets))
    SetRow(8, "Guild state request", Age(state.lastGuildStateRequestAt))
    SetRow(9, "Cache sync", cacheSync)
    SetRow(10, "Key cache", string.format("%d known keys  |  week %s", state.knownKeys,
        state.keyCacheWeekId or "unknown"))
    SetRow(11, "Synced cache week", state.lastCacheSyncWeekId or "never")
end

local function Shorten(value, limit)
    value = tostring(value or "-")
    if #value <= limit then return value end
    return value:sub(1, limit - 3) .. "..."
end

local function RefreshTrace()
    if not traceFrame or not traceFrame:IsShown() then return end
    local entries = TOAST.GetCommsTrace and TOAST.GetCommsTrace() or {}
    local pages = math.max(1, math.ceil(#entries / TRACE_ROWS_PER_PAGE))
    tracePage = math.max(1, math.min(tracePage, pages))

    local newest = #entries - (tracePage - 1) * TRACE_ROWS_PER_PAGE
    local oldest = math.max(1, newest - TRACE_ROWS_PER_PAGE + 1)
    local rowIndex = 1
    for entryIndex = oldest, newest do
        local entry = entries[entryIndex]
        local row = traceRows[rowIndex]
        if entry and row then
            row.time:SetText(entry.time)
            row.direction:SetText(entry.direction)
            row.channel:SetText(entry.channel)
            row.type:SetText(entry.type)
            row.peer:SetText(Shorten(entry.peer, 25))
            row.result:SetText(Shorten(entry.result, 16))
            row.direction:SetTextColor(entry.direction == "IN" and TEAL[1] or GOLD[1],
                entry.direction == "IN" and TEAL[2] or GOLD[2], entry.direction == "IN" and TEAL[3] or GOLD[3])
            row:Show()
        end
        rowIndex = rowIndex + 1
    end
    for index = rowIndex, #traceRows do traceRows[index]:Hide() end

    traceFrame.pageFS:SetText(string.format("Page %d/%d  -  %d local events", tracePage, pages, #entries))
end

TOAST.RefreshCommsTrace = RefreshTrace

local function BuildTrace()
    traceFrame = CreateFrame("Frame", "TOASTCommsTrace", UIParent, "BackdropTemplate")
    Mixin(traceFrame, BackdropTemplateMixin)
    traceFrame:SetSize(700, 500)
    traceFrame:SetPoint("CENTER", UIParent, "CENTER", 24, -14)
    traceFrame:SetFrameStrata("DIALOG")
    traceFrame:SetMovable(true)
    traceFrame:EnableMouse(true)
    traceFrame:RegisterForDrag("LeftButton")
    traceFrame:SetScript("OnDragStart", traceFrame.StartMoving)
    traceFrame:SetScript("OnDragStop", traceFrame.StopMovingOrSizing)
    traceFrame:SetBackdrop({
        bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background-Dark",
        edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
        edgeSize = 26,
        insets = { left = 9, right = 9, top = 9, bottom = 9 },
    })
    traceFrame:SetBackdropColor(0.03, 0.05, 0.07, 0.98)
    traceFrame:SetBackdropBorderColor(TEAL[1], TEAL[2], TEAL[3], 0.65)
    traceFrame:Hide()

    local title = traceFrame:CreateFontString(nil, "OVERLAY", "TOASTFontNormalHuge")
    title:SetPoint("TOPLEFT", traceFrame, "TOPLEFT", 22, -20)
    title:SetText("TOAST Comms Trace")
    title:SetTextColor(TEAL[1], TEAL[2], TEAL[3])

    local subtitle = traceFrame:CreateFontString(nil, "OVERLAY", "TOASTFontHighlightSmall")
    subtitle:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -5)
    subtitle:SetText("Last 50 local events - metadata only, cleared on /reload")
    subtitle:SetTextColor(MUTED[1], MUTED[2], MUTED[3])

    local divider = traceFrame:CreateTexture(nil, "ARTWORK")
    divider:SetColorTexture(1, 1, 1, 0.12)
    divider:SetPoint("TOPLEFT", traceFrame, "TOPLEFT", 20, -70)
    divider:SetPoint("TOPRIGHT", traceFrame, "TOPRIGHT", -20, -70)
    divider:SetHeight(1)

    local headers = {
        { "TIME", 24, 52, "LEFT" }, { "DIR", 82, 34, "LEFT" }, { "CHANNEL", 120, 66, "LEFT" },
        { "TYPE", 192, 142, "LEFT" }, { "PEER", 340, 174, "LEFT" }, { "RESULT", -24, 112, "RIGHT" },
    }
    for _, header in ipairs(headers) do
        local label = traceFrame:CreateFontString(nil, "OVERLAY", "TOASTFontNormalSmall")
        if header[4] == "RIGHT" then
            label:SetPoint("TOPRIGHT", traceFrame, "TOPRIGHT", header[2], -82)
        else
            label:SetPoint("TOPLEFT", traceFrame, "TOPLEFT", header[2], -82)
        end
        label:SetWidth(header[3])
        label:SetJustifyH(header[4])
        label:SetText(header[1])
        label:SetTextColor(MUTED[1], MUTED[2], MUTED[3])
    end

    for index = 1, TRACE_ROWS_PER_PAGE do
        local y = -100 - (index - 1) * 21
        local row = CreateFrame("Frame", nil, traceFrame)
        row:SetSize(652, 20)
        row:SetPoint("TOPLEFT", traceFrame, "TOPLEFT", 24, y)
        local line = row:CreateTexture(nil, "BACKGROUND")
        line:SetAllPoints()
        line:SetColorTexture(1, 1, 1, index % 2 == 0 and 0.025 or 0.01)

        local function Cell(point, width, justify)
            local cell = row:CreateFontString(nil, "OVERLAY", "TOASTFontHighlightSmall")
            if justify == "RIGHT" then
                cell:SetPoint("RIGHT", row, "RIGHT", point, 0)
            else
                cell:SetPoint("LEFT", row, "LEFT", point, 0)
            end
            cell:SetWidth(width)
            cell:SetJustifyH(justify or "LEFT")
            cell:SetWordWrap(false)
            return cell
        end
        row.time = Cell(0, 52)
        row.direction = Cell(58, 34)
        row.channel = Cell(96, 66)
        row.type = Cell(168, 142)
        row.peer = Cell(316, 174)
        row.result = Cell(0, 112, "RIGHT")
        traceRows[index] = row
    end

    local older = CreateFrame("Button", nil, traceFrame, "UIPanelButtonTemplate")
    older:SetSize(74, 24)
    older:SetPoint("BOTTOMLEFT", traceFrame, "BOTTOMLEFT", 22, 20)
    older:SetText("Older")
    older:SetScript("OnClick", function() tracePage = tracePage + 1; RefreshTrace() end)

    local newer = CreateFrame("Button", nil, traceFrame, "UIPanelButtonTemplate")
    newer:SetSize(74, 24)
    newer:SetPoint("LEFT", older, "RIGHT", 6, 0)
    newer:SetText("Newer")
    newer:SetScript("OnClick", function() tracePage = tracePage - 1; RefreshTrace() end)

    local clear = CreateFrame("Button", nil, traceFrame, "UIPanelButtonTemplate")
    clear:SetSize(74, 24)
    clear:SetPoint("LEFT", newer, "RIGHT", 6, 0)
    clear:SetText("Clear")
    clear:SetScript("OnClick", function()
        if TOAST.ClearCommsTrace then TOAST.ClearCommsTrace() end
        tracePage = 1
        RefreshTrace()
    end)

    traceFrame.pageFS = traceFrame:CreateFontString(nil, "OVERLAY", "TOASTFontHighlightSmall")
    traceFrame.pageFS:SetPoint("BOTTOM", traceFrame, "BOTTOM", 0, 27)
    traceFrame.pageFS:SetTextColor(MUTED[1], MUTED[2], MUTED[3])

    local close = CreateFrame("Button", nil, traceFrame, "UIPanelButtonTemplate")
    close:SetSize(100, 24)
    close:SetPoint("BOTTOMRIGHT", traceFrame, "BOTTOMRIGHT", -22, 20)
    close:SetText("Close")
    close:SetScript("OnClick", function() traceFrame:Hide() end)

    traceFrame:SetScript("OnShow", RefreshTrace)
end

function TOAST.ShowCommsTrace()
    if not traceFrame then BuildTrace() end
    tracePage = 1
    traceFrame:Show()
    traceFrame:Raise()
end

local function Build()
    frame = CreateFrame("Frame", "TOASTCrewCommsDebug", UIParent, "BackdropTemplate")
    frame:SetSize(500, 405)
    frame:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
    frame:SetFrameStrata("DIALOG")
    frame:SetMovable(true)
    frame:EnableMouse(true)
    frame:RegisterForDrag("LeftButton")
    frame:SetScript("OnDragStart", frame.StartMoving)
    frame:SetScript("OnDragStop", frame.StopMovingOrSizing)
    frame:SetBackdrop({
        bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background-Dark",
        edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
        edgeSize = 26,
        insets = { left = 9, right = 9, top = 9, bottom = 9 },
    })
    frame:SetBackdropColor(0.03, 0.05, 0.07, 0.98)
    frame:SetBackdropBorderColor(TEAL[1], TEAL[2], TEAL[3], 0.65)
    frame:Hide()

    local title = frame:CreateFontString(nil, "OVERLAY", "TOASTFontNormalHuge")
    title:SetPoint("TOPLEFT", frame, "TOPLEFT", 22, -20)
    title:SetText("Crew State Comms")
    title:SetTextColor(TEAL[1], TEAL[2], TEAL[3])

    local subtitle = frame:CreateFontString(nil, "OVERLAY", "TOASTFontHighlight")
    subtitle:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -4)
    subtitle:SetText("Local protocol diagnostics - no automatic sends")
    subtitle:SetTextColor(MUTED[1], MUTED[2], MUTED[3])

    local divider = frame:CreateTexture(nil, "ARTWORK")
    divider:SetColorTexture(1, 1, 1, 0.12)
    divider:SetPoint("TOPLEFT", frame, "TOPLEFT", 20, -70)
    divider:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -20, -70)
    divider:SetHeight(1)

    for index = 1, 11 do
        local y = -84 - (index - 1) * 23
        local row = {}
        row.label = frame:CreateFontString(nil, "OVERLAY", "TOASTFontHighlight")
        row.label:SetPoint("TOPLEFT", frame, "TOPLEFT", 24, y)
        row.label:SetWidth(145)
        row.label:SetJustifyH("LEFT")
        row.label:SetTextColor(MUTED[1], MUTED[2], MUTED[3])

        row.value = frame:CreateFontString(nil, "OVERLAY", "TOASTFontHighlight")
        row.value:SetPoint("TOPLEFT", frame, "TOPLEFT", 175, y)
        row.value:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -24, y)
        row.value:SetJustifyH("RIGHT")
        rows[index] = row
    end

    local refresh = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
    refresh:SetSize(160, 24)
    refresh:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", 22, 20)
    refresh:SetText("Refresh actions")
    refresh:SetScript("OnClick", function()
        if TOAST.RefreshUnknowns then TOAST.RefreshUnknowns() end
        Refresh()
    end)

    local trace = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
    trace:SetSize(110, 24)
    trace:SetPoint("BOTTOMLEFT", refresh, "BOTTOMRIGHT", 8, 0)
    trace:SetText("View trace")
    trace:SetScript("OnClick", function()
        if TOAST.ShowCommsTrace then TOAST.ShowCommsTrace() end
    end)

    local close = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
    close:SetSize(100, 24)
    close:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -22, 20)
    close:SetText("Close")
    close:SetScript("OnClick", function() frame:Hide() end)

    frame:SetScript("OnShow", Refresh)
end

TOAST.commands["debug"] = function()
    if not frame then Build() end
    if frame:IsShown() then frame:Hide() else frame:Show() end
end
