/**
 * Memory utilities
 * Helper functions for memory manipulation (formatting, snapshots)
 */

import type { AgentMemory } from './types'
import type { ExecutionContext } from '../../base/types'
import type { SupabaseClient } from '../../../../supabase'

/**
 * Load session memory from database
 *
 * Standard pattern for preloadMemory callback:
 * - If sessionId exists: restore memory snapshot from database
 * - If no sessionId: return fresh memory (one-off task)
 * - Handles errors gracefully by returning empty memory
 *
 * @param supabaseClient - Supabase client instance
 * @param context - Execution context (must include organizationId, optional sessionId)
 * @returns Agent memory (restored from DB or fresh)
 *
 * @example
 * ```typescript
 * import { loadSessionMemory } from '@repo/core'
 * import { supabaseClient } from '@repo/core/server'
 *
 * async function preloadMemory(context: ExecutionContext): Promise<AgentMemory> {
 *   return loadSessionMemory(supabaseClient, context)
 * }
 * ```
 */
export async function loadSessionMemory(
  supabaseClient: SupabaseClient,
  context: ExecutionContext
): Promise<AgentMemory> {
  // If session turn, restore memory snapshot from database
  if (context.sessionId) {
    const { data: session, error } = await supabaseClient
      .from('sessions')
      .select('memory_snapshot')
      .eq('session_id', context.sessionId)
      .eq('organization_id', context.organizationId)
      .single()

    if (error) {
      console.error('Failed to load session from DB', {
        sessionId: context.sessionId,
        error: error.message
      })
      return { sessionMemory: {}, history: [] }
    }

    const result = (session?.memory_snapshot as unknown as AgentMemory) || { sessionMemory: {}, history: [] }
    return result
  }

  // One-off task: start with fresh memory
  return { sessionMemory: {}, history: [] }
}

// ============================================================================
// Memory Helpers
// Utility functions for memory manipulation during action execution
// ============================================================================

import type { ToolCallAction } from '../actions/types'
import type { MemoryManager } from './manager'
import type { AgentScopedLogger } from '../observability/logging'
import { isToolOwnedKey, isActionOwnedKey, getKeyOwner } from './domains'

/**
 * Add tool execution error to memory
 * Errors are stored as 'error' type entries and returned to LLM as tool results
 * This allows the LLM to recover from errors and try alternative approaches
 *
 * @param memoryManager - Memory manager to add error to
 * @param action - Tool call that failed
 * @param errorMessage - Error description
 * @param iteration - Iteration number when error occurred
 * @param turnNumber - Turn number when error occurred (optional, defaults to null for one-off executions)
 * @param metadata - Structured error metadata from ToolingError (optional)
 */
export function addToolError(
  memoryManager: MemoryManager,
  action: ToolCallAction,
  errorMessage: string,
  iteration: number,
  turnNumber: number | null = null,
  metadata?: {
    errorType?: string
    severity?: string
    isRetryable?: boolean
  }
): void {
  memoryManager.addToHistory({
    type: 'error',
    content: JSON.stringify({
      error: errorMessage,
      toolName: action.name,
      toolCallId: action.id,
      ...(metadata?.errorType && { errorType: metadata.errorType }),
      ...(metadata?.severity && { severity: metadata.severity }),
      ...(metadata?.isRetryable !== undefined && { isRetryable: metadata.isRetryable })
    }),
    turnNumber,
    iterationNumber: iteration
  })
}

/**
 * Validate memory key ownership
 * Prevents LLM from updating system-managed keys (tool-owned or action-owned)
 *
 * @param key - Memory key to validate
 * @param logger - Logger for rejected operations
 * @param iteration - Current iteration number
 * @returns true if key can be updated by LLM, false if rejected
 */
export function validateMemoryKeyOwnership(key: string, logger: AgentScopedLogger, iteration: number): boolean {
  // VALIDATION: Prevent LLM from updating system-managed keys
  if (isToolOwnedKey(key) || isActionOwnedKey(key)) {
    const owner = getKeyOwner(key)
    logger.action(
      'memory-set-rejected',
      `Rejected LLM update to ${owner}-managed key: ${key}`,
      iteration,
      Date.now(),
      Date.now(),
      0
    )
    return false
  }
  return true
}
