import { LogStatus, LogError, Metadata, RunQuery } from '@memberjunction/core';
import { sendPostRequest } from '../util.js';
import { configInfo, baseUrl, publicUrl, graphqlPort, graphqlRootPath, apiKey as callbackAPIKey } from '../config.js';
import { GetAIAPIKey } from '@memberjunction/ai';
import { AIEngine } from '@memberjunction/aiengine';
import { CopyScalarsAndArrays } from '@memberjunction/global';
import mssql from 'mssql';
import { registerAccessToken } from '../resolvers/GetDataResolver.js';
import { BehaviorSubject } from 'rxjs';
import { take } from 'rxjs/operators';
export class SkipSDK {
    config;
    static __skipEntitiesCache$ = new BehaviorSubject(null);
    static __lastRefreshTime = 0;
    constructor(config) {
        this.config = {
            apiUrl: config?.apiUrl || configInfo.askSkip?.chatURL,
            apiKey: config?.apiKey || configInfo.askSkip?.apiKey,
            organizationId: config?.organizationId || configInfo.askSkip?.orgID,
            organizationInfo: config?.organizationInfo || configInfo.askSkip?.organizationInfo
        };
    }
    async chat(options) {
        LogStatus(`[SkipSDK] Sending request to Skip API: ${this.config.apiUrl}`);
        try {
            const skipRequest = await this.buildSkipRequest(options);
            const responses = await sendPostRequest(this.config.apiUrl, skipRequest, true, this.buildHeaders(), (streamMessage) => {
                if (streamMessage.type === 'status_update' && options.onStatusUpdate) {
                    const statusContent = streamMessage.value?.messages?.[0]?.content;
                    const responsePhase = streamMessage.value?.responsePhase;
                    if (statusContent) {
                        options.onStatusUpdate(statusContent, responsePhase);
                    }
                }
            });
            if (responses && responses.length > 0) {
                const finalResponse = responses[responses.length - 1].value;
                return {
                    success: true,
                    response: finalResponse,
                    responsePhase: finalResponse.responsePhase,
                    allResponses: responses
                };
            }
            else {
                return {
                    success: false,
                    error: 'No response received from Skip API'
                };
            }
        }
        catch (error) {
            LogError(`[SkipSDK] Error calling Skip API: ${error}`);
            return {
                success: false,
                error: String(error)
            };
        }
    }
    async buildSkipRequest(options) {
        const { messages, conversationId, dataContext, requestPhase = 'initial_request', contextUser, dataSource, includeEntities = true, includeQueries = true, includeNotes = true, includeRequests = false, forceEntityRefresh = false, includeCallbackAuth = true, payload } = options;
        const baseRequest = await this.buildBaseRequest(contextUser, dataSource, includeEntities, includeQueries, includeNotes, includeRequests, forceEntityRefresh, includeCallbackAuth, { conversationId, requestPhase });
        const artifacts = await this.buildArtifacts(contextUser, dataSource, conversationId);
        const processedMessages = this.processMessages(messages);
        const request = {
            messages: processedMessages,
            conversationID: conversationId,
            dataContext: dataContext ? CopyScalarsAndArrays(dataContext) : undefined,
            requestPhase,
            artifacts,
            payload,
            entities: baseRequest.entities || [],
            queries: baseRequest.queries || [],
            notes: baseRequest.notes,
            noteTypes: baseRequest.noteTypes,
            userEmail: baseRequest.userEmail,
            organizationID: baseRequest.organizationID,
            organizationInfo: baseRequest.organizationInfo,
            apiKeys: baseRequest.apiKeys,
            callingServerURL: baseRequest.callingServerURL,
            callingServerAPIKey: baseRequest.callingServerAPIKey,
            callingServerAccessToken: baseRequest.callingServerAccessToken
        };
        return request;
    }
    async buildBaseRequest(contextUser, dataSource, includeEntities, includeQueries, includeNotes, includeRequests, forceEntityRefresh, includeCallbackAuth, additionalTokenInfo = {}) {
        const entities = includeEntities ? await this.buildEntities(dataSource, forceEntityRefresh) : [];
        const queries = includeQueries ? this.buildQueries() : [];
        const { notes, noteTypes } = includeNotes ? await this.buildAgentNotes(contextUser) : { notes: [], noteTypes: [] };
        let accessToken;
        if (includeCallbackAuth) {
            const tokenInfo = {
                type: 'skip_api_request',
                userEmail: contextUser.Email,
                userName: contextUser.Name,
                userID: contextUser.ID,
                ...additionalTokenInfo
            };
            accessToken = registerAccessToken(undefined, 1000 * 60 * 10, tokenInfo);
        }
        return {
            entities,
            queries,
            notes,
            noteTypes,
            userEmail: contextUser.Email,
            organizationID: this.config.organizationId,
            organizationInfo: this.config.organizationInfo,
            apiKeys: this.buildAPIKeys(),
            callingServerURL: accessToken ? (publicUrl || `${baseUrl}:${graphqlPort}${graphqlRootPath}`) : undefined,
            callingServerAPIKey: accessToken ? callbackAPIKey : undefined,
            callingServerAccessToken: accessToken ? accessToken.Token : undefined
        };
    }
    async buildEntities(dataSource, forceRefresh, refreshIntervalMinutes = 15) {
        try {
            const now = Date.now();
            const cacheExpired = (now - SkipSDK.__lastRefreshTime) > (refreshIntervalMinutes * 60 * 1000);
            if (forceRefresh || cacheExpired || SkipSDK.__skipEntitiesCache$.value === null) {
                LogStatus(`[SkipSDK] Refreshing Skip entities cache (force: ${forceRefresh}, expired: ${cacheExpired})`);
                const newData = this.refreshSkipEntities(dataSource);
                SkipSDK.__skipEntitiesCache$.next(newData);
            }
            return SkipSDK.__skipEntitiesCache$.pipe(take(1)).toPromise();
        }
        catch (e) {
            LogError(`[SkipSDK] buildEntities error: ${e}`);
            return [];
        }
    }
    buildQueries(status = 'Approved') {
        const md = new Metadata();
        const approvedQueries = md.Queries.filter((q) => q.Status === status);
        return approvedQueries.map((q) => ({
            ID: q.ID,
            Name: q.Name,
            Description: q.Description,
            Category: q.Category,
            CategoryPath: this.buildQueryCategoryPath(md, q.CategoryID),
            CategoryID: q.CategoryID,
            SQL: q.SQL,
            Status: q.Status,
            QualityRank: q.QualityRank,
            EmbeddingVector: q.EmbeddingVector,
            EmbeddingModelID: q.EmbeddingModelID,
            EmbeddingModelName: q.EmbeddingModel,
            Fields: q.Fields.map((f) => ({
                ID: f.ID,
                QueryID: f.QueryID,
                Name: f.Name,
                Description: f.Description,
                Sequence: f.Sequence,
                SQLBaseType: f.SQLBaseType,
                SQLFullType: f.SQLFullType,
                SourceEntityID: f.SourceEntityID,
                SourceEntity: f.SourceEntity,
                SourceFieldName: f.SourceFieldName,
                IsComputed: f.IsComputed,
                ComputationDescription: f.ComputationDescription,
                IsSummary: f.IsSummary,
                SummaryDescription: f.SummaryDescription
            })),
            Parameters: q.Parameters.map((p) => ({
                ID: p.ID,
                QueryID: p.QueryID,
                Name: p.Name,
                Description: p.Description,
                Type: p.Type,
                IsRequired: p.IsRequired,
                DefaultValue: p.DefaultValue,
                SampleValue: p.SampleValue,
                ValidationFilters: p.ValidationFilters
            })),
            Entities: q.Entities.map((e) => ({
                ID: e.ID,
                QueryID: e.QueryID,
                EntityID: e.EntityID,
                Entity: e.Entity
            })),
            CacheEnabled: q.CacheEnabled,
            CacheMaxSize: q.CacheMaxSize,
            CacheTTLMinutes: q.CacheMaxSize,
            CacheValidationSQL: q.CacheValidationSQL
        }));
    }
    buildQueryCategoryPath(md, categoryID) {
        const cat = md.QueryCategories.find((c) => c.ID === categoryID);
        if (!cat)
            return '';
        if (!cat.ParentID)
            return cat.Name;
        const parentPath = this.buildQueryCategoryPath(md, cat.ParentID);
        return parentPath ? `${parentPath}/${cat.Name}` : cat.Name;
    }
    async buildAgentNotes(contextUser) {
        return { notes: [], noteTypes: [] };
    }
    async buildArtifacts(contextUser, dataSource, conversationId) {
        try {
            const rq = new RunQuery();
            await AIEngine.Instance.Config(false, contextUser);
            const skipAgent = AIEngine.Instance.GetAgentByName('Skip');
            const skipAgentId = skipAgent?.ID;
            if (!skipAgentId) {
                LogError('[SkipSDK] Skip agent not found in AIEngine');
            }
            const result = await rq.RunQuery({
                QueryName: 'GetConversationArtifactsForAgent',
                CategoryPath: 'MJ/Conversations',
                Parameters: {
                    ConversationID: conversationId,
                    AgentID: skipAgentId
                }
            }, contextUser);
            if (!result.Success || !result.Results || result.Results.length === 0) {
                return [];
            }
            const artifactMap = new Map();
            for (const row of result.Results) {
                const artifactId = row.ArtifactID;
                if (!artifactMap.has(artifactId)) {
                    let sharingScope = 'None';
                    const dbSharingScope = (row.SharingScope || '').toLowerCase();
                    if (dbSharingScope === 'always' || dbSharingScope === 'everyone') {
                        sharingScope = 'Everyone';
                    }
                    else if (dbSharingScope === 'public') {
                        sharingScope = 'Public';
                    }
                    else if (dbSharingScope === 'specific users' || dbSharingScope === 'specificusers') {
                        sharingScope = 'SpecificUsers';
                    }
                    artifactMap.set(artifactId, {
                        artifact: {
                            id: artifactId,
                            conversationId: conversationId,
                            name: row.ArtifactName,
                            description: row.ArtifactDescription || '',
                            sharingScope: sharingScope,
                            comments: row.ArtifactComments || '',
                            createdAt: new Date(row.ArtifactCreatedAt),
                            updatedAt: new Date(row.ArtifactUpdatedAt)
                        },
                        artifactType: {
                            id: row.ArtifactTypeID,
                            name: row.ArtifactTypeName,
                            description: row.ArtifactTypeDescription,
                            contentType: row.ArtifactTypeContentType,
                            enabled: true,
                            createdAt: new Date(row.ArtifactTypeCreatedAt),
                            updatedAt: new Date(row.ArtifactTypeUpdatedAt)
                        },
                        versions: []
                    });
                }
                const entry = artifactMap.get(artifactId);
                entry.versions.push({
                    id: row.VersionID,
                    artifactId: artifactId,
                    conversationDetailID: row.ConversationDetailID,
                    version: row.Version,
                    configuration: row.Configuration || '',
                    content: row.Content || '',
                    comments: row.VersionComments || '',
                    createdAt: new Date(row.VersionCreatedAt),
                    updatedAt: new Date(row.VersionUpdatedAt)
                });
            }
            const artifacts = Array.from(artifactMap.values()).map(entry => ({
                ...entry.artifact,
                artifactType: entry.artifactType,
                versions: entry.versions
            }));
            return artifacts;
        }
        catch (error) {
            LogError(`Failed to build artifacts for conversation ${conversationId}: ${error}`);
            return [];
        }
    }
    buildAPIKeys() {
        return [
            {
                vendorDriverName: 'OpenAILLM',
                apiKey: GetAIAPIKey('OpenAILLM')
            },
            {
                vendorDriverName: 'AnthropicLLM',
                apiKey: GetAIAPIKey('AnthropicLLM')
            },
            {
                vendorDriverName: 'GeminiLLM',
                apiKey: GetAIAPIKey('GeminiLLM')
            },
            {
                vendorDriverName: 'GroqLLM',
                apiKey: GetAIAPIKey('GroqLLM')
            },
            {
                vendorDriverName: 'MistralLLM',
                apiKey: GetAIAPIKey('MistralLLM')
            },
            {
                vendorDriverName: 'CerebrasLLM',
                apiKey: GetAIAPIKey('CerebrasLLM')
            }
        ];
    }
    buildHeaders() {
        return {
            'x-api-key': this.config.apiKey || '',
            'Content-Type': 'application/json'
        };
    }
    async refreshSkipEntities(dataSource) {
        try {
            const md = new Metadata();
            LogStatus(`[SkipSDK.refreshSkipEntities] Total entities in metadata: ${md.Entities.length}`);
            LogStatus(`[SkipSDK.refreshSkipEntities] Config excludeSchemas: ${JSON.stringify(configInfo.askSkip?.entitiesToSend?.excludeSchemas)}`);
            LogStatus(`[SkipSDK.refreshSkipEntities] Config includeEntitiesFromExcludedSchemas: ${JSON.stringify(configInfo.askSkip?.entitiesToSend?.includeEntitiesFromExcludedSchemas)}`);
            const skipSpecialIncludeEntities = (configInfo.askSkip?.entitiesToSend?.includeEntitiesFromExcludedSchemas ?? [])
                .map((e) => e.trim().toLowerCase());
            const entities = md.Entities.filter((e) => {
                if (!(configInfo.askSkip?.entitiesToSend?.excludeSchemas ?? []).includes(e.SchemaName) ||
                    skipSpecialIncludeEntities.includes(e.Name.trim().toLowerCase())) {
                    const sd = e.ScopeDefault?.trim();
                    if (sd && sd.length > 0) {
                        const scopes = sd.split(',').map((s) => s.trim().toLowerCase()) ?? ['all'];
                        return !scopes || scopes.length === 0 || scopes.includes('all') || scopes.includes('ai') || skipSpecialIncludeEntities.includes(e.Name.trim().toLowerCase());
                    }
                    else {
                        return true;
                    }
                }
                return false;
            });
            LogStatus(`[SkipSDK.refreshSkipEntities] Filtered entities count: ${entities.length}`);
            if (entities.length === 0) {
                LogError(`[SkipSDK.refreshSkipEntities] WARNING: No entities passed filtering! This will result in empty Skip entities list.`);
            }
            const result = await Promise.all(entities.map((e) => this.packSingleSkipEntityInfo(e, dataSource)));
            LogStatus(`[SkipSDK.refreshSkipEntities] Successfully packed ${result.length} entities for Skip`);
            SkipSDK.__lastRefreshTime = Date.now();
            return result;
        }
        catch (e) {
            LogError(`[SkipSDK] refreshSkipEntities error: ${e}`);
            return [];
        }
    }
    async packSingleSkipEntityInfo(e, dataSource) {
        try {
            const ret = {
                id: e.ID,
                name: e.Name,
                schemaName: e.SchemaName,
                baseView: e.BaseView,
                description: e.Description,
                fields: await Promise.all(e.Fields.filter(f => {
                    const scopes = f.ScopeDefault?.split(',').map((s) => s.trim().toLowerCase());
                    return !scopes || scopes.length === 0 || scopes.includes('all') || scopes.includes('ai');
                }).map(f => {
                    return this.packSingleSkipEntityField(f, dataSource);
                })),
                relatedEntities: e.RelatedEntities.map((r) => {
                    return this.packSingleSkipEntityRelationship(r);
                })
            };
            return ret;
        }
        catch (e) {
            LogError(`[SkipSDK] packSingleSkipEntityInfo error: ${e}`);
            return null;
        }
    }
    packSingleSkipEntityRelationship(r) {
        try {
            return {
                entityID: r.EntityID,
                relatedEntityID: r.RelatedEntityID,
                type: r.Type,
                entityKeyField: r.EntityKeyField,
                relatedEntityJoinField: r.RelatedEntityJoinField,
                joinView: r.JoinView,
                joinEntityJoinField: r.JoinEntityJoinField,
                joinEntityInverseJoinField: r.JoinEntityInverseJoinField,
                entity: r.Entity,
                entityBaseView: r.EntityBaseView,
                relatedEntity: r.RelatedEntity,
                relatedEntityBaseView: r.RelatedEntityBaseView,
            };
        }
        catch (e) {
            LogError(`[SkipSDK] packSingleSkipEntityRelationship error: ${e}`);
            return null;
        }
    }
    async packSingleSkipEntityField(f, dataSource) {
        try {
            return {
                entityID: f.EntityID,
                sequence: f.Sequence,
                name: f.Name,
                displayName: f.DisplayName,
                category: f.Category,
                type: f.Type,
                description: f.Description,
                isPrimaryKey: f.IsPrimaryKey,
                allowsNull: f.AllowsNull,
                isUnique: f.IsUnique,
                length: f.Length,
                precision: f.Precision,
                scale: f.Scale,
                sqlFullType: f.SQLFullType,
                defaultValue: f.DefaultValue,
                autoIncrement: f.AutoIncrement,
                valueListType: f.ValueListType,
                extendedType: f.ExtendedType,
                defaultInView: f.DefaultInView,
                defaultColumnWidth: f.DefaultColumnWidth,
                isVirtual: f.IsVirtual,
                isNameField: f.IsNameField,
                relatedEntityID: f.RelatedEntityID,
                relatedEntityFieldName: f.RelatedEntityFieldName,
                relatedEntity: f.RelatedEntity,
                relatedEntitySchemaName: f.RelatedEntitySchemaName,
                relatedEntityBaseView: f.RelatedEntityBaseView,
                possibleValues: await this.packFieldPossibleValues(f, dataSource),
            };
        }
        catch (e) {
            LogError(`[SkipSDK] packSingleSkipEntityField error: ${e}`);
            return null;
        }
    }
    async packFieldPossibleValues(f, dataSource) {
        try {
            if (f.ValuesToPackWithSchema === 'None') {
                return [];
            }
            else if (f.ValuesToPackWithSchema === 'All') {
                return await this.getFieldDistinctValues(f, dataSource);
            }
            else if (f.ValuesToPackWithSchema === 'Auto') {
                if (f.ValueListTypeEnum === 'List') {
                    return f.EntityFieldValues.map((v) => {
                        return { value: v.Value, displayValue: v.Value };
                    });
                }
                else if (f.ValueListTypeEnum === 'ListOrUserEntry') {
                    const values = await this.getFieldDistinctValues(f, dataSource);
                    if (!values || values.length === 0) {
                        return f.EntityFieldValues.map((v) => {
                            return { value: v.Value, displayValue: v.Value };
                        });
                    }
                    else {
                        return [...new Set([...f.EntityFieldValues.map((v) => {
                                    return { value: v.Value, displayValue: v.Value };
                                }), ...values])];
                    }
                }
            }
            return [];
        }
        catch (e) {
            LogError(`[SkipSDK] packFieldPossibleValues error: ${e}`);
            return [];
        }
    }
    async getFieldDistinctValues(f, dataSource) {
        try {
            const sql = `SELECT DISTINCT ${f.Name} FROM ${f.SchemaName}.${f.BaseView}`;
            const request = new mssql.Request(dataSource);
            const result = await request.query(sql);
            if (!result || !result.recordset) {
                return [];
            }
            else {
                return result.recordset.map((r) => {
                    return {
                        value: r[f.Name],
                        displayValue: r[f.Name]
                    };
                });
            }
        }
        catch (e) {
            LogError(`[SkipSDK] getFieldDistinctValues error: ${e}`);
            return [];
        }
    }
    processMessages(messages) {
        const filteredMessages = messages.filter(msg => !this.isDelegationMessage(msg.content));
        return filteredMessages.map(msg => ({
            ...msg,
            hiddenToUser: msg.hiddenToUser ?? false,
            userRating: msg.userRating ?? null,
            userFeedback: msg.userFeedback ?? null,
            reflectionInsights: msg.reflectionInsights ?? null,
            summaryOfEarlierConveration: msg.summaryOfEarlierConveration ?? null
        }));
    }
    isDelegationMessage(content) {
        if (!content)
            return false;
        const lowerContent = content.toLowerCase();
        const hasDelegatingOrDelegate = lowerContent.includes('delegating') || lowerContent.includes('delegate');
        const hasSkip = lowerContent.includes('skip');
        return hasDelegatingOrDelegate && hasSkip;
    }
}
//# sourceMappingURL=skip-sdk.js.map