/**
 * Search Service
 *
 * OpenSearch-based service for vector similarity search (KNN).
 * Uses AWS SigV4 authentication for secure access to OpenSearch domains.
 */

import { Client } from '@opensearch-project/opensearch';
import { AwsSigv4Signer } from '@opensearch-project/opensearch/aws';
import { defaultProvider } from '@aws-sdk/credential-provider-node';
import type { RelevantDocument } from '../types/chat.types';

interface KnnHit {
    _id: string;
    _score: number;
    _source: {
        content: string;
        metadata?: Record<string, unknown>;
    };
}

interface KnnResponse {
    hits: {
        hits: KnnHit[];
    };
}

export class SearchService {
    private client: Client | null = null;
    private indexName: string;
    private endpoint: string | undefined;
    private region: string;

    constructor() {
        this.endpoint = process.env.OPENSEARCH_ENDPOINT;
        this.region = process.env.AWS_REGION || 'us-east-1';
        this.indexName = process.env.OPENSEARCH_INDEX || 'knowledge-base';

        if (this.endpoint) {
            this.client = new Client({
                ...AwsSigv4Signer({
                    region: this.region,
                    service: 'es',
                    getCredentials: () => defaultProvider()(),
                }),
                node: this.endpoint,
            });
        }
    }

    /**
     * Performs K-nearest neighbor search on the vector index.
     * Returns empty array if OpenSearch is unavailable (graceful degradation).
     *
     * @param query - The search query text
     * @param k - Number of results to return (default: 5)
     * @returns Array of relevant documents sorted by similarity score
     */
    async knn(query: string, k: number = 5): Promise<RelevantDocument[]> {
        if (!this.client) {
            console.warn('OpenSearch client not configured. Skipping vector search.');
            return [];
        }

        try {
            const response = await this.client.search({
                index: this.indexName,
                body: {
                    size: k,
                    query: {
                        match: {
                            content: query,
                        },
                    },
                },
            }) as { body: KnnResponse };

            return response.body.hits.hits.map((hit) => ({
                id: hit._id,
                content: hit._source.content,
                score: hit._score,
                metadata: hit._source.metadata,
            }));
        } catch (error) {
            console.warn('OpenSearch query failed:', error);
            return [];
        }
    }
}
