-- SETTINGS: Customize your engines and display names here
local engines = {
    -- primary   = { name = "Google",    url = "https://google.ca/search?q=" },
    primary   = { name = "Google/StartPage",    url = "https://www.startpage.com/do/search?query=" },
    secondary = { name = "Wikipedia",           url = "https://wikipedia.org/wiki/Special:Search?search="},
    tertiary  = { name = "YouTube",             url = "https://youtube.com/results?search_query=" },
    chatgpt   = { name = "ChatGPT",             url = "https://chatgpt.com/?q=" },
    maps      = { name = "Maps",                url = "https://www.google.com/maps/search/" },
    spotify   = { name = "Spotify",             url = "https://open.spotify.com/search/" }
}

local closeTimeout = 1.0
local gracePeriod  = 3.0

-- Global variables
searchView = nil
searchTimer = nil
escWatcher = nil
clickOutsideWatcher = nil

local timeSinceLeft = 0
local hasEnteredWindow = false
local autoKillTime = 0

------------------------------------------------------------
-- CLOSE SEARCH
------------------------------------------------------------
local function closeSearch()
    if searchView then searchView:delete() searchView = nil end
    if searchTimer then searchTimer:stop() searchTimer = nil end
    if escWatcher then escWatcher:stop() escWatcher = nil end
    if clickOutsideWatcher then clickOutsideWatcher:stop() clickOutsideWatcher = nil end

    timeSinceLeft = 0
    hasEnteredWindow = false
    autoKillTime = 0
end

------------------------------------------------------------
-- PERFORM SEARCH
------------------------------------------------------------
local function performSearch(engine)
    hs.alert.show("Searching " .. engine.name .. "...", 1)

    hs.eventtap.keyStroke({"cmd"}, "c")
    
    hs.timer.doAfter(0.2, function()

        local query = hs.pasteboard.getContents()
        if not query or query == "" then return end

        closeSearch()
        
        -- RESET TRACKERS FOR NEW SEARCH
        timeSinceLeft = 0
        hasEnteredWindow = false
        autoKillTime = 0
        
        local url = engine.url .. hs.http.encodeForQuery(query)

        local screen = hs.mouse.getCurrentScreen()
        local screenFrame = screen:frame()
        
        local width, height = 500, 600
        local x = screenFrame.x + (screenFrame.w - width) / 2
        local y = screenFrame.y + (screenFrame.h - height) / 2

        searchView = hs.webview.new({
            x = x,
            y = y,
            w = width,
            h = height
        })

        searchView:windowStyle({"titled", "utility", "closable", "resizable"})
        searchView:level(hs.drawing.windowLevels.floating)

        searchView:userAgent(
            "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko)"
        )
        
        searchView:url(url)
        searchView:show()
        searchView:bringToFront()

        --------------------------------------------------------
        -- ESC CLOSE
        --------------------------------------------------------
        escWatcher = hs.eventtap.new(
            {hs.eventtap.event.types.keyDown},
            function(event)

                if event:getKeyCode() == 53 then
                    closeSearch()
                    return true
                end

                return false
            end
        ):start()

        --------------------------------------------------------
        -- CLICK OUTSIDE CLOSE
        --------------------------------------------------------
        clickOutsideWatcher = hs.eventtap.new(
            {hs.eventtap.event.types.leftMouseDown},
            function()

                if not searchView then return false end

                local mousePos = hs.mouse.absolutePosition()
                local winFrame = searchView:frame()

                local isOutside =
                    mousePos.x < winFrame.x or
                    mousePos.x > (winFrame.x + winFrame.w) or
                    mousePos.y < winFrame.y or
                    mousePos.y > (winFrame.y + winFrame.h)

                if isOutside then closeSearch() end

                return false
            end
        ):start()

        --------------------------------------------------------
        -- AUTO CLOSE TIMER
        --------------------------------------------------------
        searchTimer = hs.timer.doEvery(0.1, function()

            if not searchView then return end

            local mousePos = hs.mouse.absolutePosition()
            local winFrame = searchView:frame()

            local isOutside =
                mousePos.x < winFrame.x or
                mousePos.x > (winFrame.x + winFrame.w) or
                mousePos.y < winFrame.y or
                mousePos.y > (winFrame.y + winFrame.h)

            if not isOutside then
                hasEnteredWindow = true
                timeSinceLeft = 0
            else
                if hasEnteredWindow then

                    timeSinceLeft = timeSinceLeft + 0.1

                    if timeSinceLeft >= closeTimeout then
                        closeSearch()
                    end

                else

                    autoKillTime = autoKillTime + 0.1

                    if autoKillTime >= gracePeriod then
                        closeSearch()
                    end

                end
            end

        end)

    end)
end

------------------------------------------------------------
-- RIGHT CLICK TRIGGERS
------------------------------------------------------------
if clickWatcher then clickWatcher:stop() end

clickWatcher = hs.eventtap.new(
    {hs.eventtap.event.types.rightMouseDown},
    function(event)

        local flags = event:getFlags()

        if flags.cmd and flags.shift then
            performSearch(engines.maps)
            return true

        elseif flags.alt and flags.shift then
            performSearch(engines.spotify)
            return true

        elseif flags.cmd then
            performSearch(engines.primary)
            return true 

        elseif flags.alt then
            performSearch(engines.secondary)
            return true

        elseif flags.ctrl then
            performSearch(engines.tertiary)
            return true

        elseif flags.shift then
            performSearch(engines.chatgpt)
            return true
        end

        return false
    end
):start()

------------------------------------------------------------
-- CMD + ENTER → OPEN CURRENT PAGE
------------------------------------------------------------
hs.hotkey.bind({"cmd"}, "return", function()

    if searchView then

        local currentPage = searchView:url()

        if currentPage then
            hs.urlevent.openURL(currentPage)
        else
            hs.alert.show("No page loaded")
        end

    else
        hs.alert.show("No search window open")
    end

end)