local _, ns = ...
local L = ns.L or {}

local PveListCard = {}
ns.UIPveListCard = PveListCard

local ROW_GAP = 8
local SCROLL_BOTTOM_PAD = 14
local SCROLL_RIGHT_PAD = 32
local SCROLL_ROW_HEIGHT = 34
local SCROLL_ROW_INNER_RIGHT_PAD = 28
local SCROLL_ROW_VALUE_WIDTH = 82

local function installScrollWheel(scrollFrame)
    if not scrollFrame or scrollFrame.vlWheelInstalled then
        return
    end

    scrollFrame.vlWheelInstalled = true
    scrollFrame:EnableMouseWheel(true)
    scrollFrame:SetScript("OnMouseWheel", function(self, delta)
        if type(self.UpdateScrollChildRect) == "function" then
            self:UpdateScrollChildRect()
        end

        local current = type(self.GetVerticalScroll) == "function" and (tonumber(self:GetVerticalScroll()) or 0) or 0
        local range = type(self.GetVerticalScrollRange) == "function" and (tonumber(self:GetVerticalScrollRange()) or 0) or 0
        local target = current - ((tonumber(delta) or 0) * 126)
        target = math.max(0, math.min(math.max(0, range), target))

        if type(self.SetVerticalScroll) == "function" then
            self:SetVerticalScroll(target)
        end
        if self.ScrollBar and type(self.ScrollBar.SetValue) == "function" then
            self.ScrollBar:SetValue(target)
        end
    end)
end

local function hideRows(rows)
    for _, row in ipairs(rows or {}) do
        if row.hitbox then
            row.hitbox:SetScript("OnClick", nil)
        end
        row.tooltipTitle = nil
        row.tooltipLines = nil
        row:Hide()
    end
end

local function ensureRows(parent, rows, count, helpers)
    local createWeeklyStatusRow = helpers and helpers.createWeeklyStatusRow
    if not parent or type(createWeeklyStatusRow) ~= "function" then
        return rows or {}
    end

    rows = rows or {}
    while #rows < count do
        local row = createWeeklyStatusRow(parent)
        row:Hide()
        rows[#rows + 1] = row
    end

    return rows
end

local function clampScroll(scrollFrame, helpers, forceTop)
    if not scrollFrame then
        return
    end

    if type(scrollFrame.UpdateScrollChildRect) == "function" then
        scrollFrame:UpdateScrollChildRect()
    end
    if helpers and type(helpers.clampScrollFramePosition) == "function" then
        helpers.clampScrollFramePosition(scrollFrame, forceTop)
    end
end

local function prepareScrollableRow(rowFrame, columnWidth)
    if not rowFrame then
        return
    end

    rowFrame:SetHeight(SCROLL_ROW_HEIGHT)

    if rowFrame.statusBadgeFrame then
        rowFrame.statusBadgeFrame:ClearAllPoints()
        rowFrame.statusBadgeFrame:SetPoint("RIGHT", -8, 0)
    end

    if rowFrame.value then
        rowFrame.value:ClearAllPoints()
        rowFrame.value:SetPoint("RIGHT", rowFrame.statusBadgeFrame or rowFrame.statusBadge or rowFrame, "LEFT", -6, 0)
        rowFrame.value:SetWidth(SCROLL_ROW_VALUE_WIDTH)
        rowFrame.value:SetJustifyH("RIGHT")
        if rowFrame.value.SetWordWrap then
            rowFrame.value:SetWordWrap(false)
        end
        if rowFrame.value.SetMaxLines then
            rowFrame.value:SetMaxLines(1)
        end
    end

    if rowFrame.label then
        rowFrame.label:ClearAllPoints()
        rowFrame.label:SetPoint("LEFT", 20, 0)
        rowFrame.label:SetWidth(math.max(220, math.floor((tonumber(columnWidth) or 520) - 166)))
        rowFrame.label:SetJustifyH("LEFT")
        rowFrame.label:SetJustifyV("MIDDLE")
        if rowFrame.label.SetWordWrap then
            rowFrame.label:SetWordWrap(false)
        end
        if rowFrame.label.SetNonSpaceWrap then
            rowFrame.label:SetNonSpaceWrap(false)
        end
        if rowFrame.label.SetMaxLines then
            rowFrame.label:SetMaxLines(1)
        end
    end
end

local function layoutScrollableRows(card, rows, helpers)
    if not card or not card.rowScroll or not card.rowScrollChild then
        return {}
    end

    local rowFrames = ensureRows(card.rowScrollChild, card.scrollRows, #rows, helpers)
    card.scrollRows = rowFrames

    card.rowScroll:ClearAllPoints()
    card.rowScroll:SetPoint("TOPLEFT", card.summary1, "BOTTOMLEFT", 0, -16)
    card.rowScroll:SetPoint("BOTTOMRIGHT", -SCROLL_RIGHT_PAD, SCROLL_BOTTOM_PAD)
    card.rowScroll:Show()

    local rowCount = #rows
    local columnCount = 1
    local rowsPerColumn = math.max(1, math.ceil(rowCount / columnCount))
    local scrollFrameWidth = math.floor(tonumber(card.rowScroll:GetWidth()) or 0)
    if scrollFrameWidth <= 0 then
        scrollFrameWidth = math.floor(math.max(520, (tonumber(card:GetWidth()) or 780) - SCROLL_RIGHT_PAD - 14))
    end
    local scrollWidth = math.max(520, scrollFrameWidth - 4)
    local columnGap = 0
    local columnWidth = columnCount == 2
        and math.max(250, math.floor((scrollWidth - columnGap - 6) / 2))
        or math.max(320, scrollWidth - SCROLL_ROW_INNER_RIGHT_PAD)
    local rowHeight = SCROLL_ROW_HEIGHT
    local contentRows = columnCount == 2 and rowsPerColumn or rowCount
    local contentHeight = math.max(1, (contentRows * rowHeight) + (math.max(0, contentRows - 1) * ROW_GAP))

    card.rowScrollChild:SetSize(scrollWidth, contentHeight)

    for index, rowFrame in ipairs(rowFrames) do
        rowFrame:ClearAllPoints()
        rowFrame:SetWidth(columnWidth)
        prepareScrollableRow(rowFrame, columnWidth)
        if index <= rowCount then
            local column = columnCount == 2 and math.floor((index - 1) / rowsPerColumn) or 0
            local rowIndex = columnCount == 2 and ((index - 1) % rowsPerColumn) or (index - 1)
            local x = column * (columnWidth + columnGap)
            local y = -rowIndex * (rowHeight + ROW_GAP)
            rowFrame:SetPoint("TOPLEFT", card.rowScrollChild, "TOPLEFT", x, y)
        end
    end

    clampScroll(card.rowScroll, helpers, rowCount > 0 and (card.vlLastScrollableRowCount or 0) == 0)
    card.vlLastScrollableRowCount = rowCount

    return rowFrames
end

local function applyCard(card, opts, helpers)
    if not card then
        return
    end

    local setWeeklyStatusRow = helpers.setWeeklyStatusRow
    local title = opts and opts.title or ""
    local subtitle = opts and opts.subtitle or ""
    local summary1 = opts and opts.summary1 or ""
    local summary2 = opts and opts.summary2 or ""
    local summary3 = opts and opts.summary3 or ""
    local rows = opts and opts.rows or {}
    local columnCount = opts and opts.columnCount == 2 and 2 or 1
    local useScrollRows = opts and opts.scrollRows == true

    card.title:SetText(title)
    card.subtitle:SetText(subtitle)
    card.title:Hide()
    card.subtitle:Hide()
    card.summary1:SetText(summary1)
    card.summary2:SetText(summary2)
    card.summary3:SetText(summary3)

    if card.professionKnowledgeHeader then
        card.professionKnowledgeHeader:Hide()
    end
    for _, knowledgeRow in ipairs(card.professionKnowledgeRows or {}) do
        knowledgeRow.tooltipTitle = nil
        knowledgeRow.tooltipLines = nil
        if knowledgeRow.hitbox then
            knowledgeRow.hitbox:SetScript("OnClick", nil)
        end
        knowledgeRow:Hide()
    end

    local rowFrames = card.weeklyRows or {}
    if useScrollRows then
        hideRows(card.weeklyRows)
        rowFrames = layoutScrollableRows(card, rows, helpers)
    else
        card.vlLastScrollableRowCount = 0
        if card.rowScroll then
            card.rowScroll:Hide()
        end
        hideRows(card.scrollRows)
        rowFrames = ensureRows(card, card.weeklyRows, #rows, helpers)
        card.weeklyRows = rowFrames
    end

    if (not useScrollRows) and columnCount == 2 and card.weeklyRows and card.weeklyRows[1] then
        local rowCount = #rows
        local rowsPerColumn = math.max(1, math.ceil(rowCount / 2))
        local cardWidth = math.max(0, card:GetWidth() or 0)
        local columnWidth = math.max(260, math.floor((cardWidth - 48) / 2))

        for index, rowFrame in ipairs(card.weeklyRows) do
            rowFrame:ClearAllPoints()
            rowFrame:SetWidth(columnWidth)
            if index <= rowsPerColumn then
                if index == 1 then
                    rowFrame:SetPoint("TOPLEFT", card.summary1, "BOTTOMLEFT", 0, -16)
                else
                    rowFrame:SetPoint("TOPLEFT", card.weeklyRows[index - 1], "BOTTOMLEFT", 0, -8)
                end
            else
                local previousIndex = index - rowsPerColumn
                if previousIndex == 1 then
                    rowFrame:SetPoint("TOPLEFT", card.summary1, "BOTTOMLEFT", columnWidth + 20, -16)
                else
                    rowFrame:SetPoint("TOPLEFT", card.weeklyRows[index - 1], "BOTTOMLEFT", 0, -8)
                end
            end
        end
    elseif (not useScrollRows) and card.weeklyRows and card.weeklyRows[1] then
        card.weeklyRows[1]:ClearAllPoints()
        card.weeklyRows[1]:SetPoint("TOPLEFT", card.summary1, "BOTTOMLEFT", 0, -16)
        card.weeklyRows[1]:SetPoint("TOPRIGHT", -14, 0)
        for index = 2, #card.weeklyRows do
            card.weeklyRows[index]:ClearAllPoints()
            card.weeklyRows[index]:SetPoint("TOPLEFT", card.weeklyRows[index - 1], "BOTTOMLEFT", 0, -8)
            card.weeklyRows[index]:SetPoint("TOPRIGHT", -14, 0)
        end
    end

    for index, rowFrame in ipairs(rowFrames or {}) do
        local rowData = rows[index]
        if rowData then
            setWeeklyStatusRow(rowFrame, rowData.entry, rowData.label)
        else
            rowFrame:Hide()
        end
    end
end

function PveListCard.CreateCard(parent, helpers)
    helpers = helpers or {}
    local createSectionFrame = helpers.createSectionFrame
    local createLabel = helpers.createLabel
    local createWeeklyStatusRow = helpers.createWeeklyStatusRow
    local styleScrollFrame = helpers.styleScrollFrame or function() end

    local card = createSectionFrame(parent, "card")
    card:Hide()

    card.title = createLabel(card, "GameFontNormalLarge", "LEFT")
    card.title:SetPoint("TOPLEFT", 14, -18)
    card.title:Hide()

    card.subtitle = createLabel(card, "GameFontHighlightSmall", "LEFT")
    card.subtitle:SetPoint("TOPLEFT", card.title, "BOTTOMLEFT", 0, -8)
    card.subtitle:SetPoint("TOPRIGHT", -14, 0)
    card.subtitle:Hide()

    card.summary1 = createLabel(card, "GameFontHighlightSmall", "LEFT")
    card.summary1:SetPoint("TOPLEFT", 14, -18)

    card.summary2 = createLabel(card, "GameFontHighlightSmall", "LEFT")
    card.summary2:SetPoint("LEFT", card.summary1, "RIGHT", 24, 0)

    card.summary3 = createLabel(card, "GameFontHighlightSmall", "LEFT")
    card.summary3:SetPoint("LEFT", card.summary2, "RIGHT", 24, 0)

    card.professionKnowledgeHeader = createLabel(card, "GameFontHighlightSmall", "LEFT")
    card.professionKnowledgeHeader:SetPoint("TOPLEFT", card.summary1, "BOTTOMLEFT", 0, -16)
    card.professionKnowledgeHeader:SetText(L.PROFESSIONS_KNOWLEDGE_LABEL or "Knowledge")
    card.professionKnowledgeHeader:Hide()

    card.professionKnowledgeRows = {}
    for index = 1, 2 do
        local row = createWeeklyStatusRow(card)
        if index == 1 then
            row:SetPoint("TOPLEFT", card.professionKnowledgeHeader, "BOTTOMLEFT", 0, -8)
            row:SetPoint("TOPRIGHT", -14, 0)
        else
            row:SetPoint("TOPLEFT", card.professionKnowledgeRows[index - 1], "BOTTOMLEFT", 0, -8)
            row:SetPoint("TOPRIGHT", -14, 0)
        end
        row:Hide()
        card.professionKnowledgeRows[index] = row
    end

    card.rowScroll = CreateFrame("ScrollFrame", nil, card, "UIPanelScrollFrameTemplate")
    card.rowScroll:SetPoint("TOPLEFT", card.summary1, "BOTTOMLEFT", 0, -16)
    card.rowScroll:SetPoint("BOTTOMRIGHT", -SCROLL_RIGHT_PAD, SCROLL_BOTTOM_PAD)
    card.rowScroll:Hide()

    card.rowScrollChild = CreateFrame("Frame", nil, card.rowScroll)
    card.rowScrollChild:SetSize(720, 10)
    card.rowScroll:SetScrollChild(card.rowScrollChild)
    styleScrollFrame(card.rowScroll)
    installScrollWheel(card.rowScroll)

    card.scrollRows = {}

    card.weeklyRows = {}
    for index = 1, 26 do
        local row = createWeeklyStatusRow(card)
        if index == 1 then
            row:SetPoint("TOPLEFT", card.summary1, "BOTTOMLEFT", 0, -16)
            row:SetPoint("TOPRIGHT", -14, 0)
        else
            row:SetPoint("TOPLEFT", card.weeklyRows[index - 1], "BOTTOMLEFT", 0, -8)
            row:SetPoint("TOPRIGHT", -14, 0)
        end
        row:Hide()
        card.weeklyRows[index] = row
    end

    return card
end

local function refreshListCard(ui, snapshot, helpers, buildView)
    if not ui.frame or not ui.frame.pveWeeklyCard then
        return
    end

    local character = ns:GetSelectedCharacter()
    applyCard(ui.frame.pveWeeklyCard, buildView(character, snapshot), helpers)
end

function PveListCard.RefreshPveWeekly(ui, snapshot, helpers)
    refreshListCard(ui, snapshot, helpers, helpers.buildPveWeeklyListView)
end

function PveListCard.RefreshPvpWeekly(ui, snapshot, helpers)
    refreshListCard(ui, snapshot, helpers, helpers.buildPvpWeeklyListView)
end

function PveListCard.RefreshPveDelves(ui, snapshot, helpers)
    refreshListCard(ui, snapshot, helpers, helpers.buildPveDelvesListView)
end

function PveListCard.RefreshPveEvents(ui, snapshot, helpers)
    refreshListCard(ui, snapshot, helpers, helpers.buildPveEventsListView)
end

function PveListCard.RefreshPveDarkmoon(ui, snapshot, helpers)
    refreshListCard(ui, snapshot, helpers, helpers.buildPveDarkmoonListView)
end

function PveListCard.RefreshPveWorld(ui, snapshot, helpers)
    refreshListCard(ui, snapshot, helpers, helpers.buildPveWorldListView)
end

function PveListCard.RefreshPveDaily(ui, snapshot, helpers)
    refreshListCard(ui, snapshot, helpers, helpers.buildPveDailyListView)
end

function PveListCard.RefreshPveOther(ui, snapshot, helpers)
    refreshListCard(ui, snapshot, helpers, helpers.buildPveOtherListView)
end

function PveListCard.RefreshProfessionWeekly(ui, snapshot, helpers)
    if not ui.frame or not ui.frame.pveWeeklyCard then
        return
    end

    local card = ui.frame.pveWeeklyCard
    local character = ns:GetSelectedCharacter()
    local view = helpers.buildProfessionWeeklyListView(character, snapshot) or {}
    local weeklyEntries = view.rows or {}

    applyCard(card, view, helpers)

    local selectedKey = type(character) == "table" and character.key or character
    local isCurrentCharacter = type(ns.IsCurrentCharacterKey) == "function" and ns:IsCurrentCharacterKey(selectedKey) or false

    for index, rowData in ipairs(weeklyEntries) do
        local rowFrame = card.weeklyRows and card.weeklyRows[index] or nil
        local entry = rowData and rowData.entry or nil
        if rowFrame and rowFrame.hitbox and entry and entry.skillLineID and isCurrentCharacter then
            if type(rowFrame.hitbox.RegisterForClicks) == "function" then
                rowFrame.hitbox:RegisterForClicks("LeftButtonUp")
            end
            rowFrame.hitbox:SetScript("OnClick", function()
                helpers.openProfessionEntry(entry)
            end)
        end
    end

    local knowledgeEntries = view.knowledgeEntries or {}
    if type(knowledgeEntries) == "table" and #knowledgeEntries > 0 and card.professionKnowledgeHeader and card.professionKnowledgeRows then
        local lastKnowledgeRow = nil

        card.professionKnowledgeHeader:SetText(L.PROFESSIONS_KNOWLEDGE_LABEL or "Knowledge")
        card.professionKnowledgeHeader:Show()

        for index = 1, 2 do
            local knowledgeEntry = knowledgeEntries[index]
            local knowledgeRow = card.professionKnowledgeRows[index]
            if knowledgeRow and knowledgeEntry then
                local points = math.max(0, tonumber(knowledgeEntry.points) or 0)
                local professionName = knowledgeEntry.name or (L.UNKNOWN or "Unknown")
                local tooltipLines = {}

                if points > 0 then
                    tooltipLines[#tooltipLines + 1] = string.format(
                        L.PROFESSIONS_KNOWLEDGE_TOOLTIP_AVAILABLE or "%s has %d unspent knowledge points.",
                        professionName,
                        points
                    )
                else
                    tooltipLines[#tooltipLines + 1] = string.format(
                        L.PROFESSIONS_KNOWLEDGE_TOOLTIP_EMPTY or "%s has no unspent knowledge points.",
                        professionName
                    )
                end

                tooltipLines[#tooltipLines + 1] = isCurrentCharacter
                    and (L.PROFESSIONS_KNOWLEDGE_TOOLTIP_OPEN or "Left click: open this profession.")
                    or (L.PROFESSIONS_KNOWLEDGE_TOOLTIP_ALT or "Direct open works only for the currently logged-in character.")

                local professionBadgeTexture = helpers.getProfessionStatusBadgeTexture(knowledgeEntry)
                helpers.setWeeklyStatusRow(knowledgeRow, {
                    status = points > 0 and "open" or "missing",
                    text = points > 99 and "99+" or tostring(points),
                    hideStatusBadge = professionBadgeTexture == nil,
                    statusBadgeFullTexture = professionBadgeTexture,
                    tooltipTitle = professionName,
                    tooltipLines = tooltipLines,
                }, professionName)

                if knowledgeRow.hitbox then
                    local skillLineID = knowledgeEntry.skillLineID
                    local slotIndex = tonumber(knowledgeEntry.slotIndex) or index
                    local professionNameForClick = professionName
                    if type(knowledgeRow.hitbox.RegisterForClicks) == "function" then
                        knowledgeRow.hitbox:RegisterForClicks("LeftButtonUp")
                    end
                    knowledgeRow.hitbox:SetScript("OnClick", function()
                        if not isCurrentCharacter then
                            return
                        end

                        helpers.openProfessionEntry({
                            skillLineID = skillLineID,
                            slotIndex = slotIndex,
                            professionName = professionNameForClick,
                            tooltipTitle = professionNameForClick,
                            label = professionNameForClick,
                        })
                    end)
                end

                lastKnowledgeRow = knowledgeRow
            elseif knowledgeRow then
                knowledgeRow.tooltipTitle = nil
                knowledgeRow.tooltipLines = nil
                if knowledgeRow.hitbox then
                    knowledgeRow.hitbox:SetScript("OnClick", nil)
                end
                knowledgeRow:Hide()
            end
        end

        if lastKnowledgeRow and card.weeklyRows and card.weeklyRows[1] then
            card.weeklyRows[1]:ClearAllPoints()
            card.weeklyRows[1]:SetPoint("TOPLEFT", lastKnowledgeRow, "BOTTOMLEFT", 0, -12)
            card.weeklyRows[1]:SetPoint("TOPRIGHT", -14, 0)
            for index = 2, #card.weeklyRows do
                card.weeklyRows[index]:ClearAllPoints()
                card.weeklyRows[index]:SetPoint("TOPLEFT", card.weeklyRows[index - 1], "BOTTOMLEFT", 0, -8)
                card.weeklyRows[index]:SetPoint("TOPRIGHT", -14, 0)
            end
        end
    end
end
