-- [ WHATS NEW ] -------------------------------------------------------------------------------------
local _, Orbit = ...
local L = Orbit.L
local Constants = Orbit.Constants
local Pixel = Orbit.Engine.Pixel

-- [ CONSTANTS ] -------------------------------------------------------------------------------------
local WHATS_NEW_ENABLED = true -- set false for backend-only releases (skips auto-show)

local DISCORD_URL = "https://discord.gg/2sZj63kBqy"
local WINDOW_WIDTH = 600
local MAX_HEIGHT = 800
local CONTENT_PADDING = 10
local TEXT_PADDING_X = 16
local ENTRY_TITLE_FONT = "GameFontNormalLarge"
local ENTRY_BODY_FONT = "GameFontHighlight"
local ENTRY_SPACING = 16
local ENTRY_TITLE_BODY_GAP = 4
local CLOSE_BUTTON_OFFSET = -2
local FRAME_STRATA = "FULLSCREEN_DIALOG"
local FRAME_LEVEL = 500
local TITLE_INSET_LEFT = 30
local TITLE_INSET_RIGHT = -24
local TITLE_INSET_TOP = -1
local ESC_RESTORE_DELAY = 0.05
local SHOW_DELAY = 1.0
local SCROLLBAR_WIDTH = 26
local FOOTER_TOP_PADDING = 12
local FOOTER_BOTTOM_PADDING = 12
local FOOTER_BUTTON_HEIGHT = 20
local FOOTER_SIDE_PADDING = CONTENT_PADDING
local FOOTER_BUTTON_SPACING = 8
local FOOTER_DIVIDER_OFFSET = 6
local FOOTER_TEXT_HEIGHT = 16
local FOOTER_HEIGHT = FOOTER_TOP_PADDING + FOOTER_BUTTON_HEIGHT + FOOTER_BOTTOM_PADDING
local FOOTER_TOTAL = FOOTER_HEIGHT + FOOTER_TEXT_HEIGHT

-- [ FRAME ] -----------------------------------------------------------------------------------------
local Window = CreateFrame("Frame", "OrbitWhatsNewWindow", UIParent)
Window:SetSize(WINDOW_WIDTH, MAX_HEIGHT)
Window:SetPoint("CENTER")
Window:SetFrameStrata(FRAME_STRATA)
Window:SetFrameLevel(FRAME_LEVEL)
Window:SetMovable(true)
Window:SetClampedToScreen(true)
Window:EnableMouse(true)
Window:RegisterForDrag("LeftButton")
Window:Hide()

Window.NineSlice = CreateFrame("Frame", nil, Window, "NineSlicePanelTemplate")
Window.NineSlice.layoutType = "ButtonFrameTemplateNoPortrait"
NineSliceUtil.ApplyLayoutByName(Window.NineSlice, "ButtonFrameTemplateNoPortrait")

Window.Bg = Window:CreateTexture(nil, "BACKGROUND", nil, -6)
Window.Bg:SetTexture("Interface\\FrameGeneral\\UI-Background-Rock")
Window.Bg:SetHorizTile(true)
Window.Bg:SetVertTile(true)
Window.Bg:SetPoint("TOPLEFT", 6, -21)
Window.Bg:SetPoint("BOTTOMRIGHT", -2, 2)

Window:SetScript("OnDragStart", function(self) self:StartMoving() end)
Window:SetScript("OnDragStop", function(self)
    self:StopMovingOrSizing()
    local top, left = self:GetTop(), self:GetLeft()
    self:ClearAllPoints()
    self:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", left, top)
end)

-- [ TITLE ] -----------------------------------------------------------------------------------------
Window.TitleContainer = CreateFrame("Frame", nil, Window)
Window.TitleContainer:SetFrameLevel(FRAME_LEVEL + 10)
Window.TitleContainer:SetPoint("TOPLEFT", TITLE_INSET_LEFT, TITLE_INSET_TOP)
Window.TitleContainer:SetPoint("TOPRIGHT", TITLE_INSET_RIGHT, TITLE_INSET_TOP)
Window.TitleContainer:SetHeight(20)

Window.TitleContainer.TitleText = Window.TitleContainer:CreateFontString(nil, "OVERLAY", "GameFontNormal")
Window.TitleContainer.TitleText:SetPoint("TOP", Window.TitleContainer, "TOP", 0, -5)
Window.TitleContainer.TitleText:SetText(L.CMD_WHATSNEW_TITLE)

-- [ CLOSE BUTTON ] ----------------------------------------------------------------------------------
Window.CloseButton = CreateFrame("Button", nil, Window, "UIPanelCloseButton")
Window.CloseButton:SetPoint("TOPRIGHT", Window, "TOPRIGHT", 0, -1)
Window.CloseButton:SetFrameLevel(FRAME_LEVEL + 10)
Window.CloseButton:SetScript("OnClick", function() Window:Hide() end)

-- [ FOOTER ] ----------------------------------------------------------------------------------------
local Footer = CreateFrame("Frame", nil, Window)
Footer:SetHeight(FOOTER_HEIGHT)
Footer:SetPoint("BOTTOMLEFT", Window, "BOTTOMLEFT", 0, 0)
Footer:SetPoint("BOTTOMRIGHT", Window, "BOTTOMRIGHT", 0, 0)

local FooterDivider = Footer:CreateTexture(nil, "ARTWORK")
FooterDivider:SetSize(Constants.Panel.DividerWidth, Constants.Panel.DividerHeight)
FooterDivider:SetTexture("Interface\\FriendsFrame\\UI-FriendsFrame-OnlineDivider")
FooterDivider:SetPoint("TOP", Footer, "TOP", 0, FOOTER_DIVIDER_OFFSET)

local FooterText = Footer:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
FooterText:SetPoint("TOP", Footer, "TOP", 0, FOOTER_TEXT_HEIGHT)
FooterText:SetText(L.CMD_WHATSNEW_THANKS)

local btnTop = -FOOTER_TOP_PADDING

local DiscordButton = CreateFrame("Button", nil, Footer, "UIPanelButtonTemplate")
DiscordButton:SetText(L.CMN_DISCORD)
DiscordButton:SetHeight(FOOTER_BUTTON_HEIGHT)
DiscordButton:SetPoint("TOPLEFT", Footer, "TOPLEFT", FOOTER_SIDE_PADDING, btnTop)
DiscordButton:SetPoint("RIGHT", Footer, "CENTER", -FOOTER_BUTTON_SPACING / 2, 0)
-- [ DISCORD DIALOG ] --------------------------------------------------------------------------------
local DISCORD_DIALOG_WIDTH = 380
local DISCORD_DIALOG_HEIGHT = 155
local DISCORD_EDITBOX_HEIGHT = 24

local DiscordDialog = CreateFrame("Frame", "OrbitDiscordDialog", UIParent, "DefaultPanelTemplate")
DiscordDialog:SetSize(DISCORD_DIALOG_WIDTH, DISCORD_DIALOG_HEIGHT)
DiscordDialog:SetPoint("CENTER", Window, "CENTER", 0, 0)
DiscordDialog:SetFrameStrata(Orbit.Constants.Strata.Topmost)
DiscordDialog:SetFrameLevel(FRAME_LEVEL + Orbit.Constants.Levels.WhatsNewDialog)
DiscordDialog:SetMovable(true)
DiscordDialog:SetClampedToScreen(true)
DiscordDialog:EnableMouse(true)
DiscordDialog:RegisterForDrag("LeftButton")
DiscordDialog:Hide()

DiscordDialog:SetScript("OnDragStart", function(self) self:StartMoving() end)
DiscordDialog:SetScript("OnDragStop", function(self) self:StopMovingOrSizing() end)

table.insert(UISpecialFrames, "OrbitDiscordDialog")

DiscordDialog.TitleContainer.TitleText:SetText(L.CMD_DISCORD_TITLE)

local ddDesc = DiscordDialog:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
ddDesc:SetPoint("TOP", DiscordDialog, "TOP", 0, -38)
ddDesc:SetWidth(DISCORD_DIALOG_WIDTH - 40)
ddDesc:SetJustifyH("CENTER")
ddDesc:SetText(L.CMD_DISCORD_BODY)

local ddEditBox = CreateFrame("EditBox", nil, DiscordDialog, "InputBoxTemplate")
ddEditBox:SetHeight(DISCORD_EDITBOX_HEIGHT)
ddEditBox:SetPoint("LEFT", DiscordDialog, "LEFT", CONTENT_PADDING * 2, 0)
ddEditBox:SetPoint("RIGHT", DiscordDialog, "RIGHT", -CONTENT_PADDING * 2, 0)
ddEditBox:SetPoint("TOP", ddDesc, "BOTTOM", 0, -10)
ddEditBox:SetAutoFocus(false)
ddEditBox:SetScript("OnChar", function(self)
    self:SetText(DISCORD_URL)
    self:HighlightText()
end)
ddEditBox:SetScript("OnEscapePressed", function(self) DiscordDialog:Hide() end)
ddEditBox:SetScript("OnEditFocusGained", function(self) self:HighlightText() end)

local ddFooter = CreateFrame("Frame", nil, DiscordDialog)
ddFooter:SetHeight(FOOTER_HEIGHT)
ddFooter:SetPoint("BOTTOMLEFT", DiscordDialog, "BOTTOMLEFT", 0, 0)
ddFooter:SetPoint("BOTTOMRIGHT", DiscordDialog, "BOTTOMRIGHT", 0, 0)

local ddDivider = ddFooter:CreateTexture(nil, "ARTWORK")
ddDivider:SetSize(Constants.Panel.DividerWidth, Constants.Panel.DividerHeight)
ddDivider:SetTexture("Interface\\FriendsFrame\\UI-FriendsFrame-OnlineDivider")
ddDivider:SetPoint("TOP", ddFooter, "TOP", 0, FOOTER_DIVIDER_OFFSET)

local ddCloseBtn = CreateFrame("Button", nil, ddFooter, "UIPanelButtonTemplate")
ddCloseBtn:SetText(L.CMN_CLOSE)
ddCloseBtn:SetHeight(FOOTER_BUTTON_HEIGHT)
ddCloseBtn:SetPoint("TOPLEFT", ddFooter, "TOPLEFT", FOOTER_SIDE_PADDING, -FOOTER_TOP_PADDING)
ddCloseBtn:SetPoint("TOPRIGHT", ddFooter, "TOPRIGHT", -FOOTER_SIDE_PADDING, -FOOTER_TOP_PADDING)
ddCloseBtn:SetScript("OnClick", function() DiscordDialog:Hide() end)

DiscordDialog:SetScript("OnShow", function()
    ddEditBox:SetText(DISCORD_URL)
    ddEditBox:HighlightText()
    ddEditBox:SetFocus()
end)

DiscordButton:SetScript("OnClick", function() DiscordDialog:Show() end)

local CloseButton = CreateFrame("Button", nil, Footer, "UIPanelButtonTemplate")
CloseButton:SetText(L.CMN_CLOSE)
CloseButton:SetHeight(FOOTER_BUTTON_HEIGHT)
CloseButton:SetPoint("LEFT", Footer, "CENTER", FOOTER_BUTTON_SPACING / 2, 0)
CloseButton:SetPoint("TOPRIGHT", Footer, "TOPRIGHT", -FOOTER_SIDE_PADDING, btnTop)
CloseButton:SetScript("OnClick", function() Window:Hide() end)

-- [ SCROLL FRAME ] ----------------------------------------------------------------------------------
-- DefaultPanelTemplate Bg insets: left=6 right=2 top=21 | border | 10px | content | 10px | slider | 2px | border |

local BG_LEFT = 6
local BG_RIGHT = 2
local BG_TOP = 21

local ScrollFrame = CreateFrame("ScrollFrame", nil, Window, "ScrollFrameTemplate")
ScrollFrame:SetPoint("TOPLEFT", Window, "TOPLEFT", BG_LEFT + CONTENT_PADDING, -(BG_TOP + CONTENT_PADDING))
ScrollFrame:SetPoint("BOTTOMRIGHT", Window, "BOTTOMRIGHT", -(BG_RIGHT + SCROLLBAR_WIDTH), FOOTER_TOTAL + CONTENT_PADDING)

if ScrollFrame.ScrollBar then
    ScrollFrame.ScrollBar:SetAlpha(0)
end

local Content = CreateFrame("Frame", nil, ScrollFrame)
ScrollFrame:SetScrollChild(Content)

-- [ RENDER ENTRIES ] --------------------------------------------------------------------------------
-- FontStrings cannot be destroyed; pool them by render index so re-opens reuse the same set.
local fontStringPool = {}
local renderCursor = 0

local function AcquireFontString(fontObject)
    renderCursor = renderCursor + 1
    local fs = fontStringPool[renderCursor]
    if not fs then
        fs = Content:CreateFontString(nil, "ARTWORK", fontObject)
        fontStringPool[renderCursor] = fs
    else
        fs:SetFontObject(fontObject)
    end
    fs:ClearAllPoints()
    fs:SetWidth(0)
    fs:SetJustifyH("LEFT")
    fs:Show()
    return fs
end

local function FormatMarkdown(text)
    if not text then
        return ""
    end
    -- Fix double-escaped newlines and quotes (common in generated Lua)
    text = text:gsub("\\n", "\n")
    text = text:gsub('\\"', '"')

    -- Bold: **text** -> Gold
    text = text:gsub("%*%*(.-)%*%*", "|cFFFFD100%1|r")

    -- Code: `text` -> Cyan
    text = text:gsub("`(.-)`", "|cFF00D4FF%1|r")

    -- Bullets handling is now done by the layout engine
    return text
end

local function RenderEntries()
    renderCursor = 0

    local contentWidth = ScrollFrame:GetWidth() - (TEXT_PADDING_X * 2)
    Content:SetWidth(ScrollFrame:GetWidth())

    local yOffset = 0
    if not Orbit.WHATS_NEW_ENTRIES then
        return
    end
    local scale = Content:GetEffectiveScale()
    for _, entry in ipairs(Orbit.WHATS_NEW_ENTRIES) do
        local titleText = AcquireFontString(ENTRY_TITLE_FONT)
        titleText:SetPoint("TOPLEFT", Content, "TOPLEFT", TEXT_PADDING_X, -Pixel:Snap(yOffset, scale))
        titleText:SetWidth(contentWidth)
        titleText:SetText("|cFFFFD100" .. FormatMarkdown(entry.title) .. "|r")
        yOffset = yOffset + titleText:GetStringHeight() + ENTRY_TITLE_BODY_GAP

        local lines = {strsplit("\n", entry.body)}
        for _, rawLine in ipairs(lines) do
            if not rawLine or strtrim(rawLine) == "" then
                yOffset = yOffset + 8
            else
                local leadingSpaces = rawLine:match("^(%s*)")
                local baseIndent = 0
                if leadingSpaces then
                    baseIndent = math.floor(#leadingSpaces / 4) * 16
                end

                local lineText = strtrim(rawLine)
                local isBullet = lineText:match("^%- ")
                local indentX = baseIndent

                if isBullet then
                    local bulletFS = AcquireFontString(ENTRY_BODY_FONT)
                    bulletFS:SetPoint("TOPLEFT", Content, "TOPLEFT", TEXT_PADDING_X + baseIndent, -Pixel:Snap(yOffset, scale))
                    local marker = baseIndent > 0 and "-" or "•"
                    bulletFS:SetText("|cFFFFD100" .. marker .. "|r")

                    indentX = baseIndent + 14
                    lineText = lineText:sub(3)
                end

                local lineFS = AcquireFontString(ENTRY_BODY_FONT)
                lineFS:SetPoint("TOPLEFT", Content, "TOPLEFT", TEXT_PADDING_X + indentX, -Pixel:Snap(yOffset, scale))
                lineFS:SetWidth(contentWidth - indentX)
                lineFS:SetText(FormatMarkdown(lineText))

                yOffset = yOffset + lineFS:GetStringHeight() + 6
            end
        end
        yOffset = yOffset + (ENTRY_SPACING - 6)
    end
    for i = renderCursor + 1, #fontStringPool do
        fontStringPool[i]:Hide()
        fontStringPool[i]:SetText("")
    end
    Content:SetHeight(Pixel:Snap(yOffset, scale))

    local desiredHeight = BG_TOP + CONTENT_PADDING + yOffset + CONTENT_PADDING + FOOTER_TOTAL
    Window:SetHeight(math.min(desiredHeight, MAX_HEIGHT))
    if ScrollFrame.ScrollBar then
        ScrollFrame.ScrollBar:SetAlpha(desiredHeight > MAX_HEIGHT and 1 or 0)
    end
end

Window:SetScript("OnShow", function(self)
    PlaySound(SOUNDKIT.IG_MAINMENU_OPEN)
    RunNextFrame(function() RenderEntries() end)
end)

-- [ ESC KEY ] ---------------------------------------------------------------------------------------
table.insert(UISpecialFrames, "OrbitWhatsNewWindow")

Window:SetPropagateKeyboardInput(true)
Window:SetScript("OnKeyDown", function(self, key)
    if key == "ESCAPE" then
        if InCombatLockdown() then
            return
        end
        self:SetPropagateKeyboardInput(false)
        self:Hide()
        C_Timer.After(ESC_RESTORE_DELAY, function()
            if not InCombatLockdown() then
                self:SetPropagateKeyboardInput(true)
            end
        end)
    end
end)

-- [ COMBAT HIDE ] -----------------------------------------------------------------------------------
Window:RegisterEvent("PLAYER_REGEN_DISABLED")
Window:SetScript("OnEvent", function(self, event)
    if event == "PLAYER_REGEN_DISABLED" and self:IsShown() then
        self:Hide()
    end
end)

-- [ HIDE HANDLER ] ----------------------------------------------------------------------------------
Window:SetScript("OnHide", function()
    PlaySound(SOUNDKIT.IG_MAINMENU_CLOSE)
    if Orbit.db and Orbit.db.AccountSettings then
        Orbit.db.AccountSettings.WhatsNewRead = Orbit.LatestChangelogVersion
    end
end)

-- [ PUBLIC API ] ------------------------------------------------------------------------------------
function Orbit:ShowWhatsNew()
    if InCombatLockdown() then
        return
    end
    Window:Show()
end

-- [ AUTO-SHOW ON LOGIN ] ----------------------------------------------------------------------------
local trigger = CreateFrame("Frame")
trigger:RegisterEvent("PLAYER_LOGIN")
trigger:SetScript("OnEvent", function(self)
    self:UnregisterAllEvents()
    C_Timer.After(SHOW_DELAY, function()
        if not WHATS_NEW_ENABLED then
            return
        end
        if InCombatLockdown() then
            return
        end
        if not Orbit.db or not Orbit.db.AccountSettings then
            return
        end
        local as = Orbit.db.AccountSettings
        -- Brand-new install (no version ever acknowledged): suppress patch notes so onboarding leads instead.
        if as.WhatsNewRead == nil then
            as.WhatsNewRead = Orbit.LatestChangelogVersion
            return
        end
        if as.WhatsNewRead == Orbit.LatestChangelogVersion then
            return
        end
        Orbit:ShowWhatsNew()
    end)
end)
