-- [ PROFILES TAB ]-----------------------------------------------------------------------------------
-- localization: the "Global" profile name in the dropdown is a reserved identifier — only localize the displayed `text`, never the `value = "Global"` literal.

local _, Orbit = ...
local L = Orbit.L
local OrbitEngine = Orbit.Engine
local Layout = OrbitEngine.Layout
local Constants = Orbit.Constants
local GameTooltip = Orbit.Tooltip

local Panel = Orbit.OptionsPanel

-- [ CONSTANTS ]--------------------------------------------------------------------------------------
local DROPDOWN_WIDTH = 200
local SPACER_SMALL = 10
local SPACER_LARGE = 20
local ICON_BUTTON_SIZE = 14
local ARROW_SIZE = 16
local ARROW_DOWN = -math.pi / 2
local ARROW_UP = math.pi / 2
local CHECKBOX_SIZE = 20
local DISABLED_ALPHA = 0.4
local HOVER_ALPHA = 0.8

-- Hover tooltip on the row's label region explaining what this profile scope affects. Pinned OUTSIDE the dialog's right edge, vertically aligned with the row, so it never covers the settings being read.
local TOOLTIP_DIALOG_GAP = 10

local function TopLevelDialog(f)
    local p = f:GetParent()
    while p and p ~= UIParent do f = p; p = f:GetParent() end
    return f
end

local function AttachLabelTooltip(frame, def)
    if not def.tooltip or not frame.Label then return end
    local hover = CreateFrame("Frame", nil, frame)
    hover:SetPoint("TOPLEFT", frame.Label, "TOPLEFT", 0, 2)
    hover:SetPoint("BOTTOMRIGHT", frame.Label, "BOTTOMRIGHT", 0, -2)
    hover:EnableMouse(true)
    hover:SetScript("OnEnter", function(self)
        local dialog = TopLevelDialog(self)
        GameTooltip:SetOwner(self, "ANCHOR_NONE")
        GameTooltip:ClearAllPoints()
        local y = (self:GetTop() or 0) - (dialog:GetTop() or 0)
        -- Side-aware: pin to whichever side of the dialog has more screen room.
        local rightSpace = UIParent:GetWidth() - (dialog:GetRight() or 0)
        local leftSpace = dialog:GetLeft() or 0
        if rightSpace >= leftSpace then
            GameTooltip:SetPoint("TOPLEFT", dialog, "TOPRIGHT", TOOLTIP_DIALOG_GAP, y)
        else
            GameTooltip:SetPoint("TOPRIGHT", dialog, "TOPLEFT", -TOOLTIP_DIALOG_GAP, y)
        end
        GameTooltip:SetText(def.label, 1, 0.82, 0)
        GameTooltip:AddLine(def.tooltip, 1, 1, 1, true)
        GameTooltip:Show()
    end)
    hover:SetScript("OnLeave", function() GameTooltip:Hide() end)
end

-- [ WIDGET: PROFILE ACTIVE ]-------------------------------------------------------------------------
-- Composite widget: dropdown for active profile (greyed out when spec profiles control it)
Layout:RegisterWidgetType("profileactive", function(container, def, getValue, callback)
    local options = type(def.options) == "function" and def.options() or def.options or {}
    local initialValue = getValue and getValue() or def.default or ""
    local frame = Layout:CreateDropdown(container, def.label, options, initialValue, function(value)
        if callback then callback(value) end
    end)
    frame.OrbitType = "ProfileActive"
    AttachLabelTooltip(frame, def)
    if Orbit.Profile:IsSpecProfilesEnabled() then
        frame:SetAlpha(DISABLED_ALPHA)
        if frame.Dropdown then
            frame.Dropdown:SetEnabled(false)
            frame.Dropdown:EnableMouse(false)
        end
        local overlay = CreateFrame("Frame", nil, frame)
        overlay:SetAllPoints()
        overlay:SetFrameLevel((frame.Dropdown and frame.Dropdown:GetFrameLevel() or frame:GetFrameLevel()) + 10)
        overlay:EnableMouse(true)
        overlay:SetScript("OnEnter", function(self)
            GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
            GameTooltip:SetText(L.CFG_SPEC_PROFILES_CONTROL)
            GameTooltip:Show()
        end)
        overlay:SetScript("OnLeave", function() GameTooltip:Hide() end)
    end
    return frame
end)

-- [ WIDGET: PROFILE SELECT ]-------------------------------------------------------------------------
-- Composite widget: dropdown + reset/copy/delete icons
Layout:RegisterWidgetType("profileselect", function(container, def, getValue, callback)
    local options = type(def.options) == "function" and def.options() or def.options or {}
    local initialValue = getValue and getValue() or def.default or ""
    local UpdateButtonStates
    local frame = Layout:CreateDropdown(container, def.label, options, initialValue, function(value)
        if callback then callback(value) end
        C_Timer.After(0, function() if UpdateButtonStates then UpdateButtonStates() end end)
    end)
    frame.OrbitType = "ProfileSelect"
    AttachLabelTooltip(frame, def)

    UpdateButtonStates = function()
        if not frame.resetBtn then return end
        local val = frame.currentValue
        -- def.canReset/canDelete parameterize the rules (account row); default = layout-profile rules.
        local resetOK
        if def.canReset then resetOK = def.canReset(val) else resetOK = (val == "Global") end
        local deleteOK
        if def.canDelete then deleteOK = def.canDelete(val)
        else deleteOK = val ~= "Global" and val ~= Orbit.Profile:GetActiveProfileName() end
        if resetOK then
            frame.resetBtn:Enable()
            frame.resetBtn.Icon:SetDesaturated(false)
            frame.resetBtn.Icon:SetAlpha(1)
        else
            frame.resetBtn:Disable()
            frame.resetBtn.Icon:SetDesaturated(true)
            frame.resetBtn.Icon:SetAlpha(DISABLED_ALPHA)
        end
        -- Copy: always allowed
        frame.copyBtn:Enable()
        frame.copyBtn.Icon:SetDesaturated(false)
        frame.copyBtn.Icon:SetAlpha(1)
        if deleteOK then
            frame.xBtn:Enable()
            frame.xBtn.Icon:SetDesaturated(false)
            frame.xBtn.Icon:SetAlpha(1)
        else
            frame.xBtn:Disable()
            frame.xBtn.Icon:SetDesaturated(true)
            frame.xBtn.Icon:SetAlpha(DISABLED_ALPHA)
        end
    end

    local gap = Constants.Widget.ValueWidth
    local tripleWidth = ICON_BUTTON_SIZE * 3 + 4
    local tripleOffset = (gap - tripleWidth) / 2

    -- Reset button (undo icon)
    local resetBtn = CreateFrame("Button", nil, frame)
    resetBtn:SetSize(ICON_BUTTON_SIZE, ICON_BUTTON_SIZE)
    resetBtn:SetPoint("LEFT", frame.Dropdown, "RIGHT", tripleOffset + 10, 0)
    frame.resetBtn = resetBtn
    local resetIcon = resetBtn:CreateTexture(nil, "ARTWORK")
    resetIcon:SetAllPoints()
    resetIcon:SetAtlas("talents-button-undo")
    resetBtn.Icon = resetIcon
    resetBtn:SetScript("OnEnter", function(self)
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        if self:IsEnabled() then
            self.Icon:SetAlpha(HOVER_ALPHA)
            GameTooltip:SetText(L.CMN_RESET_PROFILE_TOOLTIP)
        else
            GameTooltip:SetText(L.CMN_ONLY_GLOBAL_RESET)
        end
        GameTooltip:Show()
    end)
    resetBtn:SetScript("OnLeave", function(self)
        if self:IsEnabled() then self.Icon:SetAlpha(1) end
        GameTooltip:Hide()
    end)
    resetBtn:SetScript("OnClick", function()
        if def.onReset then def.onReset(frame.currentValue) end
    end)

    -- Copy button (+ icon)
    local copyBtn = CreateFrame("Button", nil, frame)
    copyBtn:SetSize(ICON_BUTTON_SIZE, ICON_BUTTON_SIZE)
    copyBtn:SetPoint("LEFT", resetBtn, "RIGHT", 2, 0)
    frame.copyBtn = copyBtn
    local copyIcon = copyBtn:CreateTexture(nil, "ARTWORK")
    copyIcon:SetAllPoints()
    copyIcon:SetAtlas("communities-chat-icon-plus")
    copyBtn.Icon = copyIcon
    copyBtn:SetScript("OnEnter", function(self)
        if self:IsEnabled() then
            self.Icon:SetAlpha(HOVER_ALPHA)
            GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
            GameTooltip:SetText(L.CMN_COPY_PROFILE_TOOLTIP)
            GameTooltip:Show()
        end
    end)
    copyBtn:SetScript("OnLeave", function(self)
        if self:IsEnabled() then self.Icon:SetAlpha(1) end
        GameTooltip:Hide()
    end)
    copyBtn:SetScript("OnClick", function()
        if def.onCopy then def.onCopy(frame.currentValue) end
    end)

    -- Delete button (X icon)
    local xBtn = CreateFrame("Button", nil, frame)
    xBtn:SetSize(ICON_BUTTON_SIZE, ICON_BUTTON_SIZE)
    xBtn:SetPoint("LEFT", copyBtn, "RIGHT", 2, 0)
    frame.xBtn = xBtn
    local xIcon = xBtn:CreateTexture(nil, "ARTWORK")
    xIcon:SetAllPoints()
    xIcon:SetAtlas("transmog-icon-remove")
    xBtn:SetScript("OnEnter", function(self)
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        if self:IsEnabled() then
            self.Icon:SetAlpha(HOVER_ALPHA)
            GameTooltip:SetText(L.CMN_DELETE_PROFILE_TOOLTIP)
        else
            GameTooltip:SetText(L.CMN_CANNOT_DELETE_ACTIVE)
        end
        GameTooltip:Show()
    end)
    xBtn:SetScript("OnLeave", function(self)
        if self:IsEnabled() then self.Icon:SetAlpha(1) end
        GameTooltip:Hide()
    end)
    xBtn:SetScript("OnClick", function()
        if def.onDelete then def.onDelete(frame.currentValue) end
    end)
    xBtn.Icon = xIcon

    UpdateButtonStates()
    return frame
end)

-- [ WIDGET: COLLAPSE HEADER ]------------------------------------------------------------------------
-- Collapsible header: text + toggle arrow (far right)
Layout:RegisterWidgetType("collapseheader", function(container, def)
    local frame = CreateFrame("Button", nil, container)
    frame:SetHeight(30)
    frame.OrbitType = "CollapseHeader"

    local text = frame:CreateFontString(nil, "ARTWORK", "GameFontNormalHuge")
    text:SetPoint("LEFT", 0, 0)
    text:SetText(def.text or "")

    local arrow = frame:CreateTexture(nil, "ARTWORK")
    arrow:SetSize(ARROW_SIZE, ARROW_SIZE)
    arrow:SetPoint("RIGHT", frame, "RIGHT", 0, 0)
    arrow:SetAtlas("shop-header-arrow")
    arrow:SetRotation(def.collapsed and ARROW_DOWN or ARROW_UP)

    frame:SetScript("OnClick", function()
        if def.onToggle then def.onToggle() end
    end)
    frame:SetScript("OnEnter", function()
        arrow:SetAtlas("shop-header-arrow-hover")
        arrow:SetRotation(def.collapsed and ARROW_DOWN or ARROW_UP)
    end)
    frame:SetScript("OnLeave", function()
        arrow:SetAtlas("shop-header-arrow")
        arrow:SetRotation(def.collapsed and ARROW_DOWN or ARROW_UP)
    end)
    frame:SetScript("OnMouseDown", function()
        arrow:SetAtlas("shop-header-arrow-pressed")
        arrow:SetRotation(def.collapsed and ARROW_DOWN or ARROW_UP)
    end)
    frame:SetScript("OnMouseUp", function()
        arrow:SetAtlas("shop-header-arrow-hover")
        arrow:SetRotation(def.collapsed and ARROW_DOWN or ARROW_UP)
    end)

    return frame
end)

-- [ WIDGET: CHECK HEADER ]---------------------------------------------------------------------------
-- Header with checkbox toggle on the right (same visual as collapseheader)
Layout:RegisterWidgetType("checkheader", function(container, def, getValue, callback)
    local frame = CreateFrame("Frame", nil, container)
    frame:SetHeight(30)
    frame.OrbitType = "CheckHeader"
    local text = frame:CreateFontString(nil, "ARTWORK", "GameFontNormalHuge")
    text:SetPoint("LEFT", 0, 0)
    text:SetText(def.text or "")
    local cb = CreateFrame("CheckButton", nil, frame, "UICheckButtonTemplate")
    cb:SetSize(CHECKBOX_SIZE, CHECKBOX_SIZE)
    cb:SetPoint("RIGHT", frame, "RIGHT", 0, 0)
    local checked = getValue and getValue() or false
    cb:SetChecked(checked)
    cb:SetScript("OnClick", function(self)
        if callback then callback(self:GetChecked()) end
    end)
    if def.tooltip then
        cb:SetScript("OnEnter", function(self)
            GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
            GameTooltip:SetText(def.tooltip)
            GameTooltip:Show()
        end)
        cb:SetScript("OnLeave", function() GameTooltip:Hide() end)
    end
    return frame
end)

-- [ STATE ]------------------------------------------------------------------------------------------
local profilesSubView = nil -- nil, "export", "import", "clone", "delete"
local exportSelectedProfile = nil
local exportString = ""
local importString = ""
local importName = ""
local importMeta = nil
local cloneSource = nil
local cloneName = ""
local deleteTarget = nil
local subViewIsAccount = false

-- Sentinel export-value prefix for account entries (never a valid profile name, matching the :CLEAN: convention)
local EXPORT_ACCOUNT_PREFIX = ":ACCOUNT:"

-- [ PLUGIN ]-----------------------------------------------------------------------------------------
local ProfilesPlugin = {
    name = "OrbitProfiles",
    settings = {},
    GetSetting = function(self, systemIndex, key)
        if key == "ActiveProfile" then return Orbit.Profile:GetActiveProfileName()
        elseif key == "AccountProfile" then return Orbit.Profile:GetActiveAccountProfileName()
        elseif key == "UseSpecProfiles" then return Orbit.Profile:IsSpecProfilesEnabled()
        elseif key == "CreateProfile" then return Orbit.Profile._selectedToCreate or "Global"
        elseif key == "ExportProfile" then return exportSelectedProfile or "Global"
        elseif key == "ExportString" then return exportString
        elseif key == "ImportName" then return importName
        elseif key == "CloneName" then return cloneName
        end
        local specID = key:match("^SpecMapping_(%d+)$")
        if specID then return Orbit.Profile:GetProfileForSpec(tonumber(specID)) or "Global" end
        return nil
    end,
    SetSetting = function(self, systemIndex, key, value)
        if key == "ActiveProfile" then
            Orbit.Profile:SetActiveProfile(value)
            if Orbit.OptionsPanel then
                Orbit.OptionsPanel.lastTab = nil
                Orbit.OptionsPanel:Open(L.CFG_TAB_PROFILES)
            end
        elseif key == "UseSpecProfiles" then
            Orbit.Profile:SetSpecProfilesEnabled(value)
            if value then
                Orbit.Profile:CheckSpecProfile()
            else
                if Orbit.db.specMappings then wipe(Orbit.db.specMappings) end
            end
            if Orbit.OptionsPanel then Orbit.OptionsPanel.lastTab = nil; Orbit.OptionsPanel:Open(L.CFG_TAB_PROFILES) end
        elseif key == "CreateProfile" then
            Orbit.Profile._selectedToCreate = value
        elseif key == "AccountProfile" then
            Orbit.Profile:SetActiveAccountProfile(value)
            if Orbit.OptionsPanel then Orbit.OptionsPanel.lastTab = nil; Orbit.OptionsPanel:Open(L.CFG_TAB_PROFILES) end
        elseif key == "ExportProfile" then
            exportSelectedProfile = value
            local accountName = value:match("^" .. EXPORT_ACCOUNT_PREFIX .. "(.+)$")
            local str
            if accountName then str = Orbit.Profile:ExportAccountData(accountName)
            else str = Orbit.Profile:ExportSingleProfile(value) end
            exportString = str or ""
            if Orbit.OptionsPanel then Orbit.OptionsPanel.lastTab = nil; Orbit.OptionsPanel:Open(L.CFG_TAB_PROFILES) end
        elseif key == "ExportString" then
            exportString = value or ""
        elseif key == "ImportName" then
            importName = value or ""
        elseif key == "CloneName" then
            cloneName = value or ""
        else
            local specID = key:match("^SpecMapping_(%d+)$")
            if specID then
                specID = tonumber(specID)
                local profileName = (value == "Global") and nil or value
                Orbit.Profile:SetProfileForSpec(specID, profileName)
                local _, specName = GetSpecializationInfoByID(specID)
                Orbit:Print(L.MSG_SPEC_MAPPED_F:format(specName or "?", value or "Global"))
                Orbit.Profile:CheckSpecProfile()
            end
        end
    end,
    ApplySettings = function(self, systemFrame) end,
}

-- [ HELPERS ]----------------------------------------------------------------------------------------
-- Display-only rename: the reserved internal identifier stays "Global" (profile data and exports depend on it); every label shows the localized "Default".
local function ProfileDisplayName(name)
    return name == "Global" and L.CFG_PROFILE_GLOBAL or name
end

local function GetProfileOptions()
    local opts = {}
    for _, n in ipairs(Orbit.Profile:GetProfiles()) do
        table.insert(opts, { text = ProfileDisplayName(n), value = n })
    end
    return opts
end

local function GetProfileOptionsWithDefault()
    local opts = { { text = L.CFG_PROFILE_GLOBAL, value = "Global" } }
    for _, n in ipairs(Orbit.Profile:GetProfiles()) do
        if n ~= "Global" then
            table.insert(opts, { text = n, value = n })
        end
    end
    return opts
end

local function GetAccountProfileOptions()
    local opts = {}
    for _, n in ipairs(Orbit.Profile:GetAccountProfiles()) do
        table.insert(opts, { text = n == "Default" and L.CFG_DEFAULT_ACCOUNTWIDE or n, value = n })
    end
    return opts
end

-- Export content: the two defaults on top, then titled divider sections for custom account profiles ("Account: <name>") and custom layout profiles.
local function GetExportOptions()
    local opts = {
        { text = L.CFG_PROFILE_GLOBAL, value = "Global" },
        { text = L.CFG_DEFAULT_ACCOUNTWIDE, value = EXPORT_ACCOUNT_PREFIX .. "Default" },
    }
    local customAccounts = {}
    for _, n in ipairs(Orbit.Profile:GetAccountProfiles()) do
        if n ~= "Default" then table.insert(customAccounts, n) end
    end
    if #customAccounts > 0 then
        table.insert(opts, { divider = true, title = L.CFG_ACCOUNT })
        for _, n in ipairs(customAccounts) do
            table.insert(opts, { text = L.CFG_ACCOUNT_PREFIX_F:format(n), value = EXPORT_ACCOUNT_PREFIX .. n })
        end
    end
    local customProfiles = {}
    for _, n in ipairs(Orbit.Profile:GetProfiles()) do
        if n ~= "Global" then table.insert(customProfiles, n) end
    end
    if #customProfiles > 0 then
        table.insert(opts, { divider = true, title = L.CFG_TAB_PROFILES })
        for _, n in ipairs(customProfiles) do
            table.insert(opts, { text = n, value = n })
        end
    end
    return opts
end

-- [ WIDGET: STATUS MESSAGE ]-------------------------------------------------------------------------
local flashLabel
Layout:RegisterWidgetType("statusmessage", function(container, def)
    local frame = CreateFrame("Frame", nil, container)
    frame:SetHeight(20)
    frame.OrbitType = "StatusMessage"
    local text = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    text:SetPoint("CENTER")
    text:SetTextColor(1, 0.3, 0.3)
    text:SetText("")
    frame.Text = text
    flashLabel = frame
    return frame
end)

-- [ WIDGET: IMPORT PASTE BOX ]-----------------------------------------------------------------------
-- Self-validating paste target: grey placeholder inside the box, the verdict replaces the pasted string IN the box (green/red), and any edit over a verdict resets it — all in-place, no view rebuild, so the box never disappears and validation is instant.
local IMPORT_GREEN = { 0.2, 1, 0.4 }
local IMPORT_RED = { 1, 0.35, 0.35 }
local PASTE_MIN_LEN = 50
local importNameEditBox

local function BuildImportVerdict(meta)
    if meta.type == "Account" then return L.CFG_IMPORT_VALID_ACCOUNT_F:format(meta.name or "?") end
    if meta.type == "Collection" then return L.CFG_IMPORT_VALID_COLLECTION end
    return L.CFG_IMPORT_VALID_PROFILE_F:format(meta.name or "?")
end

Layout:RegisterWidgetType("importbox", function(container, def)
    local frame = Layout:CreateEditBox(container, nil, "", nil, nil, def.height, true, { hideScrollBar = true })
    frame.OrbitType = "ImportBox"
    local eb = frame.EditBox

    local placeholder = frame.InputContainer:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
    placeholder:SetPoint("TOPLEFT", 8, -8)
    placeholder:SetText(L.CFG_PASTE_STRING)

    local function ShowVerdict(text, color)
        eb._updating = true
        eb:SetText(text)
        eb:SetTextColor(color[1], color[2], color[3])
        eb._updating = false
        eb._verdict = text
        placeholder:Hide()
    end

    local function ResetBox()
        importString, importMeta = "", nil
        eb._verdict = nil
        eb._updating = true
        eb:SetText("")
        eb._updating = false
        eb:SetTextColor(1, 1, 1)
        placeholder:Show()
    end

    eb:SetScript("OnTextChanged", function(self)
        if self._updating then return end
        local text = self:GetText()
        placeholder:SetShown(text == "")
        if text == "" then
            importString, importMeta = "", nil
            self._verdict = nil
            self:SetTextColor(1, 1, 1)
            return
        end
        -- Editing over a verdict: a fresh paste replaces it, anything smaller means "start over".
        if self._verdict then
            if text == self._verdict then return end
            self._verdict = nil
            if #text < PASTE_MIN_LEN then ResetBox() return end
        end
        if #text < PASTE_MIN_LEN then return end
        local meta = Orbit.Profile:PeekImport(text)
        if meta then
            importString, importMeta = text, meta
            ShowVerdict(BuildImportVerdict(meta), IMPORT_GREEN)
            if meta.name then
                importName = meta.name
                if importNameEditBox then importNameEditBox:SetText(meta.name) end
            end
        else
            importString, importMeta = "", nil
            ShowVerdict(L.CFG_IMPORT_INVALID, IMPORT_RED)
        end
    end)

    -- Re-entering the view keeps the verdict for a still-loaded string.
    if importMeta then
        ShowVerdict(BuildImportVerdict(importMeta), IMPORT_GREEN)
    end
    return frame
end)

Layout:RegisterWidgetType("importname", function(container, def)
    local frame = Layout:CreateEditBox(container, L.CFG_PROFILE_NAME, importName, function(v) importName = v or "" end, nil, def.height)
    importNameEditBox = frame.EditBox
    return frame
end)

local flashActive = false
local function ShowFlashMessage(msg)
    if not flashLabel or flashActive then return end
    flashActive = true
    -- Capture the live label so a recreated statusmessage widget can't strand flashActive on a dead frame.
    local lbl = flashLabel
    lbl.Text:SetText(msg)
    lbl:SetAlpha(0)
    lbl:Show()
    UIFrameFadeIn(lbl, 0.2, 0, 1)
    C_Timer.After(2, function()
        UIFrameFadeOut(lbl, 0.5, 1, 0)
        C_Timer.After(0.5, function() flashActive = false end)
    end)
end

local function ReopenProfiles()
    if Orbit.OptionsPanel then
        Orbit.OptionsPanel.lastTab = nil
        Orbit.OptionsPanel:Open(L.CFG_TAB_PROFILES)
    end
end

-- [ SCHEMA ]-----------------------------------------------------------------------------------------
local function GetProfilesSchema()
    -- Export sub-view
    if profilesSubView == "export" then
        return {
            hideNativeSettings = true,
            hideResetButton = true,
            controls = {
                { type = "header", text = L.CFG_EXPORT_PROFILE },
                { type = "dropdown", key = "ExportProfile", label = L.CFG_EXPORT_CONTENT, options = GetExportOptions, default = "Global", width = DROPDOWN_WIDTH },
                { type = "spacer", height = SPACER_SMALL },
                { type = "editbox", key = "ExportString", label = L.CFG_EXPORT_STRING, height = 120, multiline = true, readOnly = true, hideScrollBar = true },
                { type = "spacer", height = SPACER_SMALL },
            },
            extraButtons = {
                {
                    text = L.CMN_BACK,
                    callback = function() profilesSubView = nil; exportString = ""; exportSelectedProfile = nil; ReopenProfiles() end,
                },
            },
        }
    end

    -- Import sub-view: the paste box validates in place (importbox widget owns the string state).
    if profilesSubView == "import" then
        return {
            hideNativeSettings = true,
            hideResetButton = true,
            controls = {
                { type = "header", text = L.CFG_IMPORT_PROFILE },
                { type = "importbox", height = 70 },
                { type = "statusmessage" },
                { type = "importname", height = 50 },
            },
            extraButtons = {
                {
                    text = L.CMN_BACK,
                    callback = function()
                        profilesSubView = nil
                        importString, importName, importMeta = "", "", nil
                        ReopenProfiles()
                    end,
                },
                {
                    text = L.CMN_APPLY,
                    callback = function()
                        if importString == "" then ShowFlashMessage(L.MSG_PASTE_IMPORT_STRING); return end
                        local mtype = importMeta and importMeta.type
                        if mtype == "Single" or mtype == "Account" then
                            if importName == "" then ShowFlashMessage(L.MSG_ENTER_PROFILE_NAME); return end
                            local existing = (mtype == "Account") and Orbit.Profile:GetAccountProfiles() or Orbit.Profile:GetProfiles()
                            for _, n in ipairs(existing) do
                                if n == importName then ShowFlashMessage(L.MSG_PROFILE_EXISTS_F:format(importName)); return end
                            end
                        end
                        local function applyImport(confirmed)
                            local ok, err = Orbit.Profile:ImportProfile(importString, importName, confirmed)
                            if ok then
                                Orbit:Print(L.MSG_IMPORT_SUCCESS)
                                profilesSubView = nil
                                importString, importName, importMeta = "", "", nil
                                ReopenProfiles()
                            elseif err == "COLLECTION_CONFIRM" then
                                Layout:ShowConfirm({
                                    title = L.CFG_IMPORT_COLLECTION_TITLE,
                                    text = L.CFG_IMPORT_COLLECTION_WARNING,
                                    acceptText = L.CMN_IMPORT,
                                    onAccept = function() applyImport(true) end,
                                })
                            else
                                ShowFlashMessage(L.MSG_INVALID_IMPORT)
                            end
                        end
                        applyImport(false)
                    end,
                },
            },
        }
    end

    -- Clone sub-view (layout or account profile, per subViewIsAccount)
    if profilesSubView == "clone" then
        local sourceDisplay = subViewIsAccount and (cloneSource == "Default" and L.CFG_DEFAULT_ACCOUNTWIDE or cloneSource) or ProfileDisplayName(cloneSource or "?")
        return {
            hideNativeSettings = true,
            hideResetButton = true,
            controls = {
                { type = "header", text = L.CFG_CLONE_PROFILE },
                { type = "description", text = L.CFG_CLONE_PROFILE_DESC_F:format(sourceDisplay) },
                { type = "spacer", height = SPACER_SMALL },
                { type = "editbox", key = "CloneName", label = L.CFG_NEW_NAME, height = 50 },
            },
            extraButtons = {
                { text = L.CMN_BACK, callback = function() profilesSubView = nil; cloneName = ""; cloneSource = nil; ReopenProfiles() end },
                {
                    text = L.CMN_CREATE,
                    callback = function()
                        if cloneName == "" then Orbit:Print(L.MSG_ENTER_PROFILE_NAME); return end
                        local created
                        if subViewIsAccount then
                            created = Orbit.Profile:CreateAccountProfile(cloneName, cloneSource)
                        else
                            created = Orbit.Profile:CreateProfile(cloneName, cloneSource)
                        end
                        if created then
                            Orbit:Print(L.MSG_PROFILE_CREATED_F:format(cloneName, sourceDisplay))
                            profilesSubView = nil; cloneName = ""; cloneSource = nil
                            ReopenProfiles()
                        else
                            Orbit:Print(L.MSG_PROFILE_EXISTS_F:format(cloneName))
                        end
                    end,
                },
            },
        }
    end

    -- Delete sub-view (layout or account profile, per subViewIsAccount)
    if profilesSubView == "delete" then
        return {
            hideNativeSettings = true,
            hideResetButton = true,
            controls = {
                { type = "header", text = L.CFG_DELETE_PROFILE_HEADER },
                { type = "description", text = L.CFG_DELETE_WARNING_F:format(deleteTarget or "?") },
            },
            extraButtons = {
                { text = L.CMN_BACK, callback = function() profilesSubView = nil; deleteTarget = nil; ReopenProfiles() end },
                {
                    text = L.CMN_DELETE,
                    callback = function()
                        if deleteTarget then
                            if subViewIsAccount then
                                Orbit.Profile:DeleteAccountProfile(deleteTarget)
                            else
                                Orbit.Profile:DeleteProfile(deleteTarget)
                            end
                            Orbit:Print(L.MSG_PROFILE_DELETED_F:format(deleteTarget))
                        end
                        profilesSubView = nil; deleteTarget = nil
                        ReopenProfiles()
                    end,
                },
            },
        }
    end

    -- Reset account-data sub-view (Default account profile only)
    if profilesSubView == "resetaccount" then
        return {
            hideNativeSettings = true,
            hideResetButton = true,
            controls = {
                { type = "header", text = L.CFG_RESET_ACCOUNT },
                { type = "description", text = L.CFG_RESET_ACCOUNT_WARNING },
            },
            extraButtons = {
                { text = L.CMN_BACK, callback = function() profilesSubView = nil; ReopenProfiles() end },
                {
                    text = L.CMN_RESET_TO_DEFAULTS,
                    callback = function()
                        Orbit.Profile:ResetAccountProfile(Orbit.Profile.DEFAULT_ACCOUNT_PROFILE)
                        profilesSubView = nil
                        ReopenProfiles()
                    end,
                },
            },
        }
    end

    -- Reset Global sub-view
    if profilesSubView == "resetglobal" then
        return {
            hideNativeSettings = true,
            hideResetButton = true,
            controls = {
                { type = "header", text = L.CFG_RESET_GLOBAL },
                { type = "description", text = L.CFG_RESET_GLOBAL_WARNING },
            },
            extraButtons = {
                { text = L.CMN_BACK, callback = function() profilesSubView = nil; ReopenProfiles() end },
                {
                    text = L.CMN_RESET_TO_DEFAULTS,
                    callback = function()
                        Orbit.API:ResetProfile("Global")
                        profilesSubView = nil
                        ReopenProfiles()
                    end,
                },
            },
        }
    end

    -- Main profiles view
    local controls = {}

    controls[#controls + 1] = { type = "header", text = L.CFG_PROFILE_LABEL }
    controls[#controls + 1] = {
        type = "profileactive", key = "ActiveProfile", label = L.CFG_ACTIVE, tooltip = L.CFG_ACTIVE_TT,
        options = GetProfileOptions, default = "Global", width = DROPDOWN_WIDTH,
    }
    controls[#controls + 1] = {
        type = "profileselect", key = "AccountProfile", label = L.CFG_ACCOUNT, tooltip = L.CFG_ACCOUNT_TT,
        options = GetAccountProfileOptions, default = "Default", width = DROPDOWN_WIDTH,
        canReset = function(v) return v == "Default" end,
        canDelete = function(v) return v ~= "Default" and v ~= Orbit.Profile:GetActiveAccountProfileName() end,
        onReset = function(selected)
            if selected == "Default" then subViewIsAccount = true; profilesSubView = "resetaccount"; ReopenProfiles() end
        end,
        onCopy = function(selected)
            cloneSource = selected
            cloneName = ""
            subViewIsAccount = true
            profilesSubView = "clone"
            ReopenProfiles()
        end,
        onDelete = function(selected)
            if selected == "Default" then return end
            if selected == Orbit.Profile:GetActiveAccountProfileName() then Orbit:Print(L.MSG_CANNOT_DELETE_ACTIVE); return end
            deleteTarget = selected
            subViewIsAccount = true
            profilesSubView = "delete"
            ReopenProfiles()
        end,
    }
    controls[#controls + 1] = {
        type = "profileselect", key = "CreateProfile", label = L.CFG_MANAGE, tooltip = L.CFG_MANAGE_TT,
        options = GetProfileOptions, default = "Global",
        onReset = function(selected)
            if selected == "Global" then subViewIsAccount = false; profilesSubView = "resetglobal"; ReopenProfiles()
            else Orbit:Print(L.MSG_ONLY_GLOBAL_RESET) end
        end,
        onCopy = function(selected)
            cloneSource = selected
            cloneName = ""
            subViewIsAccount = false
            profilesSubView = "clone"
            ReopenProfiles()
        end,
        onDelete = function(selected)
            if selected == "Global" then Orbit:Print(L.MSG_CANNOT_DELETE_GLOBAL); return end
            if selected == Orbit.Profile:GetActiveProfileName() then Orbit:Print(L.MSG_CANNOT_DELETE_ACTIVE); return end
            deleteTarget = selected
            subViewIsAccount = false
            profilesSubView = "delete"
            ReopenProfiles()
        end,
    }
    controls[#controls + 1] = { type = "spacer", height = SPACER_LARGE }

    -- Spec Profiles (header with checkbox toggle)
    controls[#controls + 1] = {
        type = "checkheader", key = "UseSpecProfiles", text = L.CFG_SPEC_PROFILES, default = false,
        tooltip = L.CFG_SPEC_PROFILES_TT,
    }
    if Orbit.Profile:IsSpecProfilesEnabled() then
        local numSpecs = GetNumSpecializations and GetNumSpecializations() or 0
        for i = 1, numSpecs do
            local specID, specName = GetSpecializationInfo(i)
            if specID and specName then
                controls[#controls + 1] = {
                    type = "dropdown", key = "SpecMapping_" .. specID, label = specName,
                    options = GetProfileOptionsWithDefault, default = "Global", width = DROPDOWN_WIDTH,
                }
            end
        end
    end
    controls[#controls + 1] = { type = "spacer", height = SPACER_SMALL }

    return {
        hideNativeSettings = true,
        hideResetButton = true,
        controls = controls,
        extraButtons = {
            { text = L.CMN_EXPORT, callback = function()
                local active = Orbit.Profile:GetActiveProfileName()
                exportSelectedProfile = active
                exportString = Orbit.Profile:ExportSingleProfile(active) or ""
                profilesSubView = "export"
                ReopenProfiles()
            end },
            { text = L.CMN_IMPORT, callback = function() profilesSubView = "import"; ReopenProfiles() end },
        },
    }
end

-- [ REGISTRATION ]-----------------------------------------------------------------------------------
Panel.Tabs[L.CFG_TAB_PROFILES] = { plugin = ProfilesPlugin, schema = GetProfilesSchema }
