GuildHelper = GuildHelper or {}

local addon = GuildHelper

addon.NewUIRaidBingo = addon.NewUIRaidBingo or {}

local page = addon.NewUIRaidBingo
local refreshPage

local PANEL_ART_KEY = "MyToonsPanelBackground"
local PANEL_HEADER_KEY = "HomePanelHeader"
local CARD_ART_KEY = "MyToonsCardBackground"
local BUTTON_ART_KEY = "MyToonsButtonBackground"
local BUTTON_ACTIVE_ART_KEY = "MyToonsButtonSelected"

local function trimString(value)
    return tostring(value or ""):match("^%s*(.-)%s*$") or ""
end

local function shortPlayerName(value)
    return trimString(value):match("^[^-]+") or trimString(value)
end

local function formatTimestampText(timestamp)
    local resolved = tonumber(timestamp) or 0
    if resolved <= 0 then
        return "Unknown"
    end
    return date("%b %d %I:%M %p", resolved)
end

local function ApplyRect(frame, parent, rect)
    frame:ClearAllPoints()
    frame:SetPoint("TOPLEFT", parent, "TOPLEFT", tonumber(rect.x) or 0, tonumber(rect.y) or 0)
    frame:SetSize(tonumber(rect.w) or 1, tonumber(rect.h) or 1)
end

local function clearFrame(frame)
    if not frame then
        return
    end
    if frame.Hide then
        frame:Hide()
    end
    if frame.SetParent then
        frame:SetParent(nil)
    end
end

local function resolveTexturePath(textureKey)
    local textures = addon.NewUIAssets and addon.NewUIAssets.Textures or nil
    return textures and textures[textureKey] or nil
end

local function resolveArtMetrics(textureKey)
    local metrics = addon.NewUIAssets and addon.NewUIAssets.Metrics or nil
    return metrics and metrics[textureKey] or nil
end

local function applyArtTexture(texture, textureKey, fallbackColor)
    local texturePath = resolveTexturePath(textureKey)
    local metrics = resolveArtMetrics(textureKey)
    if texturePath then
        texture:SetTexture(texturePath)
        texture:SetVertexColor(1, 1, 1, 0.98)
        if metrics and type(metrics.texCoord) == "table" and #metrics.texCoord == 4 then
            texture:SetTexCoord(metrics.texCoord[1], metrics.texCoord[3], metrics.texCoord[2], metrics.texCoord[4])
        end
        return
    end
    texture:SetColorTexture(unpack(fallbackColor or { 0.10, 0.10, 0.12, 0.95 }))
end

local function createSurface(parent, rect)
    local frame = CreateFrame("Frame", nil, parent, BackdropTemplateMixin and "BackdropTemplate" or nil)
    ApplyRect(frame, parent, rect)
    if frame.SetBackdrop then
        frame:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8X8", edgeFile = "Interface\\Buttons\\WHITE8X8", edgeSize = 1, insets = { left = 1, right = 1, top = 1, bottom = 1 } })
        frame:SetBackdropColor(0.09, 0.10, 0.13, 0.94)
        frame:SetBackdropBorderColor(0.42, 0.33, 0.16, 0.92)
    end
    return frame
end

local function createPanel(parent, rect, titleText, metaText)
    local panel = CreateFrame("Frame", nil, parent, BackdropTemplateMixin and "BackdropTemplate" or nil)
    ApplyRect(panel, parent, rect)
    if panel.SetBackdrop then
        panel:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8X8", edgeFile = "Interface\\Buttons\\WHITE8X8", edgeSize = 1, insets = { left = 1, right = 1, top = 1, bottom = 1 } })
        panel:SetBackdropColor(0, 0, 0, 0)
        panel:SetBackdropBorderColor(0, 0, 0, 0)
    end

    local background = panel:CreateTexture(nil, "BACKGROUND")
    background:SetAllPoints(panel)
    applyArtTexture(background, PANEL_ART_KEY, { 0.09, 0.10, 0.13, 0.94 })

    local headerBand = panel:CreateTexture(nil, "ARTWORK")
    headerBand:SetPoint("TOPLEFT", panel, "TOPLEFT", 24, -14)
    headerBand:SetPoint("TOPRIGHT", panel, "TOPRIGHT", -24, -14)
    headerBand:SetHeight(24)
    applyArtTexture(headerBand, PANEL_HEADER_KEY, { 0.08, 0.08, 0.10, 0.82 })

    local title = panel:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    title:SetPoint("CENTER", headerBand, "CENTER", 0, -1)
    title:SetText(titleText or "")
    title:SetTextColor(0.98, 0.92, 0.76, 1)

    if trimString(metaText) ~= "" then
        local meta = panel:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
        meta:SetPoint("TOPLEFT", panel, "TOPLEFT", 14, -46)
        meta:SetWidth(math.max(1, (tonumber(rect.w) or 1) - 28))
        meta:SetJustifyH("LEFT")
        meta:SetText(metaText)
        meta:SetTextColor(0.76, 0.72, 0.64, 1)
        panel.Meta = meta
    end

    panel.Title = title
    return panel
end

local function createButton(parent, rect, labelText, onClick, isActive)
    local button = CreateFrame("Button", nil, parent, BackdropTemplateMixin and "BackdropTemplate" or nil)
    ApplyRect(button, parent, rect)
    local background = button:CreateTexture(nil, "BACKGROUND")
    background:SetAllPoints(button)
    applyArtTexture(background, isActive and BUTTON_ACTIVE_ART_KEY or BUTTON_ART_KEY, isActive and { 0.20, 0.23, 0.31, 0.98 } or { 0.10, 0.11, 0.14, 0.95 })
    button.Background = background

    local text = button:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    text:SetPoint("CENTER", button, "CENTER", 0, 0)
    text:SetText(labelText or "")
    text:SetTextColor(isActive and 0.98 or 0.96, isActive and 0.88 or 0.93, isActive and 0.58 or 0.84, 1)
    button.Text = text
    button:SetScript("OnClick", function()
        if onClick then
            onClick(button)
        end
    end)
    return button
end

local function setButtonVisual(button, tint, textColor)
    if button and button.Background and button.Background.SetVertexColor then
        button.Background:SetVertexColor(unpack(tint or { 1, 1, 1, 1 }))
    end
    if button and button.Text then
        button.Text:SetTextColor(unpack(textColor or { 0.96, 0.93, 0.84, 1 }))
    end
end

local function createCard(parent, rect, titleText, valueText, subtitleText, backgroundTextureKey)
    local card = CreateFrame("Frame", nil, parent, BackdropTemplateMixin and "BackdropTemplate" or nil)
    ApplyRect(card, parent, rect)

    local background = card:CreateTexture(nil, "BACKGROUND")
    background:SetAllPoints(card)
    applyArtTexture(background, backgroundTextureKey or CARD_ART_KEY, { 0.09, 0.10, 0.13, 0.94 })

    local title = card:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    title:SetPoint("TOP", card, "TOP", 0, -16)
    title:SetWidth(math.max(1, (tonumber(rect.w) or 1) - 24))
    title:SetJustifyH("CENTER")
    title:SetText(titleText or "")
    title:SetTextColor(0.78, 0.74, 0.66, 1)

    local value = card:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
    value:SetPoint("TOP", title, "BOTTOM", 0, -8)
    value:SetWidth(math.max(1, (tonumber(rect.w) or 1) - 24))
    value:SetJustifyH("CENTER")
    value:SetText(valueText or "-")
    value:SetTextColor(0.96, 0.82, 0.34, 1)

    if trimString(subtitleText) ~= "" then
        local subtitle = card:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
        subtitle:SetPoint("BOTTOM", card, "BOTTOM", 0, 12)
        subtitle:SetWidth(math.max(1, (tonumber(rect.w) or 1) - 24))
        subtitle:SetJustifyH("CENTER")
        subtitle:SetText(subtitleText)
        subtitle:SetTextColor(0.72, 0.68, 0.58, 1)
        card.Subtitle = subtitle
    end

    card.Title = title
    card.Value = value
    return card
end

local function createInput(parent, rect, initialText)
    local host = createSurface(parent, rect)
    local input = CreateFrame("EditBox", nil, host, "InputBoxTemplate")
    if input.Left then input.Left:SetAlpha(0) end
    if input.Middle then input.Middle:SetAlpha(0) end
    if input.Right then input.Right:SetAlpha(0) end
    input:SetAutoFocus(false)
    input:SetFontObject("GameFontHighlight")
    input:SetPoint("TOPLEFT", host, "TOPLEFT", 8, -4)
    input:SetPoint("BOTTOMRIGHT", host, "BOTTOMRIGHT", -8, 4)
    input:SetText(initialText or "")
    host.Input = input
    return host
end

local function createReadOnlyMultiline(parent, rect, textValue)
    local host = createSurface(parent, rect)
    local scrollFrame = CreateFrame("ScrollFrame", nil, host, "UIPanelScrollFrameTemplate")
    scrollFrame:SetPoint("TOPLEFT", host, "TOPLEFT", 8, -8)
    scrollFrame:SetPoint("BOTTOMRIGHT", host, "BOTTOMRIGHT", -28, 8)

    local editBox = CreateFrame("EditBox", nil, scrollFrame, "InputBoxTemplate")
    if editBox.Left then editBox.Left:SetAlpha(0) end
    if editBox.Middle then editBox.Middle:SetAlpha(0) end
    if editBox.Right then editBox.Right:SetAlpha(0) end
    editBox:SetMultiLine(true)
    editBox:SetAutoFocus(false)
    editBox:SetFontObject("GameFontHighlightSmall")
    editBox:SetWidth(math.max(1, (tonumber(rect.w) or 1) - 40))
    editBox:SetText(textValue or "")
    if editBox.SetCursorPosition then
        editBox:SetCursorPosition(0)
    end
    editBox:SetScript("OnEscapePressed", function(self)
        self:ClearFocus()
    end)
    local textHeight = 0
    if editBox.GetStringHeight then
        textHeight = tonumber(editBox:GetStringHeight()) or 0
    end
    editBox:SetHeight(math.max((tonumber(rect.h) or 1) - 16, math.floor(textHeight + 16)))
    scrollFrame:SetScrollChild(editBox)
    host.EditBox = editBox
    return host
end

refreshPage = function(parent, snapshot)
    if page._rootFrame then
        clearFrame(page._rootFrame)
        page._rootFrame = nil
    end
    page:Render(parent, snapshot)
end

function page:Render(parent, snapshot)
    local layout = addon.NewUILayout and addon.NewUILayout.RaidBingo or nil
    local service = addon.RaidBingoService
    local root = CreateFrame("Frame", nil, parent)
    ApplyRect(root, parent, layout and layout.Page or { x = 0, y = 0, w = tonumber(parent:GetWidth()) or 1380, h = tonumber(parent:GetHeight()) or 742 })
    page._rootFrame = root

    if not service then
        return createPanel(root, { x = 20, y = -20, w = 918, h = 180 }, "Raid Bingo Disabled", "The Raid Bingo service is not loaded right now, so this preview page is unavailable.")
    end

    local session = service:GetSession()
    local summary = service:GetSummary()
    local selectionRows = service:GetSelectionRows()
    local prizePoolText = summary.prizePool or service:GetPrizePoolText()
    local payouts = service:CalculatePayouts(prizePoolText)
    local rolledSet = {}
    local isSessionActive = summary.sessionId and summary.status == "active"
    local hasEndedSession = summary.sessionId and summary.status ~= "active"

    if (IsInGroup() or IsInRaid()) and not summary.canLead and not summary.sessionId then
        service:MaybeRequestState()
    end

    for _, number in ipairs(session.rolledNumbers or {}) do
        rolledSet[tonumber(number)] = true
    end

    local cardsRect = layout and layout.Cards or { x = 20, y = -18, w = 1340, h = 100, gap = 8, columns = 5 }
    local cardGap = tonumber(cardsRect.gap) or 8
    local cardWidth = math.floor(((tonumber(cardsRect.w) or 1340) - (cardGap * 4)) / 5)
    local cardX = tonumber(cardsRect.x) or 20
    local cardY = tonumber(cardsRect.y) or -18

    createCard(root, { x = cardX, y = cardY, w = cardWidth, h = tonumber(cardsRect.h) or 100 }, "Session", isSessionActive and "Live" or (hasEndedSession and "Finished" or "Idle"), summary.sessionId and ("Started " .. formatTimestampText(summary.startedAt)) or "Start or request a session to begin", "HomeNewsCardBackground")
    createCard(root, { x = cardX + (cardWidth + cardGap), y = cardY, w = cardWidth, h = tonumber(cardsRect.h) or 100 }, "Rolled", tostring(summary.rolled or 0), "Unique numbers from a 1-75 bag", "HomeMythicCardBackground")
    createCard(root, { x = cardX + ((cardWidth + cardGap) * 2), y = cardY, w = cardWidth, h = tonumber(cardsRect.h) or 100 }, "Submissions", tostring(summary.submissions or 0), "Locked player cards received by the leader", "HomeMembersCardBackground")
    createCard(root, { x = cardX + ((cardWidth + cardGap) * 3), y = cardY, w = cardWidth, h = tonumber(cardsRect.h) or 100 }, "Your Card", summary.locked and "Locked" or string.format("%d/5", tonumber(summary.picked) or 0), summary.locked and "Selections sent automatically when the session syncs" or "Pick any five spaces to lock the card", "HomeRaidCardBackground")
    createCard(root, { x = cardX + ((cardWidth + cardGap) * 4), y = cardY, w = cardWidth, h = tonumber(cardsRect.h) or 100 }, "Leader", shortPlayerName(summary.leader), summary.canLead and "Leader controls enabled" or "Waiting on group leader", "HomeCardBackground")

    local controlPanel = createPanel(root, layout and layout.Control or { x = 20, y = -126, w = 300, h = 486 }, "Session Control", "")
    local controlMeta = controlPanel:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    controlMeta:SetPoint("TOPLEFT", controlPanel, "TOPLEFT", 16, -52)
    controlMeta:SetWidth(268)
    controlMeta:SetJustifyH("LEFT")
    controlMeta:SetText(string.format("Leader: %s\nStatus: %s\nSession: %s\nPrize Pool: %s%s", shortPlayerName(summary.leader), isSessionActive and (summary.canLead and "Leader controls enabled" or "Waiting on group leader") or (hasEndedSession and "Round complete" or "No active round"), summary.sessionId or "No active round", prizePoolText ~= "" and prizePoolText or "Not set", summary.prizePoolLocked and " (locked)" or ""))
    controlMeta:SetTextColor(0.82, 0.80, 0.76, 1)

    local sessionButton = createButton(controlPanel, { x = 16, y = -122, w = 126, h = 24 }, summary.sessionId and "Reset Game" or "Start Session", function() service:ResetGame(); refreshPage(parent, snapshot) end, false)
    local rollButton = createButton(controlPanel, { x = 158, y = -122, w = 126, h = 24 }, "Roll Number", function() service:RollNumber(); refreshPage(parent, snapshot) end, false)
    local syncButton = createButton(controlPanel, { x = 16, y = -156, w = 126, h = 24 }, "Broadcast State", function() service:BroadcastState(); refreshPage(parent, snapshot) end, false)
    createButton(controlPanel, { x = 158, y = -156, w = 126, h = 24 }, "Request State", function() service:RequestState() end, false)
    local endButton = createButton(controlPanel, { x = 16, y = -190, w = 126, h = 24 }, "End Session", function() service:EndSession(); refreshPage(parent, snapshot) end, false)
    createButton(controlPanel, { x = 16, y = -224, w = 126, h = 24 }, "Export Group", function() service:ShowExportDialog() end, false)
    local rerollButton = createButton(controlPanel, { x = 158, y = -224, w = 126, h = 24 }, "New Card", function() service:RollNewCard(); refreshPage(parent, snapshot) end, false)

    if not summary.canLead then
        sessionButton:Disable()
        rollButton:Disable()
        syncButton:Disable()
        endButton:Disable()
    end
    if not summary.sessionId or hasEndedSession then
        endButton:Disable()
    end
    if hasEndedSession then
        rollButton:Disable()
    end
    if summary.locked then
        rerollButton:Disable()
    end

    local prizeLabel = controlPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    prizeLabel:SetPoint("TOPLEFT", controlPanel, "TOPLEFT", 16, -274)
    prizeLabel:SetText("Prize Pool")
    local prizeInputHost = createInput(controlPanel, { x = 16, y = -300, w = 126, h = 24 }, prizePoolText)
    local prizeInput = prizeInputHost.Input
    prizeInput:SetNumeric(false)
    prizeInput:SetScript("OnTextChanged", function(self)
        local ok, value = service:SetPrizePool(self:GetText())
        if ok then
            page.State = page.State or {}
            page.State.prizePool = value
        else
            self:SetText(service:GetPrizePoolText())
        end
    end)
    prizeInput:SetScript("OnEnterPressed", function(self)
        self:ClearFocus()
        refreshPage(parent, snapshot)
    end)
    prizeInput:SetScript("OnEscapePressed", function(self)
        self:ClearFocus()
    end)
    prizeInput:SetScript("OnEditFocusLost", function(self)
        local ok = service:SetPrizePool(self:GetText())
        if not ok then
            self:SetText(service:GetPrizePoolText())
        end
        refreshPage(parent, snapshot)
    end)
    if summary.prizePoolLocked then
        prizeInput:Disable()
    end

    createButton(controlPanel, { x = 158, y = -300, w = 126, h = 24 }, "Preview", function() refreshPage(parent, snapshot) end, false)
    local postWinnersButton = createButton(controlPanel, { x = 16, y = -334, w = 126, h = 24 }, "Post Winners", function()
        local ok, message = service:PostPayouts(prizePoolText)
        if not ok and message then
            addon:Print(message)
        end
    end, false)
    if not summary.canLead then
        postWinnersButton:Disable()
    end

    local rolledLabel = controlPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    rolledLabel:SetPoint("TOPLEFT", controlPanel, "TOPLEFT", 16, -384)
    rolledLabel:SetText("Rolled Numbers")
    createReadOnlyMultiline(controlPanel, { x = 16, y = -410, w = 268, h = 60 }, #session.rolledNumbers > 0 and table.concat(session.rolledNumbers, ", ") or "No numbers rolled yet.")

    local cardPanel = createPanel(root, layout and layout.Card or { x = 328, y = -126, w = 300, h = 486 }, "Your Card", summary.locked and "Card locked. Your picks will be re-sent automatically when the leader syncs the round." or string.format("Pick exactly five numbers. Current picks: %d/5.", tonumber(summary.picked) or 0))
    local cardStatus = cardPanel.Meta
    local function applyRaidBingoCellTone(button, isMatched, isSelected)
        if isMatched then
            setButtonVisual(button, { 0.52, 1.00, 0.72, 1 }, { 0.08, 0.18, 0.10, 1 })
        elseif isSelected then
            setButtonVisual(button, { 1.00, 0.90, 0.56, 1 }, { 0.18, 0.12, 0.02, 1 })
        else
            setButtonVisual(button, { 1, 1, 1, 1 }, { 0.96, 0.93, 0.84, 1 })
        end
    end

    local bingoLetters = { "B", "I", "N", "G", "O" }
    for index, letter in ipairs(bingoLetters) do
        local header = cardPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
        header:SetPoint("TOPLEFT", cardPanel, "TOPLEFT", 24 + ((index - 1) * 56), -84)
        header:SetText(letter)
        header:SetTextColor(0.96, 0.82, 0.34, 1)
    end

    for _, cell in ipairs(service:GetCardCells()) do
        local x = 8 + ((cell.column - 1) * 56)
        local y = -112 - ((cell.row - 1) * 60)
        if cell.free then
            local freeCell = createSurface(cardPanel, { x = x, y = y, w = 51, h = 51 })
            local freeText = freeCell:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
            freeText:SetPoint("CENTER", freeCell, "CENTER", 0, 0)
            freeText:SetText("FREE")
            freeText:SetTextColor(0.96, 0.82, 0.34, 1)
        else
            local cellButton
            cellButton = createButton(cardPanel, { x = x, y = y, w = 51, h = 51 }, tostring(cell.number or "?"), function()
                local ok, result = service:ToggleCardSelection(cell.index)
                if not ok then
                    return
                end
                local updatedCard = service:GetCard()
                local selectedNumber = tonumber(updatedCard.numbers[cell.index])
                local isSelected = updatedCard.selectedIndices[cell.index] and true or false
                local isMatched = selectedNumber and rolledSet[selectedNumber] and isSelected and true or false
                applyRaidBingoCellTone(cellButton, isMatched, isSelected)
                if result == "locked" then
                    refreshPage(parent, snapshot)
                    return
                end
                if cardStatus then
                    cardStatus:SetText(string.format("Pick exactly five numbers. Current picks: %d/5.", tonumber(service:GetSummary().picked) or 0))
                end
            end, false)
            if cellButton.Text and cellButton.Text.SetFont then
                cellButton.Text:SetFont("Fonts\\MORPHEUS.ttf", 18, "OUTLINE")
            end
            applyRaidBingoCellTone(cellButton, cell.matched, cell.selected)
        end
    end

    local rightPanel = createPanel(root, layout and layout.Right or { x = 636, y = -126, w = 342, h = 486 }, "Players & Winnings", "")
    local pickLines = {}
    if #selectionRows == 0 then
        pickLines[1] = "No player submissions yet."
    else
        for index, row in ipairs(selectionRows) do
            local numbers = {}
            for numberIndex, number in ipairs(row.numbers or {}) do
                if rolledSet[tonumber(number)] then
                    numbers[numberIndex] = "|cff6ee36e" .. tostring(number) .. "|r"
                else
                    numbers[numberIndex] = tostring(number)
                end
            end
            pickLines[index] = string.format("%s [%d]: %s", shortPlayerName(row.player), tonumber(row.matched) or 0, table.concat(numbers, ", "))
        end
    end

    local payoutLines = {}
    if #payouts == 0 then
        payoutLines[1] = "Enter a prize pool to preview winnings once players reach 3, 4, or 5 matches."
    else
        for index, payout in ipairs(payouts) do
            payoutLines[index] = string.format("%s: %d matches - %d", shortPlayerName(payout.player), tonumber(payout.matched) or 0, tonumber(payout.amount) or 0)
        end
    end

    local picksHeader = rightPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    picksHeader:SetPoint("TOPLEFT", rightPanel, "TOPLEFT", 16, -52)
    picksHeader:SetText("Locked Cards")
    createReadOnlyMultiline(rightPanel, { x = 16, y = -76, w = 310, h = 186 }, table.concat(pickLines, "\n"))

    local payoutsHeader = rightPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    payoutsHeader:SetPoint("TOPLEFT", rightPanel, "TOPLEFT", 16, -276)
    payoutsHeader:SetText("Projected Winnings")
    createReadOnlyMultiline(rightPanel, { x = 16, y = -300, w = 310, h = 150 }, table.concat(payoutLines, "\n"))

    return root
end

return page