import { AiPendingActionRepository } from "../data/repositories/AiPendingActionRepository.js";
import { emitAiAssistantEvent } from "../events.js";
const CONFIRMED_EVENT_ID = "ai.action.confirmed";
const defaultConfirmedEmitter = async (eventId, payload) => {
  await emitAiAssistantEvent(eventId, payload, {
    persistent: true
  });
};
async function emitConfirmed(emitter, payload) {
  try {
    await emitter(CONFIRMED_EVENT_ID, payload);
  } catch (error) {
    console.warn(`[AI Pending Action] Failed to emit ${CONFIRMED_EVENT_ID}:`, error);
  }
}
function normalizeExecutionResult(raw) {
  if (!raw || typeof raw !== "object") return {};
  const source = raw;
  const result = {};
  if (typeof source.recordId === "string") result.recordId = source.recordId;
  if (typeof source.commandName === "string") result.commandName = source.commandName;
  return result;
}
function buildHandlerErrorFromThrown(error, input) {
  const message = error instanceof Error ? error.message : String(error);
  const name = error instanceof Error ? error.name : void 0;
  const out = {
    code: "handler_error",
    message: message || "Tool handler threw an error."
  };
  if (name) out.name = name;
  if (input !== void 0) {
    try {
      out.input = JSON.parse(JSON.stringify(input));
    } catch {
    }
  }
  const details = {};
  if (error && typeof error === "object") {
    const err = error;
    const issues = err.issues;
    if (Array.isArray(issues) && issues.length > 0) {
      details.issues = issues.map((issue) => {
        if (!issue || typeof issue !== "object") return issue;
        const obj = issue;
        return {
          path: Array.isArray(obj.path) ? obj.path : void 0,
          message: typeof obj.message === "string" ? obj.message : void 0,
          code: typeof obj.code === "string" ? obj.code : void 0,
          ...typeof obj.expected === "string" ? { expected: obj.expected } : {},
          ...typeof obj.received === "string" ? { received: obj.received } : {}
        };
      });
      const fieldErrors = {};
      for (const issue of issues) {
        const path = Array.isArray(issue.path) ? issue.path.join(".") : "";
        const msg = typeof issue.message === "string" ? issue.message : null;
        if (!path || !msg) continue;
        if (!fieldErrors[path]) fieldErrors[path] = [];
        fieldErrors[path].push(msg);
      }
      if (Object.keys(fieldErrors).length > 0) {
        details.fieldErrors = fieldErrors;
      }
      if (out.code === "handler_error") out.code = "validation_error";
    }
    if (typeof err.code === "string" && err.code.length > 0) {
      out.code = err.code;
    }
    if (err.cause !== void 0) {
      try {
        details.cause = JSON.parse(JSON.stringify(err.cause));
      } catch {
        if (err.cause instanceof Error) {
          details.cause = { message: err.cause.message, name: err.cause.name };
        }
      }
    }
    for (const key of Object.keys(err)) {
      if (key === "issues" || key === "cause" || key === "code" || key === "message" || key === "name" || key === "stack") continue;
      const value = err[key];
      if (value === void 0) continue;
      try {
        details[key] = JSON.parse(JSON.stringify(value));
      } catch {
      }
    }
  }
  if (Object.keys(details).length > 0) {
    out.details = details;
  }
  if (process.env.OM_AI_INCLUDE_HANDLER_STACK === "1" && error instanceof Error && error.stack) {
    const lines = error.stack.split("\n").slice(0, 6);
    out.stack = lines.join("\n");
  }
  return out;
}
function extractHandlerFailedRecords(raw) {
  if (!raw || typeof raw !== "object") return [];
  const source = raw;
  const records = source.records;
  if (!Array.isArray(records) || records.length === 0) return [];
  const out = [];
  for (const entry of records) {
    if (!entry || typeof entry !== "object") continue;
    const record = entry;
    if (typeof record.recordId !== "string") continue;
    const status = typeof record.status === "string" ? record.status : null;
    if (status === "updated") continue;
    const errorField = record.error;
    if (!errorField || typeof errorField !== "object") continue;
    const error = errorField;
    const code = typeof error.code === "string" ? error.code : "handler_error";
    const message = typeof error.message === "string" ? error.message : "Record update failed.";
    out.push({
      recordId: record.recordId,
      error: { code, message }
    });
  }
  return out;
}
function mergeFailedRecords(recheck, handler) {
  const seen = /* @__PURE__ */ new Map();
  for (const entry of recheck ?? []) {
    if (entry && typeof entry.recordId === "string" && !seen.has(entry.recordId)) {
      seen.set(entry.recordId, entry);
    }
  }
  for (const entry of handler ?? []) {
    if (entry && typeof entry.recordId === "string" && !seen.has(entry.recordId)) {
      seen.set(entry.recordId, entry);
    }
  }
  if (seen.size === 0) return null;
  return Array.from(seen.values());
}
function toToolHandlerContext(ctx, tool) {
  return {
    tenantId: ctx.tenantId,
    organizationId: ctx.organizationId,
    userId: ctx.userId,
    container: ctx.container,
    userFeatures: ctx.userFeatures,
    isSuperAdmin: ctx.isSuperAdmin,
    tool
  };
}
async function executePendingActionConfirm(input) {
  const { action, agent, tool, ctx, failedRecords, now } = input;
  const repo = input.repo ?? new AiPendingActionRepository(ctx.container.resolve("em"));
  const scope = {
    tenantId: ctx.tenantId,
    organizationId: ctx.organizationId,
    userId: ctx.userId
  };
  const clock = now ?? /* @__PURE__ */ new Date();
  const emitter = input.emitEvent ?? defaultConfirmedEmitter;
  if (action.status === "confirmed") {
    const prior = action.executionResult ?? {};
    return { ok: true, action, executionResult: prior };
  }
  if (action.status === "executing") {
    const prior = action.executionResult ?? {};
    return { ok: true, action, executionResult: prior };
  }
  if (action.status !== "pending") {
    return {
      ok: false,
      action,
      executionResult: {
        error: { code: "invalid_status", message: `Action is in status "${action.status}".` }
      },
      cause: new Error(`Action is in status "${action.status}"`)
    };
  }
  const partialFailedRecords = Array.isArray(failedRecords) && failedRecords.length > 0 ? failedRecords : null;
  const confirmedRow = await repo.setStatus(action.id, "confirmed", scope, {
    resolvedByUserId: ctx.userId,
    now: clock,
    ...partialFailedRecords ? { failedRecords: partialFailedRecords } : {}
  });
  const executingRow = await repo.setStatus(confirmedRow.id, "executing", scope, { now: clock });
  let handlerOutput;
  try {
    handlerOutput = await tool.handler(action.normalizedInput, toToolHandlerContext(ctx, tool));
  } catch (error) {
    const failureResult = {
      error: buildHandlerErrorFromThrown(error, action.normalizedInput)
    };
    const failedRow = await repo.setStatus(executingRow.id, "failed", scope, {
      executionResult: failureResult,
      now: clock
    });
    await emitConfirmed(emitter, {
      pendingActionId: failedRow.id,
      agentId: agent.id,
      toolName: tool.name,
      status: failedRow.status,
      tenantId: ctx.tenantId,
      organizationId: ctx.organizationId ?? null,
      userId: ctx.userId,
      resolvedByUserId: ctx.userId,
      resolvedAt: (failedRow.resolvedAt ?? clock).toISOString?.() ?? new Date(clock).toISOString(),
      executionResult: failureResult
    });
    return {
      ok: false,
      action: failedRow,
      executionResult: failureResult,
      cause: error
    };
  }
  const successResult = normalizeExecutionResult(handlerOutput);
  const handlerFailedRecords = extractHandlerFailedRecords(handlerOutput);
  const mergedFailedRecords = mergeFailedRecords(partialFailedRecords, handlerFailedRecords);
  const confirmedExtra = {
    executionResult: successResult,
    now: clock
  };
  confirmedExtra.failedRecords = mergedFailedRecords;
  const confirmedFinal = await repo.setStatus(executingRow.id, "confirmed", scope, confirmedExtra);
  const emitFailedRecordsPayload = Array.isArray(mergedFailedRecords) && mergedFailedRecords.length > 0 ? mergedFailedRecords : null;
  await emitConfirmed(emitter, {
    pendingActionId: confirmedFinal.id,
    agentId: agent.id,
    toolName: tool.name,
    status: confirmedFinal.status,
    tenantId: ctx.tenantId,
    organizationId: ctx.organizationId ?? null,
    userId: ctx.userId,
    resolvedByUserId: ctx.userId,
    resolvedAt: (confirmedFinal.resolvedAt ?? clock).toISOString?.() ?? new Date(clock).toISOString(),
    executionResult: successResult,
    ...emitFailedRecordsPayload ? { failedRecords: emitFailedRecordsPayload } : {}
  });
  return { ok: true, action: confirmedFinal, executionResult: successResult };
}
const PENDING_ACTION_CONFIRMED_EVENT_ID = CONFIRMED_EVENT_ID;
export {
  PENDING_ACTION_CONFIRMED_EVENT_ID,
  executePendingActionConfirm
};
//# sourceMappingURL=pending-action-executor.js.map
