-- Look Slots hotkeys for Adobe Lightroom Classic.
--
-- Lightroom's own key handling swallows keystrokes before macOS menu
-- shortcuts run (and it ignores NSUserKeyEquivalents), so these hotkeys are
-- intercepted system-wide and press the plugin's menu items via the raw
-- Accessibility API. (Lightroom indents plugin menu items with leading
-- spaces and its menu elements reject hs.application:selectMenuItem, hence
-- the manual AX walk.) Hotkeys are enabled ONLY while Lightroom is the
-- frontmost app; everywhere else the keys behave normally.
--
--   Ctrl+Opt+1..9       Copy Develop Settings to Slot 1..9
--   Ctrl+Opt+Cmd+1..9   Paste Develop Settings from Slot 1..9
--
-- AI-mask assist: when the plugin pastes AI masks (Select Subject/Sky/...)
-- it writes a marker file under ~/.develop-clipboard/. We watch for it and
-- press Settings > Update AI Settings so the masks re-segment on the target
-- photos - the Lightroom SDK has no API for that command.
--
-- NOTE: hotkeys, watchers, and timers must live in GLOBALS. Hammerspoon
-- garbage-collects locals after init.lua finishes, which silently kills
-- them (e.g. hotkeys stop re-enabling when Lightroom activates).

require("hs.ipc") -- enables the `hs` command-line tool for debugging

local ax = require("hs.axuielement")

local LR_BUNDLE = "com.adobe.LightroomClassicCC7"

-- Walks the menu bar along `path` (e.g. {"File", "Plug-in Extras", "Copy
-- Develop Settings to Slot 1"}) comparing titles with leading whitespace
-- stripped, and AXPresses the final item. Returns true on success.
-- Global on purpose so it can be tested from the CLI:
--   hs -c 'return pressLightroomMenuPath({"Settings", "Update AI Settings"})'
function pressLightroomMenuPath(path)
    local app = hs.application.get(LR_BUNDLE)
    if not app then return false end
    local axApp = ax.applicationElement(app)
    local el = axApp and axApp:attributeValue("AXMenuBar")
    if not el then return false end
    for i, want in ipairs(path) do
        local children = el:attributeValue("AXChildren") or {}
        -- menu-bar items and submenu items wrap their entries in one AXMenu
        if #children == 1 and children[1].attributeValue
                and children[1]:attributeValue("AXRole") == "AXMenu" then
            children = children[1]:attributeValue("AXChildren") or {}
        end
        local nextEl = nil
        for _, c in ipairs(children) do
            local t = (c:attributeValue("AXTitle") or ""):gsub("^%s+", "")
            if t == want then
                nextEl = c
                break
            end
        end
        if not nextEl then return false end
        if i == #path then
            if nextEl:attributeValue("AXEnabled") == false then return false end
            return nextEl:performAction("AXPress") ~= nil
        end
        el = nextEl
    end
    return false
end

-- ---------------------------------------------------------------------------
-- Slot hotkeys
-- ---------------------------------------------------------------------------

lrHotkeys = {}

local function bind(mods, key, menuTitle)
    lrHotkeys[#lrHotkeys + 1] = hs.hotkey.new(mods, key, function()
        if not pressLightroomMenuPath({ "File", "Plug-in Extras", menuTitle }) then
            hs.alert.show("Menu item not found: " .. menuTitle)
        end
    end)
end

for slot = 1, 9 do
    bind({ "ctrl", "alt" }, tostring(slot),
        "Copy Develop Settings to Slot " .. slot)
    bind({ "ctrl", "alt", "cmd" }, tostring(slot),
        "Paste Develop Settings from Slot " .. slot)
end

local function setEnabled(on)
    for _, hk in ipairs(lrHotkeys) do
        if on then hk:enable() else hk:disable() end
    end
end

lrWatcher = hs.application.watcher.new(function(_, event, app)
    if app and app:bundleID() == LR_BUNDLE then
        if event == hs.application.watcher.activated then
            setEnabled(true)
        elseif event == hs.application.watcher.deactivated
                or event == hs.application.watcher.terminated then
            setEnabled(false)
        end
    end
end)
lrWatcher:start()

-- Cover the case where Lightroom is already frontmost when this config loads.
local front = hs.application.frontmostApplication()
setEnabled(front ~= nil and front:bundleID() == LR_BUNDLE)

-- ---------------------------------------------------------------------------
-- AI-mask update assist
-- ---------------------------------------------------------------------------

local AI_MARKER_DIR = os.getenv("HOME") .. "/.develop-clipboard"
hs.fs.mkdir(AI_MARKER_DIR)

local lastAiFire = 0

lrAiPathwatcher = hs.pathwatcher.new(AI_MARKER_DIR, function(files)
    for _, f in ipairs(files) do
        if f:find("ai-update-request", 1, true) then
            local now = hs.timer.secondsSinceEpoch()
            if now - lastAiFire < 2 then return end -- debounce event bursts
            lastAiFire = now
            -- small delay so Lightroom finishes committing the paste
            lrAiTimer = hs.timer.doAfter(0.7, function()
                -- Develop module has Settings > Update AI Settings;
                -- Library nests it under Photo > Develop Settings.
                local ok = pressLightroomMenuPath({ "Settings", "Update AI Settings" })
                    or pressLightroomMenuPath({ "Photo", "Develop Settings", "Update AI Settings" })
                if not ok then
                    hs.alert.show("Update AI Settings: menu item not reachable")
                end
            end)
            return
        end
    end
end)
lrAiPathwatcher:start()

hs.alert.show("Lightroom slot hotkeys loaded")
