--[[
    Commander 3.0 - Watch Commands

    Commands for creating and managing live-updating queries.
]]

local ADDON_NAME, C3 = ...

-------------------------------------------------------------------------------
-- Blocked Commands (secure actions that cannot run in watch timers)
-------------------------------------------------------------------------------

-- These commands require hardware events / secure binding patterns.
-- Running them in a watch timer will cause taint or silent failures.
local BLOCKED_COMMANDS = {
    cast = true,
    use = true,
    mount = true,
    dismount = true,
    toy = true,
    settarget = true,
    targetunit = true,
    logout = true,
    camp = true,
    quit = true,
    exit = true,
}

---Check if a query starts with a blocked command
---@param query string
---@return string|nil blockedCmd The blocked command name if found
local function getBlockedCommand(query)
    -- Extract first word (command name)
    local cmd = query:match("^%s*(%S+)")
    if cmd then
        cmd = cmd:lower()
        if BLOCKED_COMMANDS[cmd] then
            return cmd
        end
    end
    return nil
end

-------------------------------------------------------------------------------
-- watch Command
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "watch",
    description = "Live-update a query (re-executes periodically)",
    category = "system",
    acceptsInput = false,
    args = {
        { name = "query", required = true, vararg = true, description = "Query to watch" },
    },
    flags = {
        interval = { short = "i", hasValue = true, description = "Update interval in seconds (default: 1)" },
    },
    execute = function(ctx)
        local query

        -- Try to extract query from full input (handles pipes correctly)
        local fullInput = ctx.input
        if fullInput then
            -- Remove "watch " prefix and extract the query part
            -- Handle: "watch bags | where quality > 1" -> "bags | where quality > 1"
            query = fullInput:match("^watch%s+(.+)$")

            -- Strip any trailing --interval flag from the query
            if query then
                query = query:gsub("%s+%-%-?i%s*[%d.]*%s*$", "")
                query = query:gsub("%s+%-%-?interval%s*[%d.]*%s*$", "")
            end
        end

        -- Fallback: use raw args if input parsing failed
        if not query or query == "" then
            local rawArgs = ctx:GetRawArgs()
            if rawArgs and #rawArgs > 0 then
                query = table.concat(rawArgs, " ")
            end
        end

        -- Strip surrounding quotes if present (for quoted pipeline queries)
        if query then
            query = query:match('^"(.-)"$') or query:match("^'(.-)'$") or query
            -- Trim whitespace
            query = query:match("^%s*(.-)%s*$")
        end

        if not query or query == "" then
            return ctx:Error("Usage: watch <query> [--interval N]\nFor piped queries: watch \"bags | where quality > 1\"")
        end

        -- Block secure action commands - they require hardware events
        local blockedCmd = getBlockedCommand(query)
        if blockedCmd then
            return ctx:Error(string.format(
                "Cannot watch '%s': secure action commands require hardware events.\n" ..
                "The '%s' command cannot be run from a timer.",
                blockedCmd, blockedCmd
            ))
        end

        -- If query is just a variable (like $health), wrap it with echo
        if query:match("^%$[%w_]+$") then
            query = "echo " .. query
        end

        -- Get interval from flag
        local interval = tonumber(ctx:GetFlag("interval")) or C3.WatchManager.DEFAULT_INTERVAL

        -- Create the watch
        local watchId, line = C3.WatchManager:Create(query, {
            interval = interval,
            session = ctx.session,
        })

        -- Add the live line to the session's active buffer (if available)
        -- No fallback to C3.Terminal - prevents cross-window bleeding when called via C3:Query()
        local buffer = ctx.session and ctx.session._activeBuffer
        if buffer then
            buffer:AddLine(line)
        end

        -- Return status message
        return C3.Types.Text(string.format("Watch #%d started (%.1fs interval): %s",
            watchId, interval, query))
    end
})

-------------------------------------------------------------------------------
-- unwatch Command
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "unwatch",
    description = "Stop a watch",
    category = "system",
    acceptsInput = false,
    args = {
        { name = "id", required = false, description = "Watch ID or 'all'" },
    },
    execute = function(ctx)
        local id = ctx:GetArg(1)

        -- No argument - show help
        if not id then
            local count = C3.WatchManager:Count()
            if count == 0 then
                return ctx:Text("No active watches")
            end
            return ctx:Error("Usage: unwatch <id|all>")
        end

        -- Stop all watches
        if id == "all" then
            local count = C3.WatchManager:StopAll()
            if count == 0 then
                return ctx:Text("No watches to stop")
            end
            return ctx:Text(string.format("Stopped %d watch%s", count, count == 1 and "" or "es"))
        end

        -- Stop specific watch
        local watchId = tonumber(id)
        if not watchId then
            return ctx:Error("Invalid watch ID: " .. tostring(id))
        end

        if C3.WatchManager:Stop(watchId) then
            return ctx:Text("Stopped watch #" .. watchId)
        else
            return ctx:Error("Watch #" .. watchId .. " not found")
        end
    end
})

-------------------------------------------------------------------------------
-- watches Command
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "watches",
    description = "List active watches",
    category = "system",
    acceptsInput = false,
    execute = function(ctx)
        local watches = C3.WatchManager:List()

        if #watches == 0 then
            return ctx:Text("No active watches")
        end

        return ctx:Produce(function(emit)
            for _, watch in ipairs(watches) do
                emit(C3.Types.Create("Watch", {
                    id = watch.id,
                    query = watch.query,
                    interval = watch.interval,
                    age = GetTime() - watch.created,
                }))
            end
        end)
    end
})
