import { type AgentRuntimeContext, type AgentState } from '@lobechat/agent-runtime';
import type {
  BotPlatformContext,
  LobeToolManifest,
  OperationSkillSet,
  ToolExecutor,
  ToolSource,
} from '@lobechat/context-engine';
import type { ChatTopicBotContext, UserInterventionConfig } from '@lobechat/types';

import type { ExecutionPlan } from '@/helpers/executionTarget';
import { type ServerUserMemoryConfig } from '@/server/modules/Mecha/ContextEngineering/types';
import type { AgentSignalOperationMarker } from '@/server/services/agentSignal/operationMarker';
import type { DeviceAccessReason } from '@/server/services/aiAgent/deviceAccessPolicy';

import { type AgentHook } from './hooks/types';

// ==================== Operation Tool Set ====================

export interface OperationToolSet {
  enabledToolIds?: string[];
  executorMap?: Record<string, ToolExecutor>;
  manifestMap: Record<string, LobeToolManifest>;
  sourceMap?: Record<string, ToolSource>;
  tools?: any[];
}

// ==================== Step Lifecycle Callbacks ====================

/**
 * Step execution lifecycle callbacks
 * Used to inject custom logic at different stages of step execution
 */
export interface StepPresentationData {
  /** LLM text output (undefined if this was a tool step) */
  content?: string;
  /** This step's execution time in ms */
  executionTimeMs: number;
  /** LLM reasoning / thinking content (undefined if none) */
  reasoning?: string;
  /** This step's cost (LLM steps only) */
  stepCost?: number;
  /** This step's input tokens (LLM steps only) */
  stepInputTokens?: number;
  /** This step's output tokens (LLM steps only) */
  stepOutputTokens?: number;
  /** This step's total tokens (LLM steps only) */
  stepTotalTokens?: number;
  /** What this step executed */
  stepType: 'call_llm' | 'call_tool';
  /** true = next step is LLM thinking; false = next step is tool execution */
  thinking: boolean;
  /** Tools the LLM decided to call (undefined if no tool calls) */
  toolsCalling?: Array<{ apiName: string; arguments?: string; identifier: string }>;
  /** Results from tool execution (only for call_tool steps) */
  toolsResult?: Array<{
    apiName: string;
    identifier: string;
    isSuccess?: boolean;
    output?: string;
  }>;
  /** Cumulative total cost */
  totalCost: number;
  /** Cumulative input tokens */
  totalInputTokens: number;
  /** Cumulative output tokens */
  totalOutputTokens: number;
  /** Total steps executed so far */
  totalSteps: number;
  /** Cumulative total tokens */
  totalTokens: number;
}

export interface StepLifecycleCallbacks {
  /**
   * Called after step execution
   */
  onAfterStep?: (
    params: StepPresentationData & {
      operationId: string;
      shouldContinue: boolean;
      state: AgentState;
      stepIndex: number;
      stepResult: any;
    },
  ) => Promise<void>;

  /**
   * Called before step execution
   */
  onBeforeStep?: (params: {
    context?: AgentRuntimeContext;
    operationId: string;
    state: AgentState;
    stepIndex: number;
  }) => Promise<void>;

  /**
   * Called when operation completes (status changes to done/error/interrupted)
   */
  onComplete?: (params: {
    finalState: AgentState;
    operationId: string;
    reason: StepCompletionReason;
  }) => Promise<void>;
}

/**
 * Step completion reason
 */
export type StepCompletionReason =
  | 'done'
  | 'error'
  | 'interrupted'
  | 'max_steps'
  | 'cost_limit'
  | 'waiting_for_human'
  | 'waiting_for_async_tool';

// ==================== Execution Params ====================

export interface AgentExecutionParams {
  approvedToolCall?: any;
  context?: AgentRuntimeContext;
  externalRetryCount?: number;
  humanInput?: any;
  operationId: string;
  /**
   * Whether a rejection should resume execution by treating the rejected tool
   * content as user input (maps to client `rejectAndContinueToolCalling`).
   * When false or unset, a rejection halts the operation.
   */
  rejectAndContinue?: boolean;
  rejectionReason?: string;
  /**
   * Resume a `waiting_for_async_tool` op after its deferred tools (e.g. server
   * sub-agents) have all delivered results. Scheduled by the completion bridge
   * via `tryResumeParentFromAsyncTool`.
   */
  resumeAsyncTool?: boolean;
  stepIndex: number;
  /** ID of the pending tool message targeted by the intervention. */
  toolMessageId?: string;
  /**
   * Watchdog re-check for a parked `waiting_for_async_tool` op: re-runs the
   * resume barrier + CAS without claiming the step lock or executing a step.
   * A no-op when the op already resumed or the barrier is still unsatisfied.
   * Scheduled one-shot by `tryResumeParentFromAsyncTool` when a sub-agent
   * completion found the parent not yet resumable (covers the
   * child-finishes-before-parent-parks race and transient barrier failures).
   */
  verifyAsyncToolBarrier?: boolean;
}

export interface AgentExecutionResult {
  /**
   * When true, the step was already being executed by another instance (lock conflict).
   * The caller should return 429 to force QStash to retry later.
   */
  locked?: boolean;
  nextStepScheduled: boolean;
  state: any;
  stepResult?: any;
  success: boolean;
}

/**
 * Params for the sub-agent completion bridge — see
 * `AgentRuntimeService.completeSubAgentBridge`.
 */
export interface SubAgentBridgeParams {
  /** Child op's final state — passed in local mode; loaded from the coordinator otherwise. */
  finalState?: AgentState;
  /** Child (sub-agent) operation ID. */
  operationId: string;
  parentOperationId: string;
  reason: string;
  threadId: string;
  /** The parent's placeholder `role: 'tool'` message to backfill. */
  toolMessageId: string;
}

export interface OperationCreationParams {
  activeDeviceId?: string;
  agentConfig?: any;
  appContext: {
    agentId?: string;
    /**
     * Run-scoped Agent Signal marker. Stamped at dispatch for background
     * self-iteration / memory runs; lands in `state.metadata.agentSignal` and is
     * read on the completion path to project receipts.
     */
    agentSignal?: AgentSignalOperationMarker;
    defaultTaskAssigneeAgentId?: string;
    documentId?: string | null;
    groupId?: string | null;
    isSubAgent?: boolean;
    scope?: string | null;
    /** Source user message ID used for same-turn Agent Signal procedure suppression. */
    sourceMessageId?: string;
    taskId?: string;
    threadId?: string | null;
    topicId?: string | null;
    trigger?: string;
  };
  autoStart?: boolean;
  /**
   * Sender/owner identity for bot-originated runs. Forwarded into
   * `state.metadata.botContext` so device-tool dispatch can audit who
   * triggered the call. `undefined` for first-party (web/desktop) callers.
   */
  botContext?: ChatTopicBotContext;
  /** Bot platform context for injecting platform capabilities (e.g. markdown support) */
  botPlatformContext?: BotPlatformContext;
  /**
   * Device-access policy decision computed once per turn by
   * `resolveDeviceAccessPolicy`. Forwarded into `state.metadata.deviceAccessPolicy`
   * so the dispatch site can include `reason` in the audit entry without
   * re-deriving it.
   */
  deviceAccessPolicy?: { canUseDevice: boolean; reason: DeviceAccessReason };
  /** Device system info for placeholder variable replacement in Local System systemRole */
  deviceSystemInfo?: Record<string, string>;
  /**
   * Resolved execution plan for the run (see `resolveExecutionPlan`).
   * Forwarded into `state.metadata.executionPlan` so step-level layers (the
   * `call_llm` device-tool injection) consume the plan instead of re-deriving
   * device capability from raw config.
   */
  executionPlan?: ExecutionPlan;
  /** Discord context for injecting channel/guild info into agent system message */
  discordContext?: any;
  evalContext?: any;
  /**
   * External lifecycle hooks
   * Registered once, auto-adapt to local (in-memory) or production (webhook) mode
   */
  hooks?: AgentHook[];
  initialContext: AgentRuntimeContext;
  initialMessages?: any[];
  /** Initial step count offset for resumed operations (accumulated from previous runs) */
  initialStepCount?: number;
  maxSteps?: number;
  modelRuntimeConfig?: any;
  operationId: string;
  /** Operation-level skill set for SkillResolver */
  operationSkillSet?: OperationSkillSet;
  /**
   * Operation ID of the parent run when this operation is a sub-agent
   * invocation (e.g. spawned via `execSubAgent`). Persisted to
   * `agent_operations.parent_operation_id` so analytics can join the
   * sub-tree back to its root.
   */
  parentOperationId?: string;
  queueRetries?: number;
  queueRetryDelay?: string;
  /** Abort startup before the first step is scheduled */
  signal?: AbortSignal;
  /**
   * Whether the LLM call should use streaming.
   * Defaults to true. Set to false for non-streaming scenarios (e.g., bot integrations).
   */
  stream?: boolean;
  toolSet: OperationToolSet;
  userId?: string;
  /**
   * User intervention configuration
   * Controls how tools requiring approval are handled
   * Use { approvalMode: 'headless' } for async tasks that should never wait for human approval
   */
  userInterventionConfig?: UserInterventionConfig;
  /** User memory (persona) for injection into LLM context */
  userMemory?: ServerUserMemoryConfig;
  /** User's timezone from settings (e.g. 'Asia/Shanghai') */
  userTimezone?: string;
  /**
   * Workspace ID propagated down from the originating chat/task router so
   * tool executions (createBrief / pinTask / etc.) ownership-filter to the
   * caller's workspace. Stored on `state.metadata.workspaceId`.
   */
  workspaceId?: string;
}

export interface OperationCreationResult {
  autoStarted: boolean;
  messageId?: string;
  operationId: string;
  success: boolean;
}

export interface OperationStatusResult {
  currentState: {
    cost?: any;
    costLimit?: any;
    error?: string;
    interruption?: any;
    lastModified: string;
    maxSteps?: number;
    pendingHumanPrompt?: any;
    pendingHumanSelect?: any;
    pendingToolsCalling?: any;
    status: string;
    stepCount: number;
    usage?: any;
  };
  executionHistory?: any[];
  hasError: boolean;
  isActive: boolean;
  isCompleted: boolean;
  metadata: any;
  needsHumanInput: boolean;
  operationId: string;
  recentEvents?: any[];
  stats: {
    lastActiveTime: number;
    totalCost: number;
    totalMessages: number;
    totalSteps: number;
    uptime: number;
  };
}

export interface PendingInterventionsResult {
  pendingInterventions: Array<{
    lastModified: string;
    modelRuntimeConfig?: any;
    operationId: string;
    pendingHumanPrompt?: any;
    pendingHumanSelect?: any;
    pendingToolsCalling?: any[];
    status: string;
    stepCount: number;
    type: 'tool_approval' | 'human_prompt' | 'human_select';
    userId?: string;
  }>;
  timestamp: string;
  totalCount: number;
}

export interface StartExecutionParams {
  context?: AgentRuntimeContext;
  delay?: number;
  operationId: string;
  priority?: 'high' | 'normal' | 'low';
}

export interface StartExecutionResult {
  messageId?: string;
  operationId: string;
  scheduled: boolean;
  success: boolean;
}
