/*
 * Runtime Web – Unified Serverless Framework for Full-Stack TypeScript Applications
 * Documentation: https://runtimeweb.com/docs/getting-started
 *
 * @template: ai-chat
 */

/**
 * Memory Service
 *
 * DynamoDB-based service for storing and retrieving conversation session history.
 * Uses AWS SDK v3 modular imports for optimal bundle size.
 *
 * DynamoDB Schema:
 * - sessionId (String, PK): Unique session identifier
 * - messages (List): Array of ConversationMessage objects
 * - createdAt (String): ISO timestamp of session creation
 * - updatedAt (String): ISO timestamp of last update
 * - ttl (Number): Optional TTL for automatic expiration
 *
 * @module backend-services/memory.service
 */

import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import {
    DynamoDBDocumentClient,
    GetCommand,
    UpdateCommand,
} from '@aws-sdk/lib-dynamodb';
import type { ConversationMessage, ConversationTurn } from '../types/chat.types';

/**
 * Service for managing conversation session memory in DynamoDB.
 *
 * Provides methods to retrieve and append conversation history,
 * enabling multi-turn conversations with context preservation.
 *
 * @example
 * ```typescript
 * const memory = new MemoryService();
 *
 * // Get conversation history
 * const history = await memory.get('session-123');
 *
 * // Append a new turn
 * await memory.append('session-123', {
 *   user: 'Hello!',
 *   assistant: 'Hi there! How can I help you?',
 * });
 * ```
 */
export class MemoryService {
    private readonly docClient: DynamoDBDocumentClient;
    private readonly tableName: string;

    /**
     * Creates a new MemoryService instance.
     *
     * Initializes the DynamoDB Document Client and configures
     * the table name from environment variables.
     */
    constructor() {
        const client = new DynamoDBClient({});
        this.docClient = DynamoDBDocumentClient.from(client, {
            marshallOptions: {
                removeUndefinedValues: true,
            },
        });
        this.tableName = process.env.MEMORY_TABLE_NAME || 'ai-chat-sessions';
    }

    /**
     * Retrieves conversation history for a session.
     *
     * Returns an empty array if the session doesn't exist,
     * enabling graceful handling of new sessions.
     *
     * @param sessionId - Unique session identifier
     * @returns Array of conversation messages, empty array if session doesn't exist
     */
    async get(sessionId: string): Promise<ConversationMessage[]> {
        const command = new GetCommand({
            TableName: this.tableName,
            Key: { sessionId },
            ProjectionExpression: 'messages',
        });

        const response = await this.docClient.send(command);

        if (!response.Item || !response.Item.messages) {
            return [];
        }

        return response.Item.messages as ConversationMessage[];
    }

    /**
     * Appends a conversation turn (user message + assistant response) to session history.
     *
     * Creates the session if it doesn't exist, using DynamoDB's
     * conditional update expressions for atomic operations.
     *
     * @param sessionId - Unique session identifier
     * @param turn - Conversation turn containing user message and assistant response
     */
    async append(sessionId: string, turn: ConversationTurn): Promise<void> {
        const timestamp = new Date().toISOString();

        const userMessage: ConversationMessage = {
            role: 'user',
            content: turn.user,
            timestamp,
        };

        const assistantMessage: ConversationMessage = {
            role: 'assistant',
            content: turn.assistant,
            timestamp,
        };

        const command = new UpdateCommand({
            TableName: this.tableName,
            Key: { sessionId },
            UpdateExpression: `
                SET messages = list_append(if_not_exists(messages, :empty), :newMessages),
                    updatedAt = :updatedAt,
                    createdAt = if_not_exists(createdAt, :createdAt)
            `,
            ExpressionAttributeValues: {
                ':empty': [],
                ':newMessages': [userMessage, assistantMessage],
                ':updatedAt': timestamp,
                ':createdAt': timestamp,
            },
        });

        await this.docClient.send(command);
    }
}
