// Copyright (C) 2025 Keygraph, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License version 3
// as published by the Free Software Foundation.

/**
 * Consolidated billing/spending cap detection utilities.
 *
 * Anthropic's spending cap behavior is inconsistent:
 * - Sometimes a proper SDK error (billing_error)
 * - Sometimes Claude responds with text about the cap
 * - Sometimes partial billing before cutoff
 *
 * This module provides defense-in-depth detection with shared pattern lists
 * to prevent drift between detection points.
 */

/**
 * Text patterns for SDK output sniffing (what Claude says).
 * Used by message-handlers.ts and the behavioral heuristic.
 */
export const BILLING_TEXT_PATTERNS = [
  'spending cap',
  'spending limit',
  'cap reached',
  'budget exceeded',
  'usage limit',
] as const;

/**
 * API patterns for error message classification (what the API returns).
 * Used by classifyErrorForTemporal in error-handling.ts.
 */
export const BILLING_API_PATTERNS = [
  'billing_error',
  'credit balance is too low',
  'insufficient credits',
  'usage is blocked due to insufficient credits',
  'please visit plans & billing',
  'please visit plans and billing',
  'usage limit reached',
  'quota exceeded',
  'daily rate limit',
  'limit will reset',
  'billing limit reached',
] as const;

/**
 * Checks if text matches any billing text pattern.
 * Used for sniffing SDK output content for spending cap messages.
 */
export function matchesBillingTextPattern(text: string): boolean {
  const lowerText = text.toLowerCase();
  return BILLING_TEXT_PATTERNS.some((pattern) => lowerText.includes(pattern));
}

/**
 * Checks if an error message matches any billing API pattern.
 * Used for classifying API error messages.
 */
export function matchesBillingApiPattern(message: string): boolean {
  const lowerMessage = message.toLowerCase();
  return BILLING_API_PATTERNS.some((pattern) => lowerMessage.includes(pattern));
}

/**
 * Behavioral heuristic for detecting spending cap.
 *
 * When Claude hits a spending cap, it often returns a short message
 * with $0 cost. Legitimate agent work NEVER costs $0 with only 1-2 turns.
 *
 * This combines three signals:
 * 1. Very low turn count (<=2)
 * 2. Zero cost ($0)
 * 3. Text matches billing patterns
 *
 * @param turns - Number of turns the agent took
 * @param cost - Total cost in USD
 * @param resultText - The result text from the agent
 * @returns true if this looks like a spending cap hit
 */
export function isSpendingCapBehavior(turns: number, cost: number, resultText: string): boolean {
  // Only check if turns <= 2 AND cost is exactly 0
  if (turns > 2 || cost !== 0) {
    return false;
  }

  return matchesBillingTextPattern(resultText);
}
