local ADDON_NAME = "Grimoire of Insight"
local ADDON_VERSION = "1.0.0"
local PREFIX = "|cffFF6B35[Grimoire]|r "

local db  -- points at GrimoireDB after ADDON_LOADED

-- ---------------------------------------------------------------------------
-- SavedVariables bootstrap
-- ---------------------------------------------------------------------------

local function InitDB()
    if not GrimoireDB then GrimoireDB = {} end
    if not GrimoireDB.minimap then GrimoireDB.minimap = { hide = false } end
    db = GrimoireDB
end

-- ---------------------------------------------------------------------------
-- Import popup (SimulationCraft-style copyable editbox)
-- ---------------------------------------------------------------------------

local popupFrame = nil

local function GetOrCreatePopup()
    if popupFrame then return popupFrame end

    local f = CreateFrame("Frame", "GrimoirePopupFrame", UIParent, "DialogBoxFrame")
    f:SetSize(420, 150)
    f:SetPoint("CENTER")
    f:SetMovable(true)
    f:SetClampedToScreen(true)
    f:SetScript("OnMouseDown", function(self, button)
        if button == "LeftButton" then self:StartMoving() end
    end)
    f:SetScript("OnMouseUp", function(self)
        self:StopMovingOrSizing()
    end)

    -- Title
    local title = f:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    title:SetPoint("TOP", f, "TOP", 0, -14)
    title:SetText("Grimoire of Insight — Copy string then paste into the app")

    -- Hide the template's built-in button and build our own from scratch
    GrimoirePopupFrameButton:Hide()

    local copyBtn = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
    copyBtn:SetSize(130, 24)
    copyBtn:SetPoint("BOTTOM", f, "BOTTOM", -70, 12)
    copyBtn:SetText("Copy to Clipboard")
    copyBtn:SetScript("OnClick", function()
        GrimoireEditBox:HighlightText()
        if C_Clipboard and C_Clipboard.SetText then
            C_Clipboard.SetText(GrimoireEditBox:GetText())
        end
        f:Hide()
    end)

    local closeBtn = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
    closeBtn:SetSize(130, 24)
    closeBtn:SetPoint("BOTTOM", f, "BOTTOM", 70, 12)
    closeBtn:SetText("Close")
    closeBtn:SetScript("OnClick", function() f:Hide() end)

    -- Single-line centered editbox
    local ctrlDown = false
    local eb = CreateFrame("EditBox", "GrimoireEditBox", f, "InputBoxTemplate")
    eb:SetSize(360, 32)
    eb:SetPoint("CENTER", f, "CENTER", 0, 12)
    eb:SetAutoFocus(true)
    eb:SetFontObject("ChatFontNormal")
    eb:SetMaxLetters(2048)
    eb:SetMultiLine(false)
    eb:SetJustifyH("CENTER")
    eb:SetScript("OnEscapePressed", function() f:Hide() end)
    eb:SetScript("OnMouseDown", function(self)
        C_Timer.After(0, function() self:HighlightText() end)
    end)
    -- Auto-close on Ctrl+C (same pattern as SimulationCraft)
    eb:SetScript("OnKeyDown", function(self, key)
        if key == "LCTRL" or key == "RCTRL" then ctrlDown = true end
    end)
    eb:SetScript("OnKeyUp", function(self, key)
        if key == "LCTRL" or key == "RCTRL" then
            C_Timer.After(0.2, function() ctrlDown = false end)
        end
        if ctrlDown and (key == "C" or key == "X") then
            C_Timer.After(0.1, function() f:Hide() end)
        end
    end)

    popupFrame = f
    return f
end

local function ShowGrimoirePopup(str)
    local f = GetOrCreatePopup()
    GrimoireEditBox:SetText(str)
    GrimoireEditBox:HighlightText()
    GrimoireEditBox:SetFocus()
    f:Show()
    f:Raise()
end

-- ---------------------------------------------------------------------------
-- Core: collect target and show popup
-- ---------------------------------------------------------------------------

local function CollectUnitData(unit)
    if not UnitExists(unit) then
        return nil, "No target selected."
    end
    if not UnitIsPlayer(unit) then
        return nil, "Target is not a player."
    end
    local name, realmArg = UnitName(unit)
    local realm = (realmArg and realmArg ~= "") and realmArg or GetRealmName()
    return { name = name, realm = realm }
end

local function SendToGrimoire(unit)
    unit = unit or "target"
    local data, err = CollectUnitData(unit)
    if not data then
        print(PREFIX .. (err or "Unknown error."))
        return
    end
    ShowGrimoirePopup(data.name .. "-" .. data.realm)
end

-- ---------------------------------------------------------------------------
-- Group collection: /grim r (raid) and /grim p (party)
-- ---------------------------------------------------------------------------

local function CollectGroup(groupType)
    local members = {}
    if groupType == "raid" then
        if not IsInRaid() then
            print(PREFIX .. "You are not in a raid group.")
            return nil
        end
        local count = GetNumGroupMembers()
        for i = 1, count do
            local unit = "raid" .. i
            if UnitExists(unit) and UnitIsPlayer(unit) then
                local name, realm = UnitName(unit)
                realm = (realm and realm ~= "") and realm or GetRealmName()
                table.insert(members, name .. "-" .. realm)
            end
        end
    elseif groupType == "party" then
        if not IsInGroup() then
            print(PREFIX .. "You are not in a group.")
            return nil
        end
        -- Include self
        local pname, prealm = UnitName("player")
        prealm = (prealm and prealm ~= "") and prealm or GetRealmName()
        table.insert(members, pname .. "-" .. prealm)
        local count = GetNumSubgroupMembers()
        for i = 1, count do
            local unit = "party" .. i
            if UnitExists(unit) and UnitIsPlayer(unit) then
                local name, realm = UnitName(unit)
                realm = (realm and realm ~= "") and realm or GetRealmName()
                table.insert(members, name .. "-" .. realm)
            end
        end
    end
    if #members == 0 then
        print(PREFIX .. "No members found.")
        return nil
    end
    return members
end

local function SendGroupToGrimoire(groupType)
    local members = CollectGroup(groupType)
    if not members then return end
    print(string.format("%sCollected %d members. Copy the string and paste into the app.", PREFIX, #members))
    ShowGrimoirePopup(table.concat(members, ","))
end

-- ---------------------------------------------------------------------------
-- Slash command: /grim
-- ---------------------------------------------------------------------------

SLASH_GRIMOIRE1 = "/grim"
SlashCmdList["GRIMOIRE"] = function(msg)
    msg = strtrim(msg or "")
    if msg == "r" then
        SendGroupToGrimoire("raid")
    elseif msg == "p" then
        SendGroupToGrimoire("party")
    elseif msg == "help" then
        print(PREFIX .. "/grim — target  |  /grim p — party  |  /grim r — raid")
    else
        SendToGrimoire("target")
    end
end

-- ---------------------------------------------------------------------------
-- Portrait right-click menu
-- ---------------------------------------------------------------------------

local UNIT_MENU_TYPES = {
    "ARENAENEMY", "ENEMY_PLAYER", "FOCUS", "TARGET",
    "PLAYER", "SELF", "PARTY", "RAID", "RAID_PLAYER",
    "FRIEND", "GUILD", "GUILD_OFFLINE",
    "CHAT_ROSTER", "COMMUNITIES_GUILD_MEMBER", "COMMUNITIES_WOW_MEMBER",
    "BN_FRIEND", "WORLD_STATE_SCORE",
}

local function InjectMenuItem()
    if not (Menu and Menu.ModifyMenu) then return end
    for _, menuType in ipairs(UNIT_MENU_TYPES) do
        Menu.ModifyMenu("MENU_UNIT_" .. menuType, function(owner, rootDescription, contextData)
            local unit = contextData and contextData.unit
            if not unit or not UnitExists(unit) or not UnitIsPlayer(unit) then return end
            rootDescription:CreateDivider()
            rootDescription:CreateTitle("Grimoire of Insight")
            rootDescription:CreateButton("Send to Grimoire", function()
                SendToGrimoire(unit)
            end)
        end)
    end
end

-- ---------------------------------------------------------------------------
-- Settings panel (ESC → Options → AddOns → Grimoire of Insight)
-- ---------------------------------------------------------------------------

local settingsCategory

local function CreateSettingsPanel()
    if settingsCategory then return end

    local f = CreateFrame("Frame", "GrimoireSettingsFrame")

    local scrollFrame = CreateFrame("ScrollFrame", nil, f)
    scrollFrame:SetPoint("TOPLEFT", 0, 0)
    scrollFrame:SetPoint("BOTTOMRIGHT", 0, 0)
    scrollFrame:EnableMouseWheel(true)
    scrollFrame:SetScript("OnMouseWheel", function(self, delta)
        local cur = self:GetVerticalScroll()
        local max = self:GetVerticalScrollRange()
        self:SetVerticalScroll(math.max(0, math.min(max, cur - delta * 40)))
    end)

    local content = CreateFrame("Frame", nil, scrollFrame)
    content:SetWidth(f:GetWidth() or 600)
    scrollFrame:SetScrollChild(content)

    f:SetScript("OnSizeChanged", function(self, w, h)
        content:SetWidth(w)
        scrollFrame:SetWidth(w)
        scrollFrame:SetHeight(h)
    end)

    local yPos = -16

    -- Title
    local titleText = content:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    titleText:SetPoint("TOPLEFT", 12, yPos)
    titleText:SetText("Grimoire of Insight  v" .. ADDON_VERSION)
    yPos = yPos - 36

    -- ── Section: Minimap ──────────────────────────────────────────────────
    local mmHeader = content:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    mmHeader:SetPoint("TOPLEFT", 12, yPos)
    mmHeader:SetText("Minimap")
    mmHeader:SetTextColor(1, 0.82, 0, 1)
    yPos = yPos - 26

    local mmCB = CreateFrame("CheckButton", nil, content, "UICheckButtonTemplate")
    mmCB:SetPoint("TOPLEFT", 10, yPos)
    mmCB:SetChecked(not db.minimap.hide)
    local mmLabel = mmCB:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    mmLabel:SetPoint("LEFT", mmCB, "RIGHT", 4, 0)
    mmLabel:SetText("Show minimap icon")
    mmLabel:SetTextColor(1, 1, 1, 1)
    mmCB:SetScript("OnClick", function(cb)
        local show = cb:GetChecked()
        db.minimap.hide = not show
        local LDBIcon = LibStub and LibStub("LibDBIcon-1.0", true)
        if LDBIcon then
            if show then LDBIcon:Show("GrimoireOfInsight")
            else          LDBIcon:Hide("GrimoireOfInsight") end
        end
    end)
    yPos = yPos - 44

    -- ── Section: Commands ─────────────────────────────────────────────────
    local cmdHeader = content:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    cmdHeader:SetPoint("TOPLEFT", 12, yPos)
    cmdHeader:SetText("Commands")
    cmdHeader:SetTextColor(1, 0.82, 0, 1)
    yPos = yPos - 26

    local commands = {
        { cmd = "/grim",      desc = "Open copy popup for your current target" },
        { cmd = "/grim p",    desc = "Copy your current party for bulk import" },
        { cmd = "/grim r",    desc = "Copy all raid members (up to 40) for bulk import" },
        { cmd = "/grim help", desc = "Print command list to chat" },
    }

    for _, entry in ipairs(commands) do
        local cmdText = content:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
        cmdText:SetPoint("TOPLEFT", 14, yPos)
        cmdText:SetText(entry.cmd)
        cmdText:SetTextColor(1, 0.82, 0, 1)

        local descText = content:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
        descText:SetPoint("TOPLEFT", 130, yPos)
        descText:SetText(entry.desc)
        descText:SetTextColor(0.85, 0.85, 0.85, 1)

        yPos = yPos - 22
    end

    content:SetHeight(math.abs(yPos) + 20)

    local category = Settings.RegisterCanvasLayoutCategory(f, "Grimoire of Insight")
    Settings.RegisterAddOnCategory(category)
    settingsCategory = category
end

-- ---------------------------------------------------------------------------
-- Minimap button (LibDataBroker + LibDBIcon)
-- ---------------------------------------------------------------------------

local function SetupMinimapButton()
    local LDB     = LibStub and LibStub("LibDataBroker-1.1", true)
    local LDBIcon = LibStub and LibStub("LibDBIcon-1.0", true)
    if not LDB or not LDBIcon then return end

    local broker = LDB:NewDataObject("GrimoireOfInsight", {
        type = "launcher",
        text = "Grimoire of Insight",
        icon = "Interface\\AddOns\\Grimoire of Insight\\icon.png",
        OnClick = function(_, button)
            if button == "LeftButton" then
                if not settingsCategory then CreateSettingsPanel() end
                Settings.OpenToCategory(settingsCategory:GetID())
            end
        end,
        OnTooltipShow = function(tooltip)
            tooltip:AddLine("Grimoire of Insight")
            tooltip:AddLine("Left-click to open settings.", 1, 1, 1)
            tooltip:AddLine("Right-click a portrait or use /grim to import characters.", 1, 1, 1)
        end,
    })

    LDBIcon:Register("GrimoireOfInsight", broker, db.minimap)
end

-- ---------------------------------------------------------------------------
-- Initialisation
-- ---------------------------------------------------------------------------

local initFrame = CreateFrame("Frame")
initFrame:RegisterEvent("ADDON_LOADED")
initFrame:SetScript("OnEvent", function(self, event, arg1)
    if event == "ADDON_LOADED" and arg1 == ADDON_NAME then
        InitDB()
        InjectMenuItem()
        CreateSettingsPanel()
        SetupMinimapButton()
        print(string.format("%sv%s loaded. /grim on a target, or right-click a portrait.", PREFIX, ADDON_VERSION))
        self:UnregisterEvent("ADDON_LOADED")
    end
end)
