-- [ WHATS NEW ] -------------------------------------------------------------------------------------
-- Shows a changelog popup once per account after each Orbit update.
-- Update WHATS_NEW_ENTRIES each release.

local _, Orbit = ...
local Constants = Orbit.Constants

-- [ 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 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", 30, -1)
Window.TitleContainer:SetPoint("TOPRIGHT", -24, -1)
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("|cFFFFD100Orbit: What's New|r")

-- [ 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("Thanks for your support! - MoONSHO7")

local btnTop = -FOOTER_TOP_PADDING

local DiscordButton = CreateFrame("Button", nil, Footer, "UIPanelButtonTemplate")
DiscordButton:SetText("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("|cFFFFD100Discord|r")

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("Join the discord to request features, report bugs\nor ask for help from the community.")

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("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("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 ] --------------------------------------------------------------------------------

local renderedFontStrings = {}

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()
    for _, fs in ipairs(renderedFontStrings) do
        fs:Hide()
        fs:SetText("")
    end
    wipe(renderedFontStrings)

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

    local yOffset = 0
    if not Orbit.WHATS_NEW_ENTRIES then
        return
    end
    for _, entry in ipairs(Orbit.WHATS_NEW_ENTRIES) do
        local titleText = Content:CreateFontString(nil, "ARTWORK", ENTRY_TITLE_FONT)
        titleText:SetPoint("TOPLEFT", Content, "TOPLEFT", TEXT_PADDING_X, -yOffset)
        titleText:SetWidth(contentWidth)
        titleText:SetJustifyH("LEFT")
        titleText:SetText("|cFFFFD100" .. FormatMarkdown(entry.title) .. "|r")
        tinsert(renderedFontStrings, titleText)
        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 = Content:CreateFontString(nil, "ARTWORK", ENTRY_BODY_FONT)
                    bulletFS:SetPoint("TOPLEFT", Content, "TOPLEFT", TEXT_PADDING_X + baseIndent, -yOffset)
                    local marker = baseIndent > 0 and "-" or "•"
                    bulletFS:SetText("|cFFFFD100" .. marker .. "|r")
                    bulletFS:SetJustifyH("LEFT")
                    tinsert(renderedFontStrings, bulletFS)
                    
                    indentX = baseIndent + 14
                    lineText = lineText:sub(3)
                end

                local lineFS = Content:CreateFontString(nil, "ARTWORK", ENTRY_BODY_FONT)
                lineFS:SetPoint("TOPLEFT", Content, "TOPLEFT", TEXT_PADDING_X + indentX, -yOffset)
                lineFS:SetWidth(contentWidth - indentX)
                lineFS:SetJustifyH("LEFT")
                lineFS:SetText(FormatMarkdown(lineText))
                tinsert(renderedFontStrings, lineFS)

                yOffset = yOffset + lineFS:GetStringHeight() + 6
            end
        end
        yOffset = yOffset + (ENTRY_SPACING - 6)
    end
    Content:SetHeight(yOffset)

    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 then
        Orbit.db.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 then
            return
        end
        if Orbit.db.WhatsNewRead == Orbit.LatestChangelogVersion then
            return
        end
        Orbit:ShowWhatsNew()
    end)
end)
