EbonholdHub.TalentsView = {}

local UI = EbonholdHub.UI
local C  = UI.Colors
local ST = EbonholdHub.SiteTheme
local SW = EbonholdHub.SiteWidgets
local SC = ST and ST.C
local SL = ST and ST.Layout
local T  = EbonholdHub.Talents

local HELP_TEXT = table.concat({
    "Plan which talents to take while leveling or in endgame.",
    "",
    "Left-click a talent icon — add 1 point to the build plan.",
    "Right-click — remove 1 point.",
    "Shift + click — fill to max rank.",
    "",
    "Copy from character — import your current talents.",
    "Clear plan — start from scratch.",
    "",
    "Trees are read from the game for your current class.",
    "Build class must match your character to edit trees.",
}, "\n")

local HEADER_PAD = 6
local ACTION_ROW_H = 24
local GAP = 4
local SECTION_GAP = 8
local ICON_SIZE = 32
local CELL_STEP = 40
local MIN_CELL_STEP = 36
local TREE_PAD = 8

local viewFrame
local headerPanel, headerActions
local bodyFrame, treesScroll, treesScrollChild, treesHolder
local mismatchLabel
local treePanels = {}
local talentButtons = {}
local eventFrame

local function GetPlan()
    return EbonholdHub.BuildForm.GetEditingTalents()
end

local function ClearTalentOrder()
    local order = EbonholdHub.BuildForm.GetEditingTalentOrder()
    for i = #order, 1, -1 do order[i] = nil end
end

local function LayoutHeader()
    if not headerPanel then return end
    local root = headerPanel:GetParent()
    if not root then return end
    local totalH = HEADER_PAD * 2 + ACTION_ROW_H
    headerPanel:ClearAllPoints()
    headerPanel:SetHeight(totalH)
    headerPanel:SetPoint("TOPLEFT",  root, "TOPLEFT",  0, 0)
    headerPanel:SetPoint("TOPRIGHT", root, "TOPRIGHT", 0, 0)
end

local function ClassMatchesBuild()
    local buildClass = EbonholdHub.BuildForm.GetEditingClass()
    local _, playerClass = UnitClass("player")
    return buildClass and playerClass and buildClass == playerClass
end

local function TalentBorderColor(planRank, playerRank)
    if planRank <= 0 then
        return C.border
    end
    if playerRank >= planRank then
        return { 0.25, 0.85, 0.35, 1.00 }
    end
    if playerRank > 0 then
        return { 0.95, 0.75, 0.20, 1.00 }
    end
    return C.accent
end

local function RefreshTalentButtons()
    local plan = GetPlan()
    local trees = viewFrame and viewFrame._trees
    if not trees then return end
    T.RefreshPlayerRanks(trees)

    for key, btn in pairs(talentButtons) do
        local tab, ti = T.ParseKey(key)
        local info = T.FindTalent(trees, tab, ti)
        if info then
            local planRank = T.GetRank(plan, tab, ti)
            local playerRank = info.playerRank or 0
            btn._rankText:SetText(planRank > 0 and (planRank .. "/" .. info.maxRank) or "")
            local bc = TalentBorderColor(planRank, playerRank)
            SW.SetBorderColor(btn._border, bc)
            if planRank >= info.maxRank then
                btn._icon:SetDesaturated(false)
                btn._icon:SetVertexColor(1, 1, 1, 1)
            elseif planRank > 0 then
                btn._icon:SetDesaturated(false)
                btn._icon:SetVertexColor(1, 1, 1, 1)
            else
                btn._icon:SetDesaturated(true)
                btn._icon:SetVertexColor(0.55, 0.55, 0.55, 1)
            end
        end
    end

    for tab, panel in pairs(treePanels) do
        local tree = trees[tab]
        if panel._pointsText and tree then
            panel._pointsText:SetText(T.PointsInTab(plan, tab) .. " pts")
        end
    end

end

local function OnTalentClick(btn, tab, ti, info, shift)
    if not ClassMatchesBuild() then return end
    local plan = GetPlan()
    if shift then
        while T.GetRank(plan, tab, ti) < info.maxRank and T.TotalPoints(plan) < T.MAX_POINTS do
            if not T.AddPoint(plan, nil, tab, ti, info) then break end
        end
    else
        T.AddPoint(plan, nil, tab, ti, info)
    end
    ClearTalentOrder()
    RefreshTalentButtons()
end

local function OnTalentRightClick(btn, tab, ti)
    if not ClassMatchesBuild() then return end
    T.RemovePoint(GetPlan(), nil, tab, ti)
    ClearTalentOrder()
    RefreshTalentButtons()
end

local function CreateTalentButton(parent, tab, ti, info)
    local key = T.MakeKey(tab, ti)
    local btn = CreateFrame("Button", nil, parent)
    btn:SetSize(ICON_SIZE, ICON_SIZE)
    btn._tab = tab
    btn._ti = ti
    btn._info = info

    SW.Fill(btn, SC.elementBg)
    btn._border = SW.ThinBorder(btn, SC.border, 1)

    local icon = btn:CreateTexture(nil, "ARTWORK")
    icon:SetPoint("TOPLEFT",     btn, "TOPLEFT",     2, -2)
    icon:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", -2,  2)
    icon:SetTexture(info.icon or "Interface\\Icons\\INV_Misc_QuestionMark")
    icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
    btn._icon = icon

    local rankText = btn:CreateFontString(nil, "OVERLAY")
    SW.SetFont(rankText, 9, SC.text, true)
    rankText:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", -1, 1)
    btn._rankText = rankText

    btn:RegisterForClicks("LeftButtonUp", "RightButtonUp")
    btn:SetScript("OnClick", function(self, button)
        if button == "RightButton" then
            OnTalentRightClick(self, tab, ti)
        else
            OnTalentClick(self, tab, ti, info, IsShiftKeyDown())
        end
    end)
    btn:SetScript("OnEnter", function(self)
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        GameTooltip:ClearLines()
        GameTooltip:SetText(info.name or "?", 1, 1, 1)
        local planRank = T.GetRank(GetPlan(), tab, ti)
        local playerRank = info.playerRank or 0
        GameTooltip:AddLine(string.format("Plan: %d / %d", planRank, info.maxRank), 0.8, 0.8, 0.8)
        GameTooltip:AddLine(string.format("Your character: %d / %d", playerRank, info.maxRank), 0.8, 0.8, 0.8)
        GameTooltip:AddLine("Click +1 | Right-click -1 | Shift+click max", 0.55, 0.55, 0.60)
        GameTooltip:Show()
    end)
    btn:SetScript("OnLeave", function() GameTooltip:Hide() end)

    talentButtons[key] = btn
    return btn
end

local function BuildTreePanel(parent, tree, specIndex)
    local panel = CreateFrame("Frame", nil, parent)
    SW.Fill(panel, SC.statCellBg)
    SW.ThinBorder(panel, SC.border, 1)
    panel._buttons = {}

    local header = SW.Label(panel, tree.name, 12, SC.text, false, "semibold")
    header:SetPoint("TOPLEFT", panel, "TOPLEFT", TREE_PAD, -8)

    local pointsText = SW.Label(panel, "0 pts", 10, SC.textMuted)
    pointsText:SetPoint("TOPRIGHT", panel, "TOPRIGHT", -TREE_PAD, -10)
    panel._pointsText = pointsText

    if specIndex then
        local build = EbonholdHub.Build.GetActive()
        local isPrimary = build and (build.spec or 1) == specIndex
        if isPrimary then
            local accent = panel:CreateTexture(nil, "ARTWORK")
            accent:SetTexture(ST.FLAT)
            accent:SetVertexColor(unpack(SC.accent))
            accent:SetPoint("TOPLEFT",  panel, "TOPLEFT",  0, 0)
            accent:SetPoint("TOPRIGHT", panel, "TOPRIGHT", 0, 0)
            accent:SetHeight(2)
        end
    end

    for ti, info in pairs(tree.talents) do
        panel._buttons[ti] = CreateTalentButton(panel, tree.index, ti, info)
    end

    treePanels[tree.index] = panel
    return panel
end

local function GetAvailWidth()
    if treesScroll and treesScroll.scrollFrame then
        local w = treesScroll.scrollFrame:GetWidth()
        if w and w > 1 then return w end
    end
    if treesHolder then
        local w = treesHolder:GetWidth()
        if w and w > 1 then return w end
    end
    if bodyFrame then
        local w = bodyFrame:GetWidth()
        if w and w > 1 then return w end
    end
    return 0
end

local function LayoutTreeColumns()
    if not treesScrollChild then return end
    local trees = viewFrame and viewFrame._trees
    if not trees then return end

    local numTabs = T.GetTabCount(trees)
    if numTabs < 1 then return end

    local availW = GetAvailWidth()
    if availW < 1 then return end

    local gaps = (numTabs - 1) * SECTION_GAP
    local colW = math.floor((availW - gaps) / numTabs)
    local maxH = 0

    for tab = 1, numTabs do
        local tree = trees[tab]
        local panel = treePanels[tab]
        if tree and panel then
            local innerW = colW - TREE_PAD * 2
            local colStep = math.max(MIN_CELL_STEP, math.floor(innerW / math.max(tree.maxCol, 1)))
            local gridW = (tree.maxCol - 1) * colStep + ICON_SIZE
            local offsetX = TREE_PAD + math.max(0, math.floor((innerW - gridW) / 2))
            local panelH = 36 + tree.maxTier * colStep + TREE_PAD
            if panelH > maxH then maxH = panelH end

            local x = (tab - 1) * (colW + SECTION_GAP)
            panel:ClearAllPoints()
            panel:SetPoint("TOPLEFT", treesScrollChild, "TOPLEFT", x, 0)
            panel:SetSize(colW, panelH)
            panel:Show()

            for ti, btn in pairs(panel._buttons) do
                local info = tree.talents[ti]
                if info and btn then
                    local bx = offsetX + (info.column - 1) * colStep
                    local by = -36 - (info.tier - 1) * colStep
                    btn:ClearAllPoints()
                    btn:SetPoint("TOPLEFT", panel, "TOPLEFT", bx, by)
                end
            end
        end
    end

    treesScrollChild:SetWidth(availW)
    treesScrollChild:SetHeight(math.max(maxH, 1))
    if treesScroll and treesScroll.UpdateThumb then
        treesScroll.UpdateThumb()
    end
end

local function CreatePlainScroll(parent, insetRight)
    insetRight = insetRight or 10
    local holder = CreateFrame("Frame", nil, parent)
    holder:SetPoint("TOPLEFT", parent, "TOPLEFT", 0, 0)
    holder:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", 0, 0)

    local scroll = CreateFrame("ScrollFrame", nil, holder)
    scroll:SetPoint("TOPLEFT", holder, "TOPLEFT", 0, 0)
    scroll:SetPoint("BOTTOMRIGHT", holder, "BOTTOMRIGHT", -insetRight, 0)

    local child = CreateFrame("Frame", nil, scroll)
    child:SetHeight(1)
    scroll:SetScrollChild(child)

    local track = CreateFrame("Frame", nil, holder)
    track:SetPoint("TOPRIGHT",    holder, "TOPRIGHT",    -2, 0)
    track:SetPoint("BOTTOMRIGHT", holder, "BOTTOMRIGHT", -2, 0)
    track:SetWidth(4)
    SW.Fill(track, SC.borderSoft)

    local thumb = CreateFrame("Frame", nil, track)
    thumb:SetWidth(4)
    thumb:SetPoint("TOP", track, "TOP", 0, 0)
    SW.Fill(thumb, SC.accent)

    local function UpdateThumb()
        local range = child:GetHeight() - scroll:GetHeight()
        if range <= 0 then
            thumb:SetHeight(track:GetHeight())
            thumb:SetPoint("TOP", track, "TOP", 0, 0)
            return
        end
        local trackH = track:GetHeight()
        local thumbH = math.max(20, trackH * (scroll:GetHeight() / child:GetHeight()))
        thumb:SetHeight(thumbH)
        local offset = (scroll:GetVerticalScroll() / range) * (trackH - thumbH)
        thumb:SetPoint("TOP", track, "TOP", 0, -offset)
    end

    scroll:EnableMouseWheel(true)
    scroll:SetScript("OnMouseWheel", function(self, delta)
        local range = child:GetHeight() - self:GetHeight()
        if range <= 0 then return end
        local newPos = self:GetVerticalScroll() - delta * 30
        newPos = math.max(0, math.min(range, newPos))
        self:SetVerticalScroll(newPos)
        UpdateThumb()
    end)
    scroll:SetScript("OnScrollRangeChanged", UpdateThumb)
    scroll:SetScript("OnSizeChanged", function()
        LayoutTreeColumns()
        UpdateThumb()
    end)

    holder.scrollFrame = scroll
    holder.scrollChild = child
    holder.UpdateThumb = UpdateThumb
    return holder, child
end

local function RebuildTrees()
    if not treesScrollChild then return end
    for _, panel in pairs(treePanels) do panel:Hide() end
    treePanels = {}
    talentButtons = {}

    local trees = T.ScanTrees()
    viewFrame._trees = trees

    if mismatchLabel then
        if ClassMatchesBuild() then
            mismatchLabel:Hide()
            treesHolder:SetPoint("TOPLEFT", bodyFrame, "TOPLEFT", 0, 0)
        else
            local buildClass = EbonholdHub.BuildForm.GetEditingClass() or "?"
            mismatchLabel:SetText(string.format(
                "|cffff6666Build class (%s) differs from your character. Switch class or edit Overview to view trees.|r",
                buildClass))
            mismatchLabel:Show()
            treesHolder:SetPoint("TOPLEFT", mismatchLabel, "BOTTOMLEFT", -4, -GAP)
        end
    end

    if not trees or not ClassMatchesBuild() then
        treesScrollChild:SetWidth(1)
        treesScrollChild:SetHeight(80)
        if treesScroll and treesScroll.UpdateThumb then treesScroll.UpdateThumb() end
        RefreshPointsLabel()
        return
    end

    for tab = 1, T.GetTabCount(trees) do
        local tree = trees[tab]
        if tree then
            BuildTreePanel(treesScrollChild, tree, tab)
        end
    end

    LayoutTreeColumns()
    RefreshTalentButtons()
end

local layoutDeferFrame

local function DeferLayoutTrees()
    if not layoutDeferFrame then
        layoutDeferFrame = CreateFrame("Frame")
        layoutDeferFrame:SetScript("OnUpdate", function(self)
            self:Hide()
            if viewFrame and viewFrame:IsShown() then
                LayoutTreeColumns()
                RefreshTalentButtons()
            end
        end)
    end
    layoutDeferFrame:Show()
end

local function BuildViewFrame(parent)
    local f = CreateFrame("Frame", nil, parent)
    SW.Fill(f, SC.mainBg)

    -- Header: thin toolbar with two action buttons only
    headerPanel = CreateFrame("Frame", nil, f)
    SW.Fill(headerPanel, SC.sidebarBg)
    local headerSep = headerPanel:CreateTexture(nil, "ARTWORK")
    headerSep:SetTexture(ST.FLAT)
    headerSep:SetVertexColor(unpack(SC.border))
    headerSep:SetHeight(1)
    headerSep:SetPoint("BOTTOMLEFT",  headerPanel, "BOTTOMLEFT",  0, 0)
    headerSep:SetPoint("BOTTOMRIGHT", headerPanel, "BOTTOMRIGHT", 0, 0)

    headerActions = CreateFrame("Frame", nil, headerPanel)
    headerActions:SetHeight(ACTION_ROW_H)
    headerActions:SetPoint("TOPLEFT",  headerPanel, "TOPLEFT",  HEADER_PAD, -HEADER_PAD)
    headerActions:SetPoint("TOPRIGHT", headerPanel, "TOPRIGHT", -HEADER_PAD, -HEADER_PAD)

    local copyBtn = SW.CreateOutlineButton(headerActions, "Copy from character", 152)
    copyBtn:SetHeight(24)
    copyBtn:SetPoint("LEFT", headerActions, "LEFT", 0, 0)
    copyBtn:SetScript("OnClick", function()
        if not ClassMatchesBuild() then return end
        local trees = viewFrame._trees or T.ScanTrees()
        local plan = select(1, T.ReadPlayerPlan(trees))
        local dstPlan = GetPlan()
        T.ClearPlan(dstPlan, nil)
        for k, v in pairs(plan) do dstPlan[k] = v end
        ClearTalentOrder()
        RefreshTalentButtons()
    end)

    local clearBtn = SW.CreateOutlineButton(headerActions, "Clear plan", 90)
    clearBtn:SetHeight(24)
    clearBtn:SetPoint("LEFT", copyBtn, "RIGHT", 6, 0)
    clearBtn:SetScript("OnClick", function()
        T.ClearPlan(GetPlan(), nil)
        ClearTalentOrder()
        RefreshTalentButtons()
    end)

    bodyFrame = CreateFrame("Frame", nil, f)
    bodyFrame:SetPoint("TOPLEFT",     headerPanel, "BOTTOMLEFT",  0, -GAP)
    bodyFrame:SetPoint("BOTTOMRIGHT", f,           "BOTTOMRIGHT", 0,  0)
    SW.Fill(bodyFrame, SC.mainBg)

    mismatchLabel = SW.Label(bodyFrame, "", 11, SC.textMuted)
    mismatchLabel:SetPoint("TOPLEFT",  bodyFrame, "TOPLEFT",  4, -2)
    mismatchLabel:SetPoint("TOPRIGHT", bodyFrame, "TOPRIGHT", -4, -2)
    mismatchLabel:SetJustifyH("LEFT")
    mismatchLabel:Hide()

    treesHolder = CreateFrame("Frame", nil, bodyFrame)
    treesHolder:SetPoint("TOPLEFT",     bodyFrame, "TOPLEFT",     0, 0)
    treesHolder:SetPoint("BOTTOMRIGHT", bodyFrame, "BOTTOMRIGHT", 0, 0)

    treesScroll, treesScrollChild = CreatePlainScroll(treesHolder, 10)

    helpVisible = false
    f._layoutV7 = true
    LayoutHeader()
    return f
end

local function ResetView()
    viewFrame = nil
    treePanels = {}
    talentButtons = {}
    treesScroll = nil
    treesScrollChild = nil
    treesHolder = nil
    mismatchLabel = nil
    headerActions = nil
end

local function EnsureBuilt(container)
    if viewFrame and viewFrame._layoutV7 then return end
    ResetView()
    viewFrame = BuildViewFrame(container)
end

function EbonholdHub.TalentsView.Mount(container)
    EnsureBuilt(container)
    viewFrame:SetParent(container)
    viewFrame:ClearAllPoints()
    viewFrame:SetAllPoints(container)
    LayoutHeader()
    RebuildTrees()
    DeferLayoutTrees()
    viewFrame:Show()
end

function EbonholdHub.TalentsView.Unmount()
    if viewFrame then viewFrame:Hide() end
end

function EbonholdHub.TalentsView.Init()
    if eventFrame then return end
    eventFrame = CreateFrame("Frame")
    eventFrame:RegisterEvent("PLAYER_TALENT_UPDATE")
    eventFrame:SetScript("OnEvent", function()
        if viewFrame and viewFrame:IsShown() then
            RefreshTalentButtons()
        end
    end)
    if EbonholdHub.BuildForm and EbonholdHub.BuildForm.OnClassChanged then
        EbonholdHub.BuildForm.OnClassChanged(function()
            if viewFrame and viewFrame:IsShown() then
                RebuildTrees()
            end
        end)
    end
end
