import { fileURLToPath as __fileURLToPath } from 'url';
import { dirname as __pathDirname } from 'path';
const __filename = __fileURLToPath(import.meta.url);
const __dirname = __pathDirname(__filename);
import { existsSync, readFileSync } from "fs";
import { join } from "path";
import { homedir } from "os";
import { execFileSync } from "child_process";
import { randomBytes } from "crypto";
import { writeFileSecure, ensureSecureDir } from "./secure-fs.js";
import { ActionQueueSchema, parseConfigSafe } from "./schemas.js";
import { LinearClient } from "../integrations/linear/client.js";
import { LinearAuthManager } from "../integrations/linear/auth.js";
const SAFE_ACTION_PATTERNS = [
  // Git/GitHub CLI commands (limited to safe operations)
  { pattern: /^gh pr (view|list|status|checks) (\d+)$/ },
  { pattern: /^gh pr review (\d+) --approve$/ },
  { pattern: /^gh pr merge (\d+) --squash$/ },
  { pattern: /^gh issue (view|list) (\d+)?$/ },
  // NPM commands (limited to safe operations)
  { pattern: /^npm run (build|test|lint|lint:fix|test:run)$/ },
  { pattern: /^npm (test|run build)$/ },
  // StackMemory commands
  { pattern: /^stackmemory (status|notify check|context list)$/ },
  // Task start with optional --assign-me flag (Linear task ID is UUID format)
  {
    pattern: /^stackmemory task start ([a-f0-9-]{36})( --assign-me)?$/
  },
  // Git commands
  { pattern: /^git (status|diff|log|branch)( --[a-z-]+)*$/ },
  { pattern: /^git add -A && git commit$/ },
  { pattern: /^gh pr create --fill$/ },
  // Custom aliases (cwm = claude worktree merge)
  { pattern: /^cwm$/ },
  // Simple echo/confirmation (no variables)
  {
    pattern: /^echo "?(Done|OK|Confirmed|Acknowledged|Great work! Time for a coffee break\.)"?$/
  }
];
function isActionAllowed(action) {
  const trimmed = action.trim();
  return SAFE_ACTION_PATTERNS.some(({ pattern, validate }) => {
    const match = trimmed.match(pattern);
    if (!match) return false;
    if (validate && !validate(match)) return false;
    return true;
  });
}
const QUEUE_PATH = join(homedir(), ".stackmemory", "sms-action-queue.json");
const DEFAULT_QUEUE = {
  actions: [],
  lastChecked: (/* @__PURE__ */ new Date()).toISOString()
};
function loadActionQueue() {
  try {
    if (existsSync(QUEUE_PATH)) {
      const data = JSON.parse(readFileSync(QUEUE_PATH, "utf8"));
      return parseConfigSafe(
        ActionQueueSchema,
        data,
        DEFAULT_QUEUE,
        "action-queue"
      );
    }
  } catch {
  }
  return { ...DEFAULT_QUEUE, lastChecked: (/* @__PURE__ */ new Date()).toISOString() };
}
function saveActionQueue(queue) {
  try {
    ensureSecureDir(join(homedir(), ".stackmemory"));
    writeFileSecure(QUEUE_PATH, JSON.stringify(queue, null, 2));
  } catch {
  }
}
function queueAction(promptId, response, action) {
  const queue = loadActionQueue();
  const id = randomBytes(8).toString("hex");
  queue.actions.push({
    id,
    promptId,
    response,
    action,
    timestamp: (/* @__PURE__ */ new Date()).toISOString(),
    status: "pending"
  });
  saveActionQueue(queue);
  return id;
}
function getLinearClient() {
  const apiKey = process.env["LINEAR_API_KEY"];
  if (apiKey) {
    return new LinearClient({ apiKey });
  }
  try {
    const authManager = new LinearAuthManager();
    const tokens = authManager.loadTokens();
    if (tokens?.accessToken) {
      return new LinearClient({ accessToken: tokens.accessToken });
    }
  } catch {
  }
  return null;
}
async function handleSpecialAction(action) {
  const taskStartMatch = action.match(
    /^stackmemory task start ([a-f0-9-]{36})( --assign-me)?$/
  );
  if (taskStartMatch) {
    const issueId = taskStartMatch[1];
    const client = getLinearClient();
    if (!client) {
      return {
        handled: true,
        success: false,
        error: "Linear not configured. Set LINEAR_API_KEY or run stackmemory linear setup."
      };
    }
    try {
      const result = await client.startIssue(issueId);
      if (result.success && result.issue) {
        return {
          handled: true,
          success: true,
          output: `Started: ${result.issue.identifier} - ${result.issue.title}`
        };
      }
      return {
        handled: true,
        success: false,
        error: result.error || "Failed to start issue"
      };
    } catch (err) {
      return {
        handled: true,
        success: false,
        error: err instanceof Error ? err.message : "Unknown error"
      };
    }
  }
  return { handled: false };
}
async function executeActionSafe(action, _response) {
  if (!isActionAllowed(action)) {
    console.error(`[sms-action] Action not in allowlist: ${action}`);
    return {
      success: false,
      error: `Action not allowed. Only pre-approved commands can be executed via SMS.`
    };
  }
  const specialResult = await handleSpecialAction(action);
  if (specialResult.handled) {
    return {
      success: specialResult.success || false,
      output: specialResult.output,
      error: specialResult.error
    };
  }
  try {
    console.log(`[sms-action] Executing safe action: ${action}`);
    const parts = action.split(" ");
    const cmd = parts[0];
    const args = parts.slice(1);
    const output = execFileSync(cmd, args, {
      encoding: "utf8",
      timeout: 6e4,
      stdio: ["pipe", "pipe", "pipe"],
      shell: false
      // Explicitly disable shell
    });
    return { success: true, output };
  } catch (err) {
    const error = err instanceof Error ? err.message : String(err);
    return { success: false, error };
  }
}
function getPendingActions() {
  const queue = loadActionQueue();
  return queue.actions.filter((a) => a.status === "pending");
}
function markActionRunning(id) {
  const queue = loadActionQueue();
  const action = queue.actions.find((a) => a.id === id);
  if (action) {
    action.status = "running";
    saveActionQueue(queue);
  }
}
function markActionCompleted(id, result, error) {
  const queue = loadActionQueue();
  const action = queue.actions.find((a) => a.id === id);
  if (action) {
    action.status = error ? "failed" : "completed";
    action.result = result;
    action.error = error;
    saveActionQueue(queue);
  }
}
async function executeAction(action) {
  markActionRunning(action.id);
  const result = await executeActionSafe(action.action, action.response);
  if (result.success) {
    markActionCompleted(action.id, result.output);
  } else {
    markActionCompleted(action.id, void 0, result.error);
  }
  return result;
}
async function processAllPendingActions() {
  const pending = getPendingActions();
  let succeeded = 0;
  let failed = 0;
  for (const action of pending) {
    const result = await executeAction(action);
    if (result.success) {
      succeeded++;
    } else {
      failed++;
    }
  }
  return { processed: pending.length, succeeded, failed };
}
function cleanupOldActions() {
  const queue = loadActionQueue();
  const completed = queue.actions.filter(
    (a) => a.status === "completed" || a.status === "failed"
  );
  if (completed.length > 50) {
    const toRemove = completed.slice(0, completed.length - 50);
    queue.actions = queue.actions.filter(
      (a) => !toRemove.find((r) => r.id === a.id)
    );
    saveActionQueue(queue);
    return toRemove.length;
  }
  return 0;
}
const ACTION_TEMPLATES = {
  // Git/PR actions (PR numbers must be validated as integers)
  approvePR: (prNumber) => {
    if (!/^\d+$/.test(prNumber)) {
      throw new Error("Invalid PR number");
    }
    return `gh pr review ${prNumber} --approve`;
  },
  mergePR: (prNumber) => {
    if (!/^\d+$/.test(prNumber)) {
      throw new Error("Invalid PR number");
    }
    return `gh pr merge ${prNumber} --squash`;
  },
  viewPR: (prNumber) => {
    if (!/^\d+$/.test(prNumber)) {
      throw new Error("Invalid PR number");
    }
    return `gh pr view ${prNumber}`;
  },
  // Build actions (no user input)
  rebuild: () => `npm run build`,
  retest: () => `npm run test:run`,
  lint: () => `npm run lint:fix`,
  // Status actions (no user input)
  status: () => `stackmemory status`,
  checkNotifications: () => `stackmemory notify check`
  // REMOVED for security - these templates allowed arbitrary user input:
  // - requestChanges (allowed arbitrary message)
  // - closePR (could be used maliciously)
  // - deploy/rollback (too dangerous for SMS)
  // - verifyDeployment (allowed arbitrary URL)
  // - notifySlack (allowed arbitrary message - command injection)
  // - notifyTeam (allowed arbitrary message - command injection)
};
function createAction(template, ...args) {
  const fn = ACTION_TEMPLATES[template];
  if (typeof fn === "function") {
    return fn(...args);
  }
  return fn;
}
function startActionWatcher(intervalMs = 5e3) {
  console.log(
    `[sms-action] Starting action watcher (interval: ${intervalMs}ms)`
  );
  return setInterval(() => {
    const pending = getPendingActions();
    if (pending.length > 0) {
      console.log(`[sms-action] Found ${pending.length} pending action(s)`);
      processAllPendingActions();
    }
  }, intervalMs);
}
function handleSMSResponse(promptId, response, action) {
  if (action) {
    const actionId = queueAction(promptId, response, action);
    console.log(`[sms-action] Queued action ${actionId}: ${action}`);
  }
}
export {
  ACTION_TEMPLATES,
  cleanupOldActions,
  createAction,
  executeAction,
  executeActionSafe,
  getPendingActions,
  handleSMSResponse,
  loadActionQueue,
  markActionCompleted,
  markActionRunning,
  processAllPendingActions,
  queueAction,
  saveActionQueue,
  startActionWatcher
};
//# sourceMappingURL=sms-action-runner.js.map
