EbonholdHub.Catalog = {}

EbonholdHub.Catalog.WEBSITE_URL = "https://ebonholdhub.vercel.app/builds"

local cached
local linkDialog

local function EnsureLinkDialog()
    if linkDialog then return end
    local UI = EbonholdHub.UI
    local C = UI.Colors

    local f = UI.CreateWindow("EbonholdHubWebsiteLinkDialog", 520, 130, "EbonholdHub Website", {
        strata = "FULLSCREEN_DIALOG",
        toplevel = true,
    })
    local content = f.content

    local hint = UI.CreateLabel(content, "Browse public builds on the website:", UI.FONT_SIZE_SMALL, C.textDim)
    hint:SetPoint("TOPLEFT", content, "TOPLEFT", 0, 0)
    f._hint = hint

    local editHolder = UI.CreateEditBox(content, 480, 28, false)
    editHolder:SetPoint("TOPLEFT", hint, "BOTTOMLEFT", 0, -8)
    local box = editHolder.editBox
    box:SetMaxLetters(256)
    box:SetScript("OnEscapePressed", function() f:Hide() end)

    local close = UI.CreateButton(content, 80, 26, "Close")
    close:SetPoint("BOTTOMRIGHT", content, "BOTTOMRIGHT", 0, 0)
    close:SetScript("OnClick", function() f:Hide() end)

    f._editBox = box
    linkDialog = f
end

local function MergeSettings(overrides)
    local settings = EbonholdHub.Build.CloneSettings(EbonholdHub.Build.DefaultSettings())
    if not overrides then return settings end
    for key, value in pairs(overrides) do
        if type(value) == "table" and type(settings[key]) == "table" then
            for subKey, subValue in pairs(value) do
                settings[key][subKey] = subValue
            end
        else
            settings[key] = value
        end
    end
    return settings
end

local function NormalizeExport(export)
    if not export then return "" end
    return export:gsub("%s+", "")
end

function EbonholdHub.Catalog.EnsureStorage()
    EbonholdHubDB.catalog = EbonholdHubDB.catalog or {}
end

local function NormalizeEntry(entry)
    if entry.export and entry.export ~= "" and EbonholdHub.ExportImport and EbonholdHub.ExportImport.DecodeAnyExport then
        local build = EbonholdHub.ExportImport.DecodeAnyExport(entry.export)
        if build then
            build.id = entry.id
            build.title = entry.title or build.title
            build.class = entry.class or build.class
            build.spec = entry.spec or build.spec
            build.author = entry.author or build.author or "Unknown"
            build.lastModified = entry.lastModified or build.lastModified
            build.isPublic = true
            build.validated = entry.validated == true or build.validated or false
            build._catalogBuild = true
            build._checksum = EbonholdHub.Build.Checksum(build)
            return build
        end
    end

    local locked = EbonholdHub.Build.EmptyLockedEchoes()
    if entry.lockedEchoes then
        for i = 1, EbonholdHub.Build.GetLockedSlotCount() do
            locked[i] = entry.lockedEchoes[i]
        end
    end

    local build = EbonholdHub.Build.NewObject({
        title             = entry.title,
        class             = entry.class,
        spec              = entry.spec or 1,
        comments          = entry.comments or "",
        lockedEchoes      = locked,
        echoTiers         = entry.echoTiers     or {},
        echoTierOrder     = entry.echoTierOrder or {},
        echoMaxPicks      = entry.echoMaxPicks  or {},
        settings          = MergeSettings(entry.settings),
        author            = entry.author or "Unknown",
        lastModified      = entry.lastModified or date("%Y-%m-%d %H:%M:%S"),
        automationEnabled = entry.automationEnabled ~= false,
        isPublic          = true,
        validated         = entry.validated == true,
    })
    -- Migrate old echoWeights if tiers not present
    if not next(build.echoTiers) and entry.echoWeights and next(entry.echoWeights) then
        build.echoWeights = entry.echoWeights
        if EbonholdHub.TierData then EbonholdHub.TierData.MigrateWeights(build) end
    end

    build.id = entry.id
    build._catalogBuild = true
    build._checksum = EbonholdHub.Build.Checksum(build)
    return build
end

local function InvalidateCache()
    cached = nil
end

local function EnsureCached()
    if cached then return cached end
    EbonholdHub.Catalog.EnsureStorage()
    cached = {}
    for i = 1, #EbonholdHubDB.catalog do
        cached[#cached + 1] = NormalizeEntry(EbonholdHubDB.catalog[i])
    end
    table.sort(cached, function(a, b)
        return (a.lastModified or "") > (b.lastModified or "")
    end)
    return cached
end

function EbonholdHub.Catalog.List()
    return EnsureCached()
end

function EbonholdHub.Catalog.Get(id)
    if not id then return nil end
    for _, build in ipairs(EnsureCached()) do
        if build.id == id then return build end
    end
    return nil
end

function EbonholdHub.Catalog.Refresh()
    InvalidateCache()
    return EnsureCached()
end

function EbonholdHub.Catalog.ShowWebsiteLink()
    EnsureLinkDialog()
    local url = EbonholdHub.Catalog.WEBSITE_URL or ""
    local opened = EbonholdHub.OpenExternalURL and EbonholdHub.OpenExternalURL(url)
    if linkDialog._hint then
        if opened then
            linkDialog._hint:SetText("Opened in your browser. Copy the URL below if needed:")
        else
            linkDialog._hint:SetText("Browse public builds on the website:")
        end
    end
    linkDialog._editBox:SetText(url)
    linkDialog._editBox:HighlightText()

    local main = EbonholdHub.MainWindow and EbonholdHub.MainWindow._frame
    if main and main:IsShown() then
        linkDialog:SetFrameStrata("FULLSCREEN_DIALOG")
        linkDialog:SetFrameLevel(main:GetFrameLevel() + 20)
        linkDialog:ClearAllPoints()
        linkDialog:SetPoint("CENTER", main, "CENTER", 0, 0)
    else
        linkDialog:ClearAllPoints()
        linkDialog:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
    end

    linkDialog:Show()
    linkDialog._editBox:SetFocus()
end

function EbonholdHub.Catalog.AddFromExport(export, opts)
    opts = opts or {}
    export = NormalizeExport(export)
    if export == "" then return nil end

    EbonholdHub.Catalog.EnsureStorage()

    local decoded = EbonholdHub.ExportImport and EbonholdHub.ExportImport.DecodeAnyExport(export)
    if not decoded then return nil end

    for _, entry in ipairs(EbonholdHubDB.catalog) do
        if NormalizeExport(entry.export) == export then
            entry.title = decoded.title or entry.title
            entry.class = decoded.class or entry.class
            entry.spec = decoded.spec or entry.spec
            entry.author = decoded.author or entry.author
            entry.lastModified = decoded.lastModified or entry.lastModified or date("%Y-%m-%d %H:%M:%S")
            if opts.source then entry.source = opts.source end
            InvalidateCache()
            if EbonholdHub.PublicBuildsView and EbonholdHub.PublicBuildsView.RefreshIfMounted then
                EbonholdHub.PublicBuildsView.RefreshIfMounted()
            end
            return entry.id
        end
    end

    local id = "cat-" .. EbonholdHub.Build.NewObjectId()
    EbonholdHubDB.catalog[#EbonholdHubDB.catalog + 1] = {
        id = id,
        title = decoded.title,
        class = decoded.class,
        spec = decoded.spec or 1,
        author = decoded.author or "Unknown",
        lastModified = decoded.lastModified or date("%Y-%m-%d %H:%M:%S"),
        export = export,
        source = opts.source or "import",
        addedAt = date("%Y-%m-%d %H:%M:%S"),
    }

    InvalidateCache()
    if EbonholdHub.PublicBuildsView and EbonholdHub.PublicBuildsView.RefreshIfMounted then
        EbonholdHub.PublicBuildsView.RefreshIfMounted()
    end
    return id
end

local function FindImportedCopy(catalogId)
    for _, b in pairs(EbonholdHubDB.builds) do
        if b.importedFrom == catalogId then
            return b
        end
    end
    return nil
end

function EbonholdHub.Catalog.Remove(id)
    if not id then return false end
    EbonholdHub.Catalog.EnsureStorage()

    local catalog = EbonholdHubDB.catalog
    for i = #catalog, 1, -1 do
        if catalog[i].id == id then
            table.remove(catalog, i)
            InvalidateCache()
            if EbonholdHub.PublicBuildsView and EbonholdHub.PublicBuildsView.RefreshIfMounted then
                EbonholdHub.PublicBuildsView.RefreshIfMounted()
            end
            return true
        end
    end
    return false
end

StaticPopupDialogs = StaticPopupDialogs or {}
StaticPopupDialogs["EBONHOLDHUB_DELETE_CATALOG"] = {
    text = "Remove \"%s\" from your catalog?\n\nCopies in My builds are not deleted.",
    button1 = "Delete",
    button2 = "Cancel",
    OnAccept = function()
        local payload = StaticPopup1 and StaticPopup1.data
        if payload and payload.id and EbonholdHub.Catalog.Remove(payload.id) then
            if payload.callback then payload.callback() end
        end
    end,
    timeout = 0,
    whileDead = true,
    hideOnEscape = true,
}

function EbonholdHub.Catalog.ConfirmRemove(build, onRemoved)
    if not build or not build.id then return end
    StaticPopup_Show(
        "EBONHOLDHUB_DELETE_CATALOG",
        build.title or "Untitled",
        nil,
        { id = build.id, callback = onRemoved }
    )
end

function EbonholdHub.Catalog.FindImportedCopy(catalogId)
    return FindImportedCopy(catalogId)
end

function EbonholdHub.Catalog.ImportBuild(build)
    if not build then return nil end

    -- Discard stale edit-session drafts so they cannot overwrite imported data.
    EbonholdHubDB.pendingAffixLoadout = nil
    EbonholdHub.EquipmentLoadout.ClearPending()
    if EbonholdHub.TierData then EbonholdHub.TierData.ClearPending() end

    local locked = EbonholdHub.Build.EmptyLockedEchoes()
    for i = 1, EbonholdHub.Build.GetLockedSlotCount() do
        locked[i] = build.lockedEchoes and build.lockedEchoes[i] or nil
    end

    local newBuild = EbonholdHub.Build.Create({
        title             = (build.title or "Imported") .. " (imported)",
        class             = build.class,
        spec              = build.spec or 1,
        comments          = build.comments or "",
        lockedEchoes      = locked,
        echoTiers         = build.echoTiers     or {},
        echoTierOrder     = build.echoTierOrder or {},
        echoMaxPicks      = build.echoMaxPicks  or {},
        echoBundles       = build.echoBundles   or {},
        loadoutEchoSpellIds = build.loadoutEchoSpellIds,
        affixLoadout      = build.affixLoadout,
        equipmentLoadout  = build.equipmentLoadout,
        equipmentSets     = build.equipmentSets,
        equipmentSetIndex = build.equipmentSetIndex,
        talents           = build.talents,
        talentOrder       = build.talentOrder,
        settings          = EbonholdHub.Build.CloneSettings(build.settings or EbonholdHub.Build.DefaultSettings()),
        automationEnabled = build.automationEnabled,
        copiedFrom        = build.copiedFrom,
        isPublic          = false,
    })
    newBuild.importedFrom = build.id
    newBuild._importedAt = build.lastModified
    -- Migrate old echoWeights if tiers not present
    if not next(newBuild.echoTiers) and build.echoWeights and next(build.echoWeights) then
        newBuild.echoWeights = build.echoWeights
        if EbonholdHub.TierData then EbonholdHub.TierData.MigrateWeights(newBuild) end
    end
    newBuild._checksum = EbonholdHub.Build.Checksum(newBuild)
    EbonholdHub.Build.EnsureSettings(newBuild)
    EbonholdHub.Build.SetActive(newBuild.id)
    if EbonholdHub.BuildList and EbonholdHub.BuildList.Refresh then
        EbonholdHub.BuildList.Refresh()
    end
    return newBuild
end

function EbonholdHub.Catalog.UpdateImported(localBuild, catalogBuild)
    EbonholdHubDB.pendingAffixLoadout = nil
    EbonholdHub.EquipmentLoadout.ClearPending()
    if EbonholdHub.TierData then EbonholdHub.TierData.ClearPending() end

    EbonholdHub.Build.UpdateFromPublic(localBuild, catalogBuild)
    EbonholdHub.Build.SetActive(localBuild.id)
    if EbonholdHub.BuildList and EbonholdHub.BuildList.Refresh then
        EbonholdHub.BuildList.Refresh()
    end
    return localBuild
end

function EbonholdHub.Catalog.Init()
    EbonholdHub.Catalog.EnsureStorage()
    EnsureCached()
end
