local _, addonTable = ...
---@type MK
local MK = addonTable.MK
local core = addonTable
local TRANSLATE = core.TRANSLATE or function(msg) return msg end

core.DebugTab = core.DebugTab or {}
local DebugTab = core.DebugTab
local Debug = core.Debug or {}

local DEBUG_CONTENT_HEIGHT = 452
local DEBUG_CONTENT_TOP_OFFSET = 20
local DEBUG_CONTENT_BOTTOM_PADDING = 20
local DEBUG_FORM_WIDTH = 820
local DEBUG_TITLE_TO_CONTROLS_GAP = 14
local DEBUG_SECTION_GAP = 16
local DEBUG_SCROLLBAR_RIGHT_OFFSET = 18
local DEBUG_BUTTON_WIDTH = 112
local DEBUG_BUTTON_HEIGHT = 26
local DEBUG_BUTTON_GAP_X = 10
local DEBUG_INPUT_WIDTH = 174
local DEBUG_INPUT_HEIGHT = 28
local DEBUG_CONTROL_WIDTH = 500
local DEBUG_CHECK_BUTTON_SIZE = 26

local function ApplyDebugFont(fontString, size)
    if fontString and core.ApplyFont then
        core.ApplyFont(fontString, size or 13, "")
    end
end

local function GetDebugRowHeight()
    local layout = core.MK_OPTIONS_LAYOUT or {}
    return layout.panelRowHeight or 40
end

local function GetDebugSettingsPanelHeight(rowCount)
    return math.max(tonumber(rowCount) or 0, 0) * GetDebugRowHeight()
end

local function UpdateDebugScrollState(widgets)
    if not widgets or not widgets.debugScrollFrame or not widgets.debugContent or not widgets.debugScrollBar then
        return
    end

    local scrollFrame = widgets.debugScrollFrame
    local content = widgets.debugContent
    local scrollBar = widgets.debugScrollBar

    if scrollFrame.UpdateScrollChildRect then
        scrollFrame:UpdateScrollChildRect()
    end

    local viewHeight = scrollFrame:GetHeight() or 0
    content:SetHeight(math.max(content.mkDebugContentHeight or DEBUG_CONTENT_HEIGHT, viewHeight))

    local contentHeight = content:GetHeight() or 0
    local frameScrollRange = scrollFrame.GetVerticalScrollRange and scrollFrame:GetVerticalScrollRange() or 0
    local maxScroll = math.max(frameScrollRange, contentHeight - viewHeight, 0)
    local currentValue
    if scrollBar.mkGetScrollValue then
        currentValue = scrollBar.mkGetScrollValue()
    elseif scrollBar.GetValue then
        currentValue = scrollBar:GetValue()
    else
        currentValue = 0
    end
    currentValue = math.min(currentValue or 0, maxScroll)

    if scrollBar.mkSetScrollRange then
        scrollBar.mkSetScrollRange(0, maxScroll)
    elseif scrollBar.SetMinMaxValues then
        scrollBar:SetMinMaxValues(0, maxScroll)
    end
    if scrollBar.SetVisibleExtentPercentage then
        local visibleExtentPercentage = 0
        if viewHeight > 0 then
            visibleExtentPercentage = math.min(math.max(viewHeight / (maxScroll + viewHeight), 0), 1)
        end
        scrollBar:SetVisibleExtentPercentage(visibleExtentPercentage)
    end
    if scrollBar.SetPanExtentPercentage then
        local panExtentPercentage = maxScroll > 0 and math.min(math.max(36 / maxScroll, 0), 1) or 0
        scrollBar:SetPanExtentPercentage(panExtentPercentage)
    end
    if scrollBar.SetScrollAllowed then
        scrollBar:SetScrollAllowed(maxScroll > 0)
    end
    if scrollBar.mkSetScrollValue then
        scrollBar.mkSetScrollValue(currentValue)
    elseif scrollBar.SetValue then
        scrollBar:SetValue(currentValue)
    end
    scrollFrame:SetVerticalScroll(currentValue)

    if maxScroll > 0 then
        scrollBar:Show()
    else
        scrollBar:Hide()
        if scrollBar.mkSetScrollValue then
            scrollBar.mkSetScrollValue(0)
        elseif scrollBar.SetValue then
            scrollBar:SetValue(0)
        end
        scrollFrame:SetVerticalScroll(0)
    end
end

local function CreateDebugSectionTitle(parent, name, text)
    local createTitle = core.CreatePlayerInfoSectionTitle
    local title, titleBg, titleText
    if createTitle then
        title, titleBg, titleText = createTitle(parent, name, TRANSLATE(text))
    else
        title = CreateFrame("Frame", name, parent)
        title:SetHeight(40)
        titleText = title:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        titleText:SetPoint("LEFT", title, "LEFT", 14, 0)
        titleText:SetText(TRANSLATE(text))
    end
    return title, titleBg, titleText
end

local function CreateDebugSettingsGroup(parent, name, titleKey, rowCount, anchorFrame)
    local title, titleBg, titleText = CreateDebugSectionTitle(parent, name .. "Title", titleKey)
    if anchorFrame then
        title:SetPoint("TOPLEFT", anchorFrame, "BOTTOMLEFT", 0, -DEBUG_SECTION_GAP)
        title:SetPoint("TOPRIGHT", anchorFrame, "BOTTOMRIGHT", 0, -DEBUG_SECTION_GAP)
    else
        title:SetPoint("TOPLEFT", parent, "TOPLEFT", 0, 0)
        title:SetPoint("TOPRIGHT", parent, "TOPRIGHT", 0, 0)
    end

    local panelHeight = GetDebugSettingsPanelHeight(rowCount)
    local panel
    if core.CreateOptionsSettingsPanel then
        panel = core.CreateOptionsSettingsPanel(parent, rowCount)
    else
        panel = CreateFrame("Frame", nil, parent, "BackdropTemplate")
        panel.rows = {}
        for rowIndex = 1, rowCount do
            local row = CreateFrame("Frame", nil, panel)
            row:SetPoint("TOPLEFT", panel, "TOPLEFT", 0, -((rowIndex - 1) * GetDebugRowHeight()))
            row:SetPoint("TOPRIGHT", panel, "TOPRIGHT", 0, -((rowIndex - 1) * GetDebugRowHeight()))
            row:SetHeight(GetDebugRowHeight())
            panel.rows[rowIndex] = row
        end
    end
    panel:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -DEBUG_TITLE_TO_CONTROLS_GAP)
    panel:SetPoint("TOPRIGHT", title, "BOTTOMRIGHT", 0, -DEBUG_TITLE_TO_CONTROLS_GAP)
    panel:SetHeight(panelHeight)

    local titleHeight = math.max(title:GetHeight() or 0, 40)
    return {
        title = title,
        titleBg = titleBg,
        titleText = titleText,
        panel = panel,
        rows = panel.rows,
        height = titleHeight + DEBUG_TITLE_TO_CONTROLS_GAP + panelHeight,
    }
end

local function CreateDebugRowLabel(row, text)
    if core.CreateOptionsRowLabel then
        return core.CreateOptionsRowLabel(row, TRANSLATE(text))
    end

    local label = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    label:SetPoint("LEFT", row, "LEFT", 28, 0)
    label:SetSize(220, 28)
    label:SetJustifyH("LEFT")
    label:SetJustifyV("MIDDLE")
    label:SetText(TRANSLATE(text))
    ApplyDebugFont(label, 14)
    return label
end

local function AnchorDebugRowControl(control, row)
    if core.AnchorOptionsRowControl then
        core.AnchorOptionsRowControl(control, row)
        return
    end
    control:ClearAllPoints()
    control:SetPoint("LEFT", row, "LEFT", 304, 0)
end

local function CreateDebugActionButton(parent, owner, label, actionID, getPayload)
    local button = CreateFrame("Button", nil, parent, "UIPanelButtonTemplate")
    button:SetSize(DEBUG_BUTTON_WIDTH, DEBUG_BUTTON_HEIGHT)
    button:SetText(TRANSLATE(label))
    ApplyDebugFont(button.Text or (button.GetFontString and button:GetFontString()), 13)
    button:SetScript("OnClick", function()
        if Debug.RunAction then
            Debug.RunAction(owner, actionID, getPayload and getPayload())
        end
    end)
    return button
end

local function CreateDebugButtonRow(row, owner, buttonConfigs)
    local holder = CreateFrame("Frame", nil, row)
    holder:SetSize(DEBUG_CONTROL_WIDTH, DEBUG_BUTTON_HEIGHT)
    AnchorDebugRowControl(holder, row)

    for index, config in ipairs(buttonConfigs or {}) do
        local button = CreateDebugActionButton(holder, owner, config.label, config.actionID, config.getPayload)
        button:SetPoint("LEFT", holder, "LEFT", (index - 1) * (DEBUG_BUTTON_WIDTH + DEBUG_BUTTON_GAP_X), 0)
    end

    return holder
end

local function CreateDebugInput(parent, placeholderText)
    local holder = CreateFrame("Frame", nil, parent, "BackdropTemplate")
    holder:SetSize(DEBUG_INPUT_WIDTH, DEBUG_INPUT_HEIGHT)
    if holder.SetBackdrop then
        holder:SetBackdrop({
            bgFile = "Interface\\Buttons\\WHITE8X8",
            edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
            tile = false,
            edgeSize = 10,
            insets = { left = 3, right = 3, top = 3, bottom = 3 },
        })
        holder:SetBackdropColor(0, 0, 0, 0.35)
        holder:SetBackdropBorderColor(0.48, 0.38, 0.18, 0.78)
    end

    local editBox = CreateFrame("EditBox", nil, holder)
    editBox:SetPoint("TOPLEFT", holder, "TOPLEFT", 9, -1)
    editBox:SetPoint("BOTTOMRIGHT", holder, "BOTTOMRIGHT", -8, 1)
    editBox:SetAutoFocus(false)
    editBox:SetMaxLetters(64)
    editBox:SetTextColor(1, 0.96, 0.86, 1)
    ApplyDebugFont(editBox, 13)
    editBox:SetScript("OnEscapePressed", function(self)
        self:ClearFocus()
    end)
    editBox:SetScript("OnEnterPressed", function(self)
        self:ClearFocus()
    end)

    local placeholder = editBox:CreateFontString(nil, "OVERLAY", "GameFontDisable")
    placeholder:SetPoint("LEFT", editBox, "LEFT", 0, 0)
    placeholder:SetPoint("RIGHT", editBox, "RIGHT", 0, 0)
    placeholder:SetJustifyH("LEFT")
    placeholder:SetText(TRANSLATE(placeholderText or ""))
    placeholder:SetTextColor(0.55, 0.55, 0.55, 0.88)
    ApplyDebugFont(placeholder, 13)

    editBox:HookScript("OnTextChanged", function(self)
        if self:GetText() == "" then
            placeholder:Show()
        else
            placeholder:Hide()
        end
    end)

    holder.editBox = editBox
    holder.GetText = function()
        return editBox:GetText() or ""
    end
    return holder
end

local function CreateDebugInputActionRow(row, owner, placeholderText, actionLabel, actionID)
    local holder = CreateFrame("Frame", nil, row)
    holder:SetSize(DEBUG_CONTROL_WIDTH, DEBUG_INPUT_HEIGHT)
    AnchorDebugRowControl(holder, row)

    local input = CreateDebugInput(holder, placeholderText)
    input:SetPoint("LEFT", holder, "LEFT", 0, 0)

    local button = CreateDebugActionButton(holder, owner, actionLabel, actionID, function()
        return input:GetText()
    end)
    button:SetPoint("LEFT", input, "RIGHT", DEBUG_BUTTON_GAP_X, 0)
    return holder, input, button
end

local function SkinDebugCheckButton(button)
    if not button then
        return
    end

    local textures = {
        button:GetNormalTexture(),
        button:GetPushedTexture(),
        button:GetHighlightTexture(),
        button:GetCheckedTexture(),
        button:GetDisabledCheckedTexture(),
    }
    for _, texture in ipairs(textures) do
        if texture then
            texture:ClearAllPoints()
            texture:SetPoint("CENTER", button, "CENTER", 0, 0)
            texture:SetSize(DEBUG_CHECK_BUTTON_SIZE, DEBUG_CHECK_BUTTON_SIZE)
        end
    end
end

local function CreateDebugCheckButton(parent, owner, getValue, setValue)
    local checkButton = CreateFrame("CheckButton", nil, parent, "UICheckButtonTemplate")
    checkButton:SetSize(DEBUG_CHECK_BUTTON_SIZE, DEBUG_CHECK_BUTTON_SIZE)
    SkinDebugCheckButton(checkButton)
    checkButton:SetScript("OnClick", function(self)
        if setValue then
            setValue(self:GetChecked() == true)
        end
        if owner.RefreshMainWindow and owner:IsMainWindowShown() and owner.activeTab == "debug" then
            owner:RefreshMainWindow("DebugToggle")
        end
    end)
    checkButton.Refresh = function(self)
        self:SetChecked(getValue and getValue() == true)
    end
    checkButton:Refresh()
    return checkButton
end

local function CreateDebugCheckRow(row, owner, getValue, setValue)
    local holder = CreateFrame("Frame", nil, row)
    holder:SetSize(DEBUG_CONTROL_WIDTH, DEBUG_CHECK_BUTTON_SIZE)
    AnchorDebugRowControl(holder, row)

    local checkButton = CreateDebugCheckButton(holder, owner, getValue, setValue)
    checkButton:SetPoint("LEFT", holder, "LEFT", -3, 0)
    holder.checkButton = checkButton
    holder.Refresh = function()
        checkButton:Refresh()
    end
    return holder
end

local function CreateDebugStatusText(row)
    local text = row:CreateFontString(nil, "OVERLAY", "GameFontDisable")
    text:SetSize(DEBUG_CONTROL_WIDTH, 28)
    text:SetJustifyH("LEFT")
    text:SetJustifyV("MIDDLE")
    text:SetWordWrap(false)
    text:SetTextColor(0.72, 0.66, 0.5, 1)
    ApplyDebugFont(text, 13)
    AnchorDebugRowControl(text, row)
    return text
end

function DebugTab.Build(owner, tabGroup)
    local root = CreateFrame("Frame", "MKDebugPanel", UIParent)
    root.mkFillDecorFrame = true
    root.mkDecorFrameInsetLeft = 24
    root.mkDecorFrameInsetTop = 4
    root.mkDecorFrameInsetRight = 24
    root.mkDecorFrameInsetBottom = 10
    tabGroup:AddChild(root)

    local scrollFrame, content, scrollBar
    local createScrollContainer = core.CreateNativeScrollContainer
    if createScrollContainer then
        scrollFrame, content, scrollBar = createScrollContainer(root, function()
            UpdateDebugScrollState(owner.widgets)
        end, 0)
    end

    if not (scrollFrame and content) then
        content = CreateFrame("Frame", nil, root)
        content:SetAllPoints(root)
    end
    content:SetHeight(DEBUG_CONTENT_HEIGHT)

    if scrollBar and tabGroup and tabGroup.frame then
        scrollBar:ClearAllPoints()
        scrollBar:SetPoint("TOPRIGHT", tabGroup.frame, "TOPRIGHT", DEBUG_SCROLLBAR_RIGHT_OFFSET, -4)
        scrollBar:SetPoint("BOTTOMRIGHT", tabGroup.frame, "BOTTOMRIGHT", DEBUG_SCROLLBAR_RIGHT_OFFSET, 4)
        scrollBar:SetWidth(17)
    end

    local debugControls = CreateFrame("Frame", nil, content)
    debugControls:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -DEBUG_CONTENT_TOP_OFFSET)
    debugControls:SetPoint("TOPRIGHT", content, "TOPRIGHT", 0, -DEBUG_CONTENT_TOP_OFFSET)

    local totalControlsHeight = 0

    local statusGroup = CreateDebugSettingsGroup(debugControls, "MKDebugStatus", "调试状态", 1)
    totalControlsHeight = totalControlsHeight + statusGroup.height
    CreateDebugRowLabel(statusGroup.rows[1], "当前状态")
    local status = CreateDebugStatusText(statusGroup.rows[1])
    status:SetText(Debug.GetStatusText and Debug.GetStatusText(owner) or "")

    local overviewGroup = CreateDebugSettingsGroup(debugControls, "MKDebugOverview", "测试总览", 1, statusGroup.panel)
    totalControlsHeight = totalControlsHeight + DEBUG_SECTION_GAP + overviewGroup.height
    CreateDebugRowLabel(overviewGroup.rows[1], "命令总览")
    CreateDebugButtonRow(overviewGroup.rows[1], owner, {
        { label = "显示命令总览", actionID = "help" },
    })

    local dataGroup = CreateDebugSettingsGroup(debugControls, "MKDebugTestData", "测试数据", 2, overviewGroup.panel)
    totalControlsHeight = totalControlsHeight + DEBUG_SECTION_GAP + dataGroup.height
    CreateDebugRowLabel(dataGroup.rows[1], "启用测试数据")
    CreateDebugButtonRow(dataGroup.rows[1], owner, {
        { label = "启用", actionID = "testData" },
        { label = "启用 +1 组", actionID = "testData1" },
        { label = "启用 +2 组", actionID = "testData2" },
        { label = "启用 +3 组", actionID = "testData3" },
    })
    CreateDebugRowLabel(dataGroup.rows[2], "清理测试数据")
    CreateDebugButtonRow(dataGroup.rows[2], owner, {
        { label = "清理", actionID = "testDataOff" },
    })

    local dataDiagnosticsGroup = CreateDebugSettingsGroup(debugControls, "MKDebugDataDiagnostics", "数据诊断", 1, dataGroup.panel)
    totalControlsHeight = totalControlsHeight + DEBUG_SECTION_GAP + dataDiagnosticsGroup.height
    CreateDebugRowLabel(dataDiagnosticsGroup.rows[1], "缓存与队伍")
    CreateDebugButtonRow(dataDiagnosticsGroup.rows[1], owner, {
        { label = "角色缓存", actionID = "chars" },
        { label = "队伍钥石", actionID = "group" },
    })

    local teleportGroup = CreateDebugSettingsGroup(debugControls, "MKDebugTeleport", "传送诊断", 2, dataDiagnosticsGroup.panel)
    totalControlsHeight = totalControlsHeight + DEBUG_SECTION_GAP + teleportGroup.height
    CreateDebugRowLabel(teleportGroup.rows[1], "英雄之路映射")
    CreateDebugButtonRow(teleportGroup.rows[1], owner, {
        { label = "导出映射", actionID = "teleport" },
    })
    CreateDebugRowLabel(teleportGroup.rows[2], "传送弹窗预览")
    local _, mapInput = CreateDebugInputActionRow(teleportGroup.rows[2], owner, "mapID", "预览弹窗", "dialog")

    local accountGroup = CreateDebugSettingsGroup(debugControls, "MKDebugAccount", "账号诊断", 3, teleportGroup.panel)
    totalControlsHeight = totalControlsHeight + DEBUG_SECTION_GAP + accountGroup.height
    CreateDebugRowLabel(accountGroup.rows[1], "当前角色")
    CreateDebugButtonRow(accountGroup.rows[1], owner, {
        { label = "当前角色", actionID = "account" },
    })
    CreateDebugRowLabel(accountGroup.rows[2], "指定玩家")
    local _, accountInput = CreateDebugInputActionRow(accountGroup.rows[2], owner, "角色-服务器", "查询指定", "accountTarget")
    CreateDebugRowLabel(accountGroup.rows[3], "账号缓存")
    CreateDebugButtonRow(accountGroup.rows[3], owner, {
        { label = "刷新缓存", actionID = "accountRefresh" },
    })

    local memoryGroup = CreateDebugSettingsGroup(debugControls, "MKDebugMemory", "内存诊断", 1, accountGroup.panel)
    totalControlsHeight = totalControlsHeight + DEBUG_SECTION_GAP + memoryGroup.height
    CreateDebugRowLabel(memoryGroup.rows[1], "插件内存")
    CreateDebugButtonRow(memoryGroup.rows[1], owner, {
        { label = "输出诊断", actionID = "memory" },
    })

    local logGroup = CreateDebugSettingsGroup(debugControls, "MKDebugLog", "调试日志", 2, memoryGroup.panel)
    totalControlsHeight = totalControlsHeight + DEBUG_SECTION_GAP + logGroup.height
    CreateDebugRowLabel(logGroup.rows[1], "精简调试日志")
    local debugLogToggle = CreateDebugCheckRow(logGroup.rows[1], owner, function()
        return owner.IsDebugLoggingEnabled and owner:IsDebugLoggingEnabled()
    end, function(value)
        Debug.SetDebugLogging(owner, value)
    end)
    CreateDebugRowLabel(logGroup.rows[2], "完整调试日志")
    local verboseLogToggle = CreateDebugCheckRow(logGroup.rows[2], owner, function()
        return owner.IsDebugVerboseLoggingEnabled and owner:IsDebugVerboseLoggingEnabled()
    end, function(value)
        Debug.SetVerboseLogging(owner, value)
    end)

    local localeGroup = CreateDebugSettingsGroup(debugControls, "MKDebugLocale", "本地化调试", 1, logGroup.panel)
    totalControlsHeight = totalControlsHeight + DEBUG_SECTION_GAP + localeGroup.height
    CreateDebugRowLabel(localeGroup.rows[1], "语言覆盖")
    CreateDebugButtonRow(localeGroup.rows[1], owner, {
        { label = "简体中文", actionID = "localeZh" },
        { label = "繁体中文", actionID = "localeZhTW" },
        { label = "英文", actionID = "localeEn" },
        { label = "重置语言", actionID = "localeReset" },
    })

    debugControls:SetHeight(totalControlsHeight)
    content.mkDebugContentHeight = math.max(
        DEBUG_CONTENT_HEIGHT,
        DEBUG_CONTENT_TOP_OFFSET + totalControlsHeight + DEBUG_CONTENT_BOTTOM_PADDING
    )
    content:SetHeight(content.mkDebugContentHeight)

    owner.widgets = {
        tabGroup = owner.widgets and owner.widgets.tabGroup or tabGroup,
        root = root,
        debugRoot = root,
        debugScrollFrame = scrollFrame,
        debugScrollBar = scrollBar,
        debugContent = content,
        debugControls = debugControls,
        debugStatus = status,
        debugLogToggle = debugLogToggle,
        debugVerboseToggle = verboseLogToggle,
        debugMapInput = mapInput,
        debugAccountInput = accountInput,
    }
    owner.tabViews = owner.tabViews or {}
    owner.tabViews.debug = owner.widgets

    if core.ApplyThemeToTabGroup then
        core.ApplyThemeToTabGroup(tabGroup)
    end
    if core.ApplyFontToFrame then
        core.ApplyFontToFrame(root)
    end

    if C_Timer and C_Timer.After then
        C_Timer.After(0, function()
            if core.FinalizeFlatTabLayout then
                core.FinalizeFlatTabLayout(tabGroup)
            end
            if owner.activeTab == "debug" and owner.widgets and owner.widgets.debugContent == content then
                UpdateDebugScrollState(owner.widgets)
            end
        end)
    else
        UpdateDebugScrollState(owner.widgets)
    end

    owner:RefreshMainWindow("BuildSelectedTab", "debug")
end

function DebugTab.Refresh(owner, widgets)
    if not widgets or not widgets.debugRoot then
        return
    end

    if widgets.debugStatus then
        widgets.debugStatus:SetText(Debug.GetStatusText and Debug.GetStatusText(owner) or "")
    end
    if widgets.debugLogToggle and widgets.debugLogToggle.Refresh then
        widgets.debugLogToggle:Refresh()
    end
    if widgets.debugVerboseToggle and widgets.debugVerboseToggle.Refresh then
        widgets.debugVerboseToggle:Refresh()
    end
    UpdateDebugScrollState(widgets)
end

function DebugTab.BuildSnapshot(owner)
    if Debug.BuildSnapshot then
        return Debug.BuildSnapshot(owner)
    end
    return "debug"
end
