/** 模型目录未提供上下文窗口时使用的保守压缩阈值。 */
export const DEFAULT_AGENT_COMPRESSION_TOKEN_THRESHOLD = 30_000;

/** 智能体工作记忆占模型上下文窗口的最大比例。 */
export const AGENT_COMPRESSION_CONTEXT_RATIO = 0.85;

/** 未配置生成上限时预留的默认输出 Token 数。 */
const DEFAULT_OUTPUT_RESERVE_TOKENS = 8192;

/** AgentScope 1.x 混合文本 Token 估算使用的平均字符数。 */
const CHARS_PER_TOKEN = 2.5;

/** 系统消息角色与协议结构的估算开销。 */
const SYSTEM_MESSAGE_OVERHEAD_TOKENS = 5;

/** 单个工具 Schema 的估算结构开销。 */
const TOOL_SCHEMA_OVERHEAD_TOKENS = 15;

export interface AgentCompressionThresholdInput {
  contextWindowTokens?: unknown;
  generationConfig?: Record<string, any>;
  maxOutputTokens?: unknown;
  systemPrompt?: unknown;
  toolBindings?: Array<Record<string, any>>;
}

/**
 * 根据模型上下文窗口、输出预算和系统/工具占用计算智能体压缩阈值。
 *
 * @param input 压缩阈值计算输入
 * @returns 压缩触发 Token 数
 */
export function resolveAgentCompressionTokenThreshold(
  input: AgentCompressionThresholdInput,
) {
  const normalizedContextWindow = positiveInteger(input.contextWindowTokens);
  if (normalizedContextWindow === undefined) {
    return DEFAULT_AGENT_COMPRESSION_TOKEN_THRESHOLD;
  }

  const ratioThreshold = Math.floor(
    normalizedContextWindow * AGENT_COMPRESSION_CONTEXT_RATIO,
  );
  const safetyMargin = Math.max(
    2048,
    Math.floor(normalizedContextWindow * 0.05),
  );
  const availableContextThreshold =
    normalizedContextWindow -
    resolveOutputReserveTokens(input) -
    estimateSystemAndToolTokens(input) -
    safetyMargin;

  return Math.max(1, Math.min(ratioThreshold, availableContextThreshold));
}

function resolveOutputReserveTokens(input: AgentCompressionThresholdInput) {
  const generationConfig = input.generationConfig || {};
  const configuredOutputReserve = Math.max(
    positiveInteger(generationConfig.maxCompletionTokens) || 0,
    positiveInteger(generationConfig.maxTokens) || 0,
  );
  const catalogMaxOutput = positiveInteger(input.maxOutputTokens);
  if (configuredOutputReserve > 0) {
    return catalogMaxOutput
      ? Math.min(configuredOutputReserve, catalogMaxOutput)
      : configuredOutputReserve;
  }
  return Math.min(
    DEFAULT_OUTPUT_RESERVE_TOKENS,
    catalogMaxOutput || DEFAULT_OUTPUT_RESERVE_TOKENS,
  );
}

function estimateSystemAndToolTokens(input: AgentCompressionThresholdInput) {
  const systemPrompt = String(input.systemPrompt || '');
  let tokens =
    estimateTextTokens(systemPrompt) +
    (systemPrompt ? SYSTEM_MESSAGE_OVERHEAD_TOKENS : 0);

  for (const binding of input.toolBindings || []) {
    if (binding?.enabled === false) continue;
    const schemaPayload = {
      hitlConfigJson: binding?.hitlConfigJson,
      optionsJson: binding?.optionsJson,
      resourceSnapshot: binding?.resourceSnapshot,
      resourceSummary: binding?.resourceSummary,
      toolName: binding?.toolName,
      toolType: binding?.toolType,
    };
    tokens +=
      estimateTextTokens(safeJsonStringify(schemaPayload)) +
      TOOL_SCHEMA_OVERHEAD_TOKENS;
  }
  return tokens;
}

function estimateTextTokens(text: string) {
  return text ? Math.ceil(text.length / CHARS_PER_TOKEN) : 0;
}

function positiveInteger(value: unknown) {
  const number = Number(value);
  if (!Number.isFinite(number) || number <= 0) return;
  return Math.floor(number);
}

function safeJsonStringify(value: unknown) {
  try {
    return JSON.stringify(value) || '';
  } catch {
    return '';
  }
}
