local addonName, ns = ...
local L = ns.L

local cache = {}
local W = ns.Widgets
local C = ns.COLORS

-- Styled dropdown colors (match Widgets.lua)
local DARK_BG_R, DARK_BG_G, DARK_BG_B = 0.08, 0.08, 0.08
local BTN_BLUE_R, BTN_BLUE_G, BTN_BLUE_B = 0.004, 0.557, 0.906
local BTN_ORANGE_R, BTN_ORANGE_G, BTN_ORANGE_B = 1.0, 0.663, 0.0

-- Dynamic dropdown for profile selection (with frame pooling)
local function CreateDynamicDropdown(parent, opts)
    opts = opts or {}
    local width = opts.width or 160
    local height = 22
    local itemHeight = 20

    local f = CreateFrame("Frame", nil, parent)
    f:SetSize(width, height)

    -- Dropdown button
    local btn = CreateFrame("Button", nil, f, "BackdropTemplate")
    btn:SetSize(width, height)
    btn:SetPoint("TOPLEFT", 0, 0)
    btn:SetBackdrop({
        bgFile = [[Interface\Buttons\WHITE8x8]],
        edgeFile = [[Interface\Buttons\WHITE8x8]],
        edgeSize = 1,
    })
    btn:SetBackdropColor(DARK_BG_R, DARK_BG_G, DARK_BG_B, 0.95)
    btn:SetBackdropBorderColor(BTN_BLUE_R, BTN_BLUE_G, BTN_BLUE_B, 0.7)
    f.button = btn

    -- Selected text
    local text = btn:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    text:SetPoint("LEFT", 8, 0)
    text:SetPoint("RIGHT", -20, 0)
    text:SetJustifyH("LEFT")
    text:SetTextColor(1, 1, 1, 1)
    text:SetText(opts.placeholder or "Select...")
    btn.text = text
    f.text = text

    -- Arrow texture
    local arrow = btn:CreateTexture(nil, "OVERLAY")
    arrow:SetSize(12, 12)
    arrow:SetPoint("RIGHT", -4, 0)
    arrow:SetTexture([[Interface\Buttons\UI-ScrollBar-ScrollDownButton-Up]])
    arrow:SetTexCoord(0.25, 0.75, 0.25, 0.75)
    arrow:SetVertexColor(BTN_ORANGE_R, BTN_ORANGE_G, BTN_ORANGE_B, 1)

    -- Dropdown menu frame
    local menu = CreateFrame("Frame", nil, btn, "BackdropTemplate")
    menu:SetPoint("TOPLEFT", btn, "BOTTOMLEFT", 0, -2)
    menu:SetWidth(width)
    menu:SetBackdrop({
        bgFile = [[Interface\Buttons\WHITE8x8]],
        edgeFile = [[Interface\Buttons\WHITE8x8]],
        edgeSize = 1,
    })
    menu:SetBackdropColor(DARK_BG_R, DARK_BG_G, DARK_BG_B, 0.98)
    menu:SetBackdropBorderColor(BTN_BLUE_R, BTN_BLUE_G, BTN_BLUE_B, 0.7)
    menu:SetFrameStrata("DIALOG")
    menu:SetFrameLevel(100)
    menu:Hide()
    f.menu = menu

    f.menuOpen = false
    f.itemPool = {}
    f.activeItems = {}
    f.selectedValue = nil

    -- Pre-create empty state frame (reused)
    local emptyItem = CreateFrame("Frame", nil, menu)
    emptyItem:SetSize(width - 2, itemHeight)
    emptyItem:SetPoint("TOPLEFT", 1, -1)
    local emptyText = emptyItem:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
    emptyText:SetPoint("LEFT", 8, 0)
    emptyText:SetText(opts.emptyText or L["COMMON_NONE"])
    emptyItem:Hide()
    f.emptyItem = emptyItem

    function f:SetText(str)
        text:SetText(str)
    end

    function f:GetSelectedValue()
        return f.selectedValue
    end

    function f:SetSelectedValue(val)
        f.selectedValue = val
    end

    -- Get or create pooled menu item
    local function GetPooledItem()
        for _, item in ipairs(f.itemPool) do
            if not item.inUse then
                item.inUse = true
                item:Show()
                return item
            end
        end

        -- Create new item
        local item = CreateFrame("Button", nil, menu, "BackdropTemplate")
        item:SetSize(width - 2, itemHeight)
        item:SetBackdrop({ bgFile = [[Interface\Buttons\WHITE8x8]] })
        item:SetBackdropColor(0, 0, 0, 0)

        item.label = item:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
        item.label:SetPoint("LEFT", 8, 0)
        item.label:SetPoint("RIGHT", -8, 0)
        item.label:SetJustifyH("LEFT")
        item.label:SetTextColor(1, 1, 1, 1)

        item:SetScript("OnEnter", function(self)
            self:SetBackdropColor(BTN_ORANGE_R, BTN_ORANGE_G, BTN_ORANGE_B, 0.3)
        end)
        item:SetScript("OnLeave", function(self)
            self:SetBackdropColor(0, 0, 0, 0)
        end)

        f.itemPool[#f.itemPool + 1] = item
        item.inUse = true
        return item
    end

    function f:Refresh(options)
        -- Return all items to pool
        for _, item in ipairs(f.activeItems) do
            item.inUse = false
            item:Hide()
        end
        f.activeItems = {}

        if #options == 0 then
            f.emptyItem:Show()
            menu:SetHeight(itemHeight + 2)
        else
            f.emptyItem:Hide()

            for i, opt in ipairs(options) do
                local item = GetPooledItem()
                item:ClearAllPoints()
                item:SetPoint("TOPLEFT", 1, -1 - (i - 1) * itemHeight)
                item.label:SetText(opt.text)
                item.optValue = opt.value
                item.optText = opt.text

                item:SetScript("OnClick", function(self)
                    f.selectedValue = self.optValue
                    text:SetText(self.optText)
                    menu:Hide()
                    if opts.onSelect then opts.onSelect(self.optValue, self.optText) end
                end)

                f.activeItems[i] = item
            end

            menu:SetHeight(#options * itemHeight + 2)
        end
    end

    -- Toggle menu
    btn:SetScript("OnClick", function()
        if f.menuOpen then
            menu:Hide()
        else
            menu:Show()
            f.menuOpen = true
            btn:SetBackdropBorderColor(BTN_ORANGE_R, BTN_ORANGE_G, BTN_ORANGE_B, 0.9)
        end
    end)

    btn:SetScript("OnEnter", function(self)
        if not f.menuOpen then
            self:SetBackdropBorderColor(BTN_ORANGE_R, BTN_ORANGE_G, BTN_ORANGE_B, 0.9)
        end
    end)
    btn:SetScript("OnLeave", function(self)
        if not f.menuOpen then
            self:SetBackdropBorderColor(BTN_BLUE_R, BTN_BLUE_G, BTN_BLUE_B, 0.7)
        end
    end)

    -- OnUpdate only runs when menu is open
    menu:SetScript("OnShow", function()
        f:SetScript("OnUpdate", function()
            if not menu:IsMouseOver() and not btn:IsMouseOver() then
                if IsMouseButtonDown("LeftButton") or IsMouseButtonDown("RightButton") then
                    menu:Hide()
                end
            end
        end)
    end)

    menu:SetScript("OnHide", function()
        f:SetScript("OnUpdate", nil)
        f.menuOpen = false
        btn:SetBackdropBorderColor(BTN_BLUE_R, BTN_BLUE_G, BTN_BLUE_B, 0.7)
    end)

    return f
end

-- Static popup for profile name input
StaticPopupDialogs["NAOWH_QOL_PROFILE_NAME"] = {
    text = "%s",
    button1 = L["COMMON_OK"],
    button2 = L["COMMON_CANCEL"],
    hasEditBox = true,
    OnShow = function(self)
        local editBox = self.editBox or self.EditBox
        if editBox then
            editBox:SetText(self.data and self.data.default or "")
            editBox:HighlightText()
        end
    end,
    OnAccept = function(self)
        local editBox = self.editBox or self.EditBox
        local name = editBox and strtrim(editBox:GetText()) or ""
        if name ~= "" and self.data and self.data.callback then
            self.data.callback(name)
        end
    end,
    EditBoxOnEnterPressed = function(self)
        local parent = self:GetParent()
        local name = strtrim(self:GetText())
        if name ~= "" and parent.data and parent.data.callback then
            parent.data.callback(name)
        end
        parent:Hide()
    end,
    timeout = 0,
    whileDead = true,
    hideOnEscape = true,
    preferredIndex = 3,
}

StaticPopupDialogs["NAOWH_QOL_PROFILE_CONFIRM"] = {
    text = "%s",
    button1 = L["COMMON_YES"],
    button2 = L["COMMON_NO"],
    OnAccept = function(self)
        if self.data and self.data.callback then
            self.data.callback()
        end
    end,
    timeout = 0,
    whileDead = true,
    hideOnEscape = true,
    preferredIndex = 3,
}

function ns:InitImportExport()
    local p = ns.MainFrame.Content

    W:CachedPanel(cache, "frame", p, function(f)
        local _, sc = W:CreateScrollFrame(f, 1200)

        W:CreatePageHeader(sc,
            W.Colorize(L["IMPORTEXPORT_TITLE"], C.ORANGE),
            W.Colorize(L["IMPORTEXPORT_SUBTITLE"], C.BLUE),
            { subtitleFont = "GameFontNormalSmall", separator = false })

        -- Profile Management Section
        W:CreateSectionHeader(sc, L["IMPORTEXPORT_SECTION_MANAGE"], -80)

        local profileStatus = sc:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
        profileStatus:SetPoint("TOPLEFT", 35, -115)
        profileStatus:SetText("")

        local function UpdateProfileStatus()
            local active = ns.SettingsIO:GetActiveProfile()
            local count = #ns.SettingsIO:GetProfileList()
            profileStatus:SetText("|cff44ff44" .. string.format(L["IMPORTEXPORT_ACTIVE_STATUS"], active, count) .. "|r")
        end

        -- Profile dropdown
        local profileDropdown = CreateDynamicDropdown(sc, {
            width = 160,
            placeholder = L["IMPORTEXPORT_PLACEHOLDER"],
            emptyText = L["COMMON_NONE"],
            onSelect = function(name)
                local ok, err = ns.SettingsIO:LoadProfile(name)
                if ok then
                    UpdateProfileStatus()
                    profileStatus:SetText("|cff44ff44" .. string.format(L["IMPORTEXPORT_STATUS_LOADED"], name) .. "|r")
                    StaticPopup_Show("NAOWH_QOL_RELOAD")
                else
                    profileStatus:SetText("|cffff4444" .. (err or L["IMPORTEXPORT_ERR_LOAD"]) .. "|r")
                end
            end
        })
        profileDropdown:SetPoint("TOPLEFT", 35, -135)

        local function RefreshProfileDropdown()
            local profiles = ns.SettingsIO:GetProfileList()
            local options = {}
            for _, name in ipairs(profiles) do
                options[#options + 1] = { text = name, value = name }
            end
            profileDropdown:Refresh(options)
            profileDropdown:SetText(ns.SettingsIO:GetActiveProfile())
            profileDropdown:SetSelectedValue(ns.SettingsIO:GetActiveProfile())
        end

        -- Forward declarations for spec profile dropdowns
        local specDropdowns = {}
        local RefreshSpecDropdowns

        -- Save button — saves current settings directly into the active profile.
        -- No popup: use "New" to create a differently-named profile.
        local saveBtn = W:CreateButton(sc, { text = L["COMMON_SAVE"], width = 60, height = 24 })
        saveBtn:SetPoint("LEFT", profileDropdown, "RIGHT", 8, 0)
        saveBtn:SetScript("OnClick", function()
            local active = ns.SettingsIO:GetActiveProfile()
            ns.SettingsIO:SaveProfile(active)
            RefreshProfileDropdown()
            if RefreshSpecDropdowns then RefreshSpecDropdowns() end
            UpdateProfileStatus()
            profileStatus:SetText("|cff44ff44" .. string.format(L["IMPORTEXPORT_STATUS_SAVED"], active) .. "|r")
        end)

        -- Rename button
        local renameBtn = W:CreateButton(sc, { text = L["COMMON_RENAME"], width = 70, height = 24 })
        renameBtn:SetPoint("LEFT", saveBtn, "RIGHT", 4, 0)
        renameBtn:SetScript("OnClick", function()
            local current = ns.SettingsIO:GetActiveProfile()
                local dialog = StaticPopup_Show("NAOWH_QOL_PROFILE_NAME", string.format(L["IMPORTEXPORT_POPUP_RENAME"], current))
            if dialog then
                dialog.data = {
                    default = current,
                    callback = function(newName)
                        local ok, err = ns.SettingsIO:RenameProfile(current, newName)
                        if ok then
                            RefreshProfileDropdown()
                            if RefreshSpecDropdowns then RefreshSpecDropdowns() end
                            UpdateProfileStatus()
                            profileStatus:SetText("|cff44ff44" .. string.format(L["IMPORTEXPORT_STATUS_RENAMED"], newName) .. "|r")
                        else
                            if err == "exists" then
                                profileStatus:SetText("|cffff4444" .. L["IMPORTEXPORT_ERR_EXISTS"] .. "|r")
                            else
                                profileStatus:SetText("|cffff4444" .. L["IMPORTEXPORT_ERR_RENAME"] .. "|r")
                            end
                        end
                    end
                }
            end
        end)

        -- Delete button
        local deleteBtn = W:CreateButton(sc, { text = L["COMMON_DELETE"], width = 60, height = 24 })
        deleteBtn:SetPoint("LEFT", renameBtn, "RIGHT", 4, 0)
        deleteBtn:SetScript("OnClick", function()
            local current = ns.SettingsIO:GetActiveProfile()
            local profiles = ns.SettingsIO:GetProfileList()
            if #profiles <= 1 then
                profileStatus:SetText("|cffff4444" .. L["IMPORTEXPORT_ERR_LAST"] .. "|r")
                return
            end
                local dialog = StaticPopup_Show("NAOWH_QOL_PROFILE_CONFIRM", string.format(L["IMPORTEXPORT_POPUP_DELETE"], current))
            if dialog then
                dialog.data = {
                    callback = function()
                        ns.SettingsIO:DeleteProfile(current)
                        RefreshProfileDropdown()
                        if RefreshSpecDropdowns then RefreshSpecDropdowns() end
                        UpdateProfileStatus()
                        profileStatus:SetText("|cff44ff44" .. L["IMPORTEXPORT_STATUS_DELETED"] .. "|r")
                    end
                }
            end
        end)

        -- New profile button (creates profile with default settings)
        local newBtn = W:CreateButton(sc, { text = L["COMMON_NEW"], width = 60, height = 24 })
        newBtn:SetPoint("LEFT", deleteBtn, "RIGHT", 4, 0)
        newBtn:SetScript("OnClick", function()
                local dialog = StaticPopup_Show("NAOWH_QOL_PROFILE_NAME", L["IMPORTEXPORT_POPUP_NEW"])
            if dialog then
                dialog.data = {
                    default = "",
                    callback = function(name)
                        -- Check if profile already exists
                        local profiles = ns.SettingsIO:GetProfileList()
                        for _, pname in ipairs(profiles) do
                            if pname == name then
                                profileStatus:SetText("|cffff4444" .. L["IMPORTEXPORT_ERR_EXISTS"] .. "|r")
                                return
                            end
                        end
                        ns.SettingsIO:CreateDefaultProfile(name)
                        RefreshProfileDropdown()
                        if RefreshSpecDropdowns then RefreshSpecDropdowns() end
                        UpdateProfileStatus()
                        profileStatus:SetText("|cff44ff44" .. string.format(L["IMPORTEXPORT_STATUS_CREATED"], name) .. "|r")
                    end
                }
            end
        end)

        -- Reset to Defaults button — wipes the active profile back to factory
        -- settings (everything off, default colors).  Only works on the Default
        -- profile to protect user-created named profiles from accidental resets.
        local resetBtn = W:CreateButton(sc, { text = L["IMPORTEXPORT_RESET_BTN"], width = 120, height = 24 })
        resetBtn:SetPoint("LEFT", newBtn, "RIGHT", 4, 0)
        resetBtn:SetScript("OnClick", function()
            local active = ns.SettingsIO:GetActiveProfile()
            if active ~= "Default" then
                profileStatus:SetText("|cffff4444" .. L["IMPORTEXPORT_RESET_ERR_NAMED"] .. "|r")
                return
            end
            local dialog = StaticPopup_Show("NAOWH_QOL_PROFILE_CONFIRM",
                L["IMPORTEXPORT_POPUP_RESET"])
            if dialog then
                dialog.data = {
                    callback = function()
                        -- Reset the AceDB profile to aceDBDefaults (all off)
                        ns.db:ResetProfile()
                        NaowhQOL = ns.db.profile
                        -- Also clear snapshot data so reload doesn't restore old values
                        if NaowhQOL_Profiles and NaowhQOL_Profiles.profileData then
                            NaowhQOL_Profiles.profileData["Default"] = nil
                        end
                        -- Refresh everything
                        if ns.SettingsIO then
                            ns.SettingsIO:TriggerRefreshAll()
                        end
                        RefreshProfileDropdown()
                        if RefreshSpecDropdowns then RefreshSpecDropdowns() end
                        UpdateProfileStatus()
                        profileStatus:SetText("|cff44ff44" .. L["IMPORTEXPORT_RESET_OK"] .. "|r")
                        StaticPopup_Show("NAOWH_QOL_RELOAD")
                    end
                }
            end
        end)

        -- Copy profile section (AceDB profiles are account-wide)
        W:CreateSectionHeader(sc, L["IMPORTEXPORT_SECTION_COPY"], -180)

        local copyFromDropdown = CreateDynamicDropdown(sc, {
            width = 200,
            placeholder = L["IMPORTEXPORT_SELECT_COPY"],
            emptyText = L["COMMON_NONE"],
        })
        copyFromDropdown:SetPoint("TOPLEFT", 35, -218)

        local function RefreshCopyFromDropdown()
            local profiles = ns.SettingsIO:GetProfileList()
            local currentProfile = ns.SettingsIO:GetActiveProfile()
            local options = {}
            for _, name in ipairs(profiles) do
                if name ~= currentProfile then
                    options[#options + 1] = { text = name, value = name }
                end
            end
            copyFromDropdown:Refresh(options)
            copyFromDropdown:SetText(L["IMPORTEXPORT_SELECT_COPY"])
            copyFromDropdown:SetSelectedValue(nil)
        end

        local copyNote = sc:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
        copyNote:SetPoint("TOPLEFT", 35, -248)
        copyNote:SetText(L["IMPORTEXPORT_COPY_NOTE"])

        local copyStatus = sc:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
        copyStatus:SetPoint("TOPLEFT", 35, -264)
        copyStatus:SetText("")

        local copyBtn = W:CreateButton(sc, { text = L["IMPORTEXPORT_COPY_BTN"], width = 110, height = 24 })
        copyBtn:SetPoint("LEFT", copyFromDropdown, "RIGHT", 8, 0)
        copyBtn:SetScript("OnClick", function()
            local sourceProfile = copyFromDropdown:GetSelectedValue()
            if not sourceProfile then
                copyStatus:SetText("|cffff4444" .. L["IMPORTEXPORT_COPY_ERR"] .. "|r")
                return
            end
            local ok, err = ns.SettingsIO:CopyProfile(sourceProfile)
            if ok then
                copyStatus:SetText("|cff44ff44" .. string.format(L["IMPORTEXPORT_COPY_OK"], sourceProfile) .. "|r")
                RefreshProfileDropdown()
                UpdateProfileStatus()
            else
                copyStatus:SetText("|cffff4444" .. (err or L["IMPORTEXPORT_COPY_ERR"]) .. "|r")
            end
        end)

        -- Spec Profile Swap Section
        W:CreateSectionHeader(sc, L["IMPORTEXPORT_SECTION_SPEC"], -296)

        local specRows = {}

        RefreshSpecDropdowns = function()
            local profiles = ns.SettingsIO:GetProfileList()
            local options = {{ text = L["COMMON_NONE"], value = "" }}
            for _, name in ipairs(profiles) do
                options[#options + 1] = { text = name, value = name }
            end
            for specIndex, dropdown in pairs(specDropdowns) do
                dropdown:Refresh(options)
                local specData = ns.SettingsIO:GetSpecProfileData(specIndex)
                if specData.profile and specData.profile ~= "" then
                    dropdown:SetText(specData.profile)
                    dropdown:SetSelectedValue(specData.profile)
                else
                    dropdown:SetText(L["COMMON_NONE"])
                    dropdown:SetSelectedValue("")
                end
            end
        end

        local function BuildSpecRows()
            local numSpecs = GetNumSpecializations()
            local yOffset = -315

            for i = 1, numSpecs do
                local specID, specName, _, specIcon = GetSpecializationInfo(i)
                if specName then
                    local specData = ns.SettingsIO:GetSpecProfileData(i)

                    -- Spec icon
                    local icon = sc:CreateTexture(nil, "ARTWORK")
                    icon:SetSize(20, 20)
                    icon:SetPoint("TOPLEFT", 35, yOffset - 5)
                    icon:SetTexture(specIcon)
                    icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)

                    -- Enable checkbox
                    local specIndex = i
                    local cb = W:CreateCheckbox(sc, {
                        template = "interface",
                        checked = specData.enabled,
                        onChange = function(checked)
                            ns.SettingsIO:SetSpecProfileEnabled(specIndex, checked)
                        end
                    })
                    cb:SetPoint("LEFT", icon, "RIGHT", 6, -2)
                    cb.Text:SetText(specName)

                    -- Profile dropdown
                    local dropdown = CreateDynamicDropdown(sc, {
                        width = 140,
                        placeholder = L["COMMON_NONE"],
                        emptyText = L["COMMON_NONE"],
                        onSelect = function(profileName)
                            ns.SettingsIO:SetSpecProfile(specIndex, profileName)
                        end
                    })
                    dropdown:SetPoint("LEFT", cb, "RIGHT", 100, -2)
                    specDropdowns[specIndex] = dropdown

                    specRows[i] = { icon = icon, checkbox = cb, dropdown = dropdown }
                    yOffset = yOffset - 45
                end
            end
        end

        -- Initialize dropdowns
        C_Timer.After(0.1, function()
            ns.SettingsIO:InitProfiles()
            RefreshProfileDropdown()
            RefreshCopyFromDropdown()
            UpdateProfileStatus()
            BuildSpecRows()
            RefreshSpecDropdowns()
        end)

        -- Keep profile dropdown in sync whenever the active profile changes
        ns.SettingsIO:RegisterRefresh("importexport_profiles", function()
            RefreshProfileDropdown()
            RefreshCopyFromDropdown()
            UpdateProfileStatus()
            if RefreshSpecDropdowns then RefreshSpecDropdowns() end
        end)

        -- Multi-line edit box helper
        local function MakeTextBox(parent, y, height, readOnly)
            local bg = CreateFrame("Frame", nil, parent, "BackdropTemplate")
            bg:SetSize(430, height)
            bg:SetPoint("TOPLEFT", 35, y)
            bg:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8x8",
                edgeFile = "Interface\\Buttons\\WHITE8x8", edgeSize = 1 })
            bg:SetBackdropColor(0.04, 0.04, 0.04, 1)
            bg:SetBackdropBorderColor(0, 0, 0, 1)

            local sf = CreateFrame("ScrollFrame", nil, bg, "UIPanelScrollFrameTemplate")
            sf:SetPoint("TOPLEFT", 6, -6); sf:SetPoint("BOTTOMRIGHT", -20, 6)

            local box = CreateFrame("EditBox", nil, sf)
            box:SetWidth(sf:GetWidth() or 400)
            box:SetFontObject("GameFontHighlightSmall")
            box:SetAutoFocus(false); box:SetMultiLine(true)
            box:SetMaxLetters(0)
            sf:SetScrollChild(box)

            -- Make entire background clickable to focus the edit box
            bg:EnableMouse(true)
            bg:SetScript("OnMouseDown", function() box:SetFocus() end)

            if readOnly then
                local storedText = ""
                box:SetScript("OnTextChanged", function(self, userInput)
                    if userInput then
                        self:SetText(storedText)
                        self:HighlightText()
                    else
                        storedText = self:GetText()
                    end
                end)
                box:SetScript("OnEditFocusGained", function(self) self:HighlightText() end)
                box:SetScript("OnCursorChanged", function(self) self:HighlightText() end)
                box:SetScript("OnEditFocusLost", function(self) self:SetText(""); self:HighlightText(0, 0) end)
            end
            box:SetScript("OnEscapePressed", function(self) self:ClearFocus() end)

            return box, bg
        end

        W:CreateSectionHeader(sc, L["IMPORTEXPORT_SECTION_EXPORT"], -480)

        local exportBtn = W:CreateButton(sc, { text = L["IMPORTEXPORT_EXPORT_BTN"], width = 130, height = 26 })
        exportBtn:SetPoint("TOPLEFT", 35, -515)

        local exportHint = sc:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
        exportHint:SetPoint("LEFT", exportBtn, "RIGHT", 10, 0)
        exportHint:SetText(L["IMPORTEXPORT_EXPORT_HINT"])

        local exportBox = MakeTextBox(sc, -548, 80, true)

        exportBtn:SetScript("OnClick", function()
            if not ns.SettingsIO then return end
            local str = ns.SettingsIO:Export()
            exportBox:SetText(str)
            exportBox:SetFocus(); exportBox:HighlightText()
        end)

        W:CreateSectionHeader(sc, L["IMPORTEXPORT_SECTION_IMPORT"], -645)

        local importBox = MakeTextBox(sc, -680, 80, false)

        local loadBtn = W:CreateButton(sc, { text = L["IMPORTEXPORT_LOAD_BTN"], width = 80, height = 26 })
        loadBtn:SetPoint("TOPLEFT", 35, -768)

        local statusText = sc:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
        statusText:SetPoint("LEFT", loadBtn, "RIGHT", 10, 0)
        statusText:SetText("")

        local checkContainer = CreateFrame("Frame", nil, sc)
        checkContainer:SetPoint("TOPLEFT", 35, -800); checkContainer:SetSize(430, 1)

        local checkBoxes = {}
        local checkPool = {}
        local importBtn

        local function ClearChecks()
            for _, cb in pairs(checkBoxes) do cb:Hide() end
            checkBoxes = {}
            if importBtn then importBtn:Hide() end
            statusText:SetText("")
        end

        local function GetPooledCheckbox()
            for _, cb in ipairs(checkPool) do
                if not cb:IsShown() then return cb end
            end
            local cb = W:CreateCheckbox(checkContainer, {
                template = "interface",
            })
            checkPool[#checkPool + 1] = cb
            return cb
        end

        local function BuildChecks(foundKeys)
            ClearChecks()
            if not foundKeys then
                statusText:SetText("|cffff4444" .. L["IMPORTEXPORT_INVALID"] .. "|r")
                return
            end

            local yOff = 0
            local count = 0
            for _, m in ipairs(ns.SettingsIO.modules) do
                if foundKeys[m.key] then
                    local cb = GetPooledCheckbox()
                    cb:ClearAllPoints()
                    cb:SetPoint("TOPLEFT", 0, yOff)
                    cb.Text:SetText(m.label)
                    cb:SetChecked(true)
                    cb:Show()
                    checkBoxes[m.key] = cb
                    yOff = yOff - 28
                    count = count + 1
                end
            end

            if count == 0 then
                statusText:SetText("|cffff4444" .. L["IMPORTEXPORT_NO_MODULES"] .. "|r")
                return
            end

            statusText:SetText("|cff44ff44" .. string.format(L["IMPORTEXPORT_FOUND"], count) .. "|r")

            if not importBtn then
                importBtn = W:CreateButton(checkContainer, {
                    text = L["IMPORTEXPORT_IMPORT_BTN"],
                    width = 130,
                    height = 26,
                    onClick = function()
                        local selected = {}
                        for key, cb in pairs(checkBoxes) do
                            if cb:GetChecked() then selected[key] = true end
                        end
                        local raw = strtrim(importBox:GetText())

                        -- Prompt for a new profile name so imported settings
                        -- don't overwrite the current profile.
                        local dialog = StaticPopup_Show("NAOWH_QOL_PROFILE_NAME", L["IMPORTEXPORT_POPUP_IMPORT"])
                        if dialog then
                            dialog.data = {
                                default = "",
                                callback = function(newName)
                                    -- Prevent overwriting an existing profile
                                    local profiles = ns.SettingsIO:GetProfileList()
                                    for _, pname in ipairs(profiles) do
                                        if pname == newName then
                                            statusText:SetText("|cffff4444" .. L["IMPORTEXPORT_ERR_EXISTS"] .. "|r")
                                            return
                                        end
                                    end

                                    -- Create the new profile, import into it, then save
                                    ns.SettingsIO:CreateDefaultProfile(newName)
                                    local ok, err = ns.SettingsIO:Import(raw, selected)
                                    if ok then
                                        ns.SettingsIO:SaveProfile(newName)
                                        RefreshProfileDropdown()
                                        if RefreshSpecDropdowns then RefreshSpecDropdowns() end
                                        UpdateProfileStatus()
                                        statusText:SetText("|cff44ff44" .. string.format(L["IMPORTEXPORT_IMPORT_OK_PROFILE"], newName) .. "|r")
                                        StaticPopup_Show("NAOWH_QOL_RELOAD")
                                    else
                                        statusText:SetText("|cffff4444" .. (err or L["IMPORTEXPORT_IMPORT_ERR"]) .. "|r")
                                    end
                                end
                            }
                        end
                    end
                })
            end
            importBtn:ClearAllPoints()
            importBtn:SetPoint("TOPLEFT", 0, yOff - 8)
            importBtn:Show()

            checkContainer:SetHeight(math.abs(yOff) + 40)
        end

        loadBtn:SetScript("OnClick", function()
            local raw = strtrim(importBox:GetText())
            if raw == "" then ClearChecks(); statusText:SetText("|cffff4444" .. L["IMPORTEXPORT_PASTE_FIRST"] .. "|r"); return end
            local found = ns.SettingsIO:Preview(raw)
            BuildChecks(found)
        end)

    end)
end
