"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TruPulseToolController = void 0;
const logger_1 = __importDefault(require("../../../../logger"));
const clickhouseQueries_1 = require("../../../services/argus/utils/clickhouseQueries");
const defaultFilters_1 = require("../../../services/argus/utils/defaultFilters");
class TruPulseToolController {
    /**
     * Apply default filters helper
     */
    static applyDefaults(filters, limit, sorting, defaultSortField = 'timestamp') {
        return (0, defaultFilters_1.applyDefaultFilters)(filters, limit, sorting, defaultSortField);
    }
    /**
     * 3.1 Discovery / schema / facets
     * GET /api/argus/tools/describe-data-model
     */
    static async describeDataModel(req, res) {
        try {
            // For now we return a static description based on existing ClickHouse schema
            const data = {
                entities: [
                    {
                        name: 'session',
                        description: 'Top-level interaction grouping traces and spans, derived from span attribute session.id',
                        retentionDays: 30,
                        supportedTimeRanges: ['last_24h', 'last_7d', 'last_30d', 'custom'],
                    },
                    {
                        name: 'trace',
                        description: 'Classic distributed trace, aggregated from OTEL_TRACES_TABLE_NAME',
                        retentionDays: 30,
                        supportedTimeRanges: ['last_24h', 'last_7d', 'last_30d', 'custom'],
                    },
                    {
                        name: 'span',
                        description: 'Individual span within a trace, including LLM/tool/retriever spans',
                        retentionDays: 30,
                        supportedTimeRanges: ['last_24h', 'last_7d', 'last_30d', 'custom'],
                    },
                    {
                        name: 'evaluation',
                        description: 'Evaluation results stored in EVAL_RESULTS_TABLE_NAME',
                        retentionDays: 30,
                        supportedTimeRanges: ['last_24h', 'last_7d', 'last_30d', 'custom'],
                    },
                ],
                conventions: {
                    genaiSemconvVersions: ['v0.1 (subset)'],
                    openInferenceCompatible: false,
                },
                fields: {
                    trace: [
                        'traceId',
                        'timestamp',
                        'duration',
                        'status',
                        'applicationId',
                        'spanId',
                        'name',
                    ],
                    span: [
                        'spanId',
                        'traceId',
                        'timestamp',
                        'duration',
                        'status',
                        'applicationId',
                        'name',
                        'parentSpanId',
                        'spanKind',
                    ],
                    session: [
                        'sessionId',
                        'timestamp',
                        'applicationId',
                        'traceId',
                        'status',
                    ],
                    evaluation: [
                        'id',
                        'trace_id',
                        'sessionId',
                        'eval_id',
                        'provider',
                        'latency_ms',
                        'cost',
                        'score',
                        'success',
                        'failure_reason',
                        'metricName',
                    ],
                },
            };
            res.json({
                success: true,
                data,
            });
        }
        catch (error) {
            logger_1.default.error(`describe_data_model error: ${error}`);
            res.status(500).json({
                success: false,
                message: error instanceof Error ? error.message : 'Internal server error',
            });
        }
    }
    /**
     * 3.1 list_dimensions(entity, time_range)
     * GET /api/argus/tools/list-dimensions
     */
    static async listDimensions(req, res) {
        try {
            const { entity } = req.query;
            const target = String(entity || '').toLowerCase();
            let dimensions = [];
            switch (target) {
                case 'session':
                    dimensions = [
                        'session.id',
                        'application.id',
                        'user.id',
                        'tenant_id',
                        'environment',
                        'status',
                        'service.name',
                        'gen_ai.request.model',
                        'gen_ai.response.model',
                        'gen_ai.provider.name',
                        'gen_ai.operation.name',
                        'gen_ai.conversation.id',
                        'duration_ms',
                        'start_time',
                        'end_time',
                        'trace_count',
                        'span_count',
                        'has_evaluations',
                        'has_security_findings',
                        'http.method',
                        'http.status_code',
                    ];
                    break;
                case 'trace':
                    dimensions = [
                        'trace.id',
                        'session.id',
                        'application.id',
                        'user.id',
                        'tenant_id',
                        'environment',
                        'service.name',
                        'status',
                        'status_code',
                        'error.type',
                        'error.message',
                        'gen_ai.request.model',
                        'gen_ai.response.model',
                        'gen_ai.provider.name',
                        'gen_ai.operation.name',
                        'gen_ai.conversation.id',
                        'duration_ms',
                        'span_count',
                        'http.method',
                        'http.url',
                        'http.status_code',
                        'operation_name',
                        'has_evaluations',
                        'has_security_findings',
                        'root_span_id',
                    ];
                    break;
                case 'span':
                    dimensions = [
                        'span.id',
                        'trace.id',
                        'session.id',
                        'parent_span_id',
                        'application.id',
                        'user.id',
                        'tenant_id',
                        'environment',
                        'service.name',
                        'span.kind',
                        'step_type',
                        'name',
                        'status',
                        'status_code',
                        'error.type',
                        'error.message',
                        'gen_ai.request.model',
                        'gen_ai.response.model',
                        'gen_ai.provider.name',
                        'gen_ai.operation.name',
                        'gen_ai.conversation.id',
                        'gen_ai.usage.input_tokens',
                        'gen_ai.usage.output_tokens',
                        'tool.name',
                        'mcp.method.name',
                        'duration_ms',
                        'http.method',
                        'http.url',
                        'http.status_code',
                        'operation_name',
                    ];
                    break;
                case 'evaluation':
                    dimensions = [
                        'eval.id',
                        'metric.name',
                        'application.id',
                        'session.id',
                        'trace.id',
                        'span.id',
                        'provider',
                        'model',
                        'prompt_version',
                        'is_redteam',
                        'success',
                        'failure_reason',
                        'score',
                        'latency_ms',
                        'cost',
                        'dataset_record_id',
                        'workspace_id',
                        'parent_prompt_id',
                        'entity_type',
                        'entity_id',
                        'threshold',
                        'status',
                        'created_at',
                        'updated_at',
                    ];
                    break;
                default:
                    dimensions = [];
            }
            res.json({
                success: true,
                data: {
                    entity: target,
                    dimensions,
                },
            });
        }
        catch (error) {
            logger_1.default.error(`list_dimensions error: ${error}`);
            res.status(500).json({
                success: false,
                message: error instanceof Error ? error.message : 'Internal server error',
            });
        }
    }
    /**
     * 3.1 get_dimension_values(entity, key, time_range, prefix?, limit?)
     * GET /api/argus/tools/get-dimension-values
     *
     * Returns distinct values for a given dimension key (facet) for an entity,
     * optionally constrained by time range and value prefix.
     */
    static async getDimensionValues(req, res) {
        try {
            const { entity, key, timeRange, prefix, limit } = req.query;
            if (!entity || !key) {
                res.status(400).json({
                    success: false,
                    message: 'Both entity and key are required query parameters',
                });
                return;
            }
            const target = String(entity).toLowerCase();
            const dimensionKey = String(key);
            const prefixStr = typeof prefix === 'string' ? prefix : undefined;
            const valueLimit = Math.min(Number(limit) || 50, 500);
            // Parse optional time range: JSON string { start: string, end: string }
            let dateRange;
            if (timeRange) {
                try {
                    const parsed = typeof timeRange === 'string' ? JSON.parse(timeRange) : timeRange;
                    if (parsed && typeof parsed.start === 'string' && typeof parsed.end === 'string') {
                        dateRange = { start: parsed.start, end: parsed.end };
                    }
                }
                catch {
                    // Ignore invalid timeRange; fall back to defaults in applyDefaults
                }
            }
            // Helper to resolve a dimension value from a row based on the key
            const resolveValue = (row) => {
                if (!row) {
                    return undefined;
                }
                // Try direct field using camelCase mapping from dot/underscore notation
                const toCamel = (str) => str
                    .split(/[._]/)
                    .map((part, idx) => idx === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1))
                    .join('');
                const candidates = [];
                candidates.push(dimensionKey);
                candidates.push(toCamel(dimensionKey));
                // Common special cases for IDs
                if (dimensionKey === 'session.id') {
                    candidates.push('sessionId');
                }
                else if (dimensionKey === 'trace.id') {
                    candidates.push('traceId');
                }
                else if (dimensionKey === 'span.id') {
                    candidates.push('spanId');
                }
                else if (dimensionKey === 'application.id') {
                    candidates.push('applicationId');
                }
                for (const field of candidates) {
                    if (field in row && row[field] !== undefined && row[field] !== null) {
                        return row[field];
                    }
                }
                // Look into resourceAttributes/spanAttributes maps for OTEL-style keys
                if (row.resourceAttributes && dimensionKey in row.resourceAttributes) {
                    return row.resourceAttributes[dimensionKey];
                }
                if (row.spanAttributes && dimensionKey in row.spanAttributes) {
                    return row.spanAttributes[dimensionKey];
                }
                return undefined;
            };
            // Fetch a sample of rows for the target entity
            let rows = [];
            const baseLimit = valueLimit * 10; // fetch more rows than needed for better coverage
            switch (target) {
                case 'session': {
                    const { filters: sessionFilters, limit: finalLimit, sorting } = TruPulseToolController.applyDefaults(dateRange ? { dateRange } : undefined, baseLimit, undefined, 'timestamp');
                    rows = await (0, clickhouseQueries_1.querySessions)({
                        projectId: undefined,
                        ...(sessionFilters || {}),
                        sorting,
                        limit: finalLimit,
                        offset: 0,
                    });
                    break;
                }
                case 'trace': {
                    const { filters: traceFilters, limit: finalLimit, sorting } = TruPulseToolController.applyDefaults(dateRange ? { dateRange } : undefined, baseLimit, undefined, 'timestamp');
                    rows = await (0, clickhouseQueries_1.queryTraces)({
                        projectId: undefined,
                        ...(traceFilters || {}),
                        sorting,
                        limit: finalLimit,
                        offset: 0,
                    });
                    break;
                }
                case 'span': {
                    const { filters: spanFilters, limit: finalLimit, sorting } = TruPulseToolController.applyDefaults(dateRange ? { dateRange } : undefined, baseLimit, undefined, 'timestamp');
                    rows = await (0, clickhouseQueries_1.querySpans)({
                        projectId: undefined,
                        ...(spanFilters || {}),
                        sorting,
                        limit: finalLimit,
                        offset: 0,
                    });
                    break;
                }
                default: {
                    res.status(400).json({
                        success: false,
                        message: `Unsupported entity for get-dimension-values: ${entity}. Supported entities: session, trace, span.`,
                    });
                    return;
                }
            }
            const valuesSet = new Set();
            for (const row of rows) {
                const raw = resolveValue(row);
                if (raw === undefined || raw === null) {
                    continue;
                }
                const asString = String(raw);
                if (prefixStr && !asString.toLowerCase().startsWith(prefixStr.toLowerCase())) {
                    continue;
                }
                valuesSet.add(asString);
                if (valuesSet.size >= valueLimit) {
                    break;
                }
            }
            res.json({
                success: true,
                data: {
                    entity: target,
                    key: dimensionKey,
                    values: Array.from(valuesSet),
                },
            });
        }
        catch (error) {
            logger_1.default.error(`get_dimension_values error: ${error}`);
            res.status(500).json({
                success: false,
                message: error instanceof Error ? error.message : 'Internal server error',
            });
        }
    }
    /**
     * GET /api/argus/tools/get-traces-by-ids?projectId=xxx&orgId=xxx&traceIds=xxx&limit=xxx
     * Get multiple traces by their IDs
     */
    static async getTracesByIds(req, res) {
        try {
            const { projectId, traceIds: traceIdsStr, limit: limitStr, filters: filtersStr } = req.query;
            if (!projectId) {
                res.status(400).json({
                    success: false,
                    message: 'projectId is required in query parameters',
                });
                return;
            }
            // Parse traceIds from query param (can be comma-separated or JSON array)
            let traceIds = [];
            if (traceIdsStr) {
                if (typeof traceIdsStr === 'string') {
                    // Try parsing as JSON first, then fall back to comma-separated
                    try {
                        traceIds = JSON.parse(traceIdsStr);
                    }
                    catch {
                        traceIds = traceIdsStr.split(',').map((id) => id.trim()).filter(Boolean);
                    }
                }
                else if (Array.isArray(traceIdsStr)) {
                    traceIds = traceIdsStr;
                }
            }
            if (!traceIds || traceIds.length === 0) {
                res.status(400).json({
                    success: false,
                    message: 'traceIds is required in query parameters (comma-separated or JSON array)',
                });
                return;
            }
            const limit = limitStr ? Number(limitStr) : 100;
            const filters = filtersStr
                ? typeof filtersStr === 'string'
                    ? JSON.parse(filtersStr)
                    : filtersStr
                : undefined;
            // Only use includeEvaluationResults from filters (no legacy include param)
            const includeEvaluationResults = filters?.includeEvaluationResults === true;
            const queryParams = {
                projectId: projectId,
                traceIds: traceIds.slice(0, limit),
                includeEvaluationResults,
                evalResultsFilters: filters?.evalResultsFilters,
            };
            const traces = await (0, clickhouseQueries_1.queryTraces)(queryParams);
            res.json({
                success: true,
                data: {
                    traces,
                    count: traces.length,
                },
            });
        }
        catch (error) {
            logger_1.default.error(`Get traces by IDs error: ${error}`);
            res.status(500).json({
                success: false,
                message: error instanceof Error ? error.message : 'Internal server error',
            });
        }
    }
    /**
     * 3.3 search_traces(filter, time_range, sort, page)
     * Aliases getTraces with page/pageSize semantics.
     * GET /api/argus/tools/search-traces
     */
    static async searchTraces(req, res) {
        try {
            const { projectId, filters: filtersStr, sorting: sortingStr, page: pageStr, pageSize: pageSizeStr, } = req.query;
            const page = pageStr ? Number(pageStr) : 1;
            const pageSize = pageSizeStr ? Number(pageSizeStr) : 100;
            const offset = (page - 1) * pageSize;
            const filters = filtersStr
                ? typeof filtersStr === 'string'
                    ? JSON.parse(filtersStr)
                    : filtersStr
                : undefined;
            const sorting = sortingStr
                ? typeof sortingStr === 'string'
                    ? JSON.parse(sortingStr)
                    : sortingStr
                : undefined;
            const { filters: finalFilters, limit: finalLimit, sorting: finalSorting } = TruPulseToolController.applyDefaults(filters, pageSize, sorting);
            // Only use includeEvaluationResults from filters (no legacy include param)
            const includeEvaluationResults = filters?.includeEvaluationResults === true;
            const evalResultsFilters = filters?.evalResultsFilters;
            const traceParams = {
                projectId: projectId,
                applicationId: finalFilters.applicationId,
                traceId: finalFilters.traceId,
                dateRange: finalFilters.dateRange,
                status: finalFilters.status,
                includeEvaluationResults,
                evalResultsFilters,
                sorting: finalSorting,
                limit: finalLimit,
                offset,
            };
            const [traces, total] = await Promise.all([
                (0, clickhouseQueries_1.queryTraces)(traceParams),
                (0, clickhouseQueries_1.countTraces)(traceParams),
            ]);
            res.json({
                success: true,
                data: {
                    traces,
                    page,
                    pageSize: finalLimit,
                    total,
                },
            });
        }
        catch (error) {
            logger_1.default.error(`search_traces error: ${error}`);
            res.status(500).json({
                success: false,
                message: error instanceof Error ? error.message : 'Internal server error',
            });
        }
    }
    /**
     * 3.3 get_trace(trace_id, include={span_tree,summary})
     * GET /api/argus/tools/get-trace
     */
    static async getTrace(req, res) {
        try {
            const { projectId, traceId, filters: filtersStr } = req.query;
            if (!traceId) {
                res.status(400).json({
                    success: false,
                    message: 'traceId is required in query parameters',
                });
                return;
            }
            // Optional filters may contain includeEvaluationResults and evalResultsFilters to control evaluation joins
            const filters = filtersStr
                ? typeof filtersStr === 'string'
                    ? JSON.parse(filtersStr)
                    : filtersStr
                : undefined;
            // Only use includeEvaluationResults from filters (no legacy include param)
            const includeEvaluationResults = filters?.includeEvaluationResults === true;
            const trace = await (0, clickhouseQueries_1.queryTraceById)({
                projectId: projectId,
                traceId: traceId,
                includeEvaluationResults,
                evalResultsFilters: filters?.evalResultsFilters,
            });
            if (!trace) {
                res.status(404).json({
                    success: false,
                    message: 'Trace not found',
                });
                return;
            }
            const timestamps = trace.spans.map((span) => new Date(span.timestamp));
            const startTime = new Date(Math.min(...timestamps.map((d) => d.getTime())));
            const endTime = new Date(Math.max(...timestamps.map((d) => d.getTime())));
            const result = {
                traceId,
                startTime,
                endTime,
                traceDurationMs: endTime.getTime() - startTime.getTime(),
                spanCount: trace.spans.length,
                applicationId: trace.resourceAttributes['service.name'],
                status: trace.spans.some((span) => span.status === 'STATUS_CODE_ERROR')
                    ? 'error'
                    : 'success',
                ...trace,
            };
            res.json({
                success: true,
                data: result,
            });
        }
        catch (error) {
            logger_1.default.error(`get_trace error: ${error}`);
            res.status(500).json({
                success: false,
                message: error instanceof Error ? error.message : 'Internal server error',
            });
        }
    }
    /**
     * 3.3 get_trace_span_tree(trace_id)
     * Lightweight wrapper that only returns the span tree.
     * GET /api/argus/tools/get-trace-span-tree
     */
    static async getTraceSpanTree(req, res) {
        try {
            const { projectId, traceId } = req.query;
            if (!traceId) {
                res.status(400).json({
                    success: false,
                    message: 'traceId is required in query parameters',
                });
                return;
            }
            const trace = await (0, clickhouseQueries_1.queryTraceById)({
                projectId: projectId,
                traceId: traceId,
            });
            if (!trace) {
                res.status(404).json({
                    success: false,
                    message: 'Trace not found',
                });
                return;
            }
            const nodes = trace.spans.map((span) => ({
                spanId: span.spanId,
                parentSpanId: span.parentSpanId,
                name: span.spanName,
                timestamp: span.timestamp,
                duration: span.duration,
                status: span.status,
            }));
            const byId = new Map();
            nodes.forEach((n) => byId.set(n.spanId, { ...n, children: [] }));
            const roots = [];
            byId.forEach((n) => {
                if (n.parentSpanId && byId.has(n.parentSpanId)) {
                    byId.get(n.parentSpanId).children.push(n);
                }
                else {
                    roots.push(n);
                }
            });
            res.json({
                success: true,
                data: roots,
            });
        }
        catch (error) {
            logger_1.default.error(`get_trace_span_tree error: ${error}`);
            res.status(500).json({
                success: false,
                message: error instanceof Error ? error.message : 'Internal server error',
            });
        }
    }
    /**
     * 3.3 get_trace_logs(trace_id, query?, limit?)
     * Not implemented (no logs table wired yet).
     */
    static async getTraceLogs(req, res) {
        res.status(501).json({
            success: false,
            message: 'get_trace_logs is not implemented yet.',
        });
    }
    /**
     * 3.4 search_spans(filter, time_range, sort, page)
     * Aliases getSpans with page/pageSize semantics.
     * GET /api/argus/tools/search-spans
     */
    static async searchSpans(req, res) {
        try {
            const { projectId, filters: filtersStr, sorting: sortingStr, page: pageStr, pageSize: pageSizeStr, } = req.query;
            const page = pageStr ? Number(pageStr) : 1;
            const pageSize = pageSizeStr ? Number(pageSizeStr) : 100;
            const offset = (page - 1) * pageSize;
            const filters = filtersStr
                ? typeof filtersStr === 'string'
                    ? JSON.parse(filtersStr)
                    : filtersStr
                : undefined;
            const sorting = sortingStr
                ? typeof sortingStr === 'string'
                    ? JSON.parse(sortingStr)
                    : sortingStr
                : undefined;
            const { filters: finalFilters, limit: finalLimit, sorting: finalSorting } = TruPulseToolController.applyDefaults(filters, pageSize, sorting);
            // Only use includeEvaluationResults from filters (no legacy include param)
            const includeEvaluationResults = filters?.includeEvaluationResults === true;
            const evalResultsFilters = filters?.evalResultsFilters;
            const spanParams = {
                projectId: projectId,
                applicationId: finalFilters.applicationId,
                spanId: finalFilters.spanId,
                traceId: finalFilters.traceId,
                dateRange: finalFilters.dateRange,
                status: finalFilters.status,
                includeEvaluationResults,
                evalResultsFilters,
                sorting: finalSorting,
                limit: finalLimit,
                offset,
            };
            const [spans, total] = await Promise.all([
                (0, clickhouseQueries_1.querySpans)(spanParams),
                (0, clickhouseQueries_1.countSpans)(spanParams),
            ]);
            res.json({
                success: true,
                data: {
                    spans,
                    page,
                    pageSize: finalLimit,
                    total,
                },
            });
        }
        catch (error) {
            logger_1.default.error(`search_spans error: ${error}`);
            res.status(500).json({
                success: false,
                message: error instanceof Error ? error.message : 'Internal server error',
            });
        }
    }
    /**
     * 3.4 get_span(span_id, include={events,links,attributes,stacktrace})
     * GET /api/argus/tools/get-span
     */
    static async getSpan(req, res) {
        try {
            const { projectId, spanId, filters: filtersStr } = req.query;
            if (!spanId) {
                res.status(400).json({
                    success: false,
                    message: 'spanId is required in query parameters',
                });
                return;
            }
            const filters = filtersStr
                ? typeof filtersStr === 'string'
                    ? JSON.parse(filtersStr)
                    : filtersStr
                : undefined;
            // Only use includeEvaluationResults from filters (no legacy include param)
            const includeEvaluationResults = filters?.includeEvaluationResults === true;
            const span = await (0, clickhouseQueries_1.querySpanById)({
                projectId: projectId,
                spanId: spanId,
                includeEvaluationResults,
                evalResultsFilters: filters?.evalResultsFilters,
            });
            if (!span) {
                res.status(404).json({
                    success: false,
                    message: 'Span not found',
                });
                return;
            }
            res.json({
                success: true,
                data: span,
            });
        }
        catch (error) {
            logger_1.default.error(`get_span error: ${error}`);
            res.status(500).json({
                success: false,
                message: error instanceof Error ? error.message : 'Internal server error',
            });
        }
    }
    /**
     * 3.4 get_span_events(span_id, event_name?)
     * GET /api/argus/tools/get-span-events
     */
    static async getSpanEvents(req, res) {
        try {
            const { projectId, spanId, eventName } = req.query;
            if (!spanId) {
                res.status(400).json({
                    success: false,
                    message: 'spanId is required in query parameters',
                });
                return;
            }
            const span = await (0, clickhouseQueries_1.querySpanById)({
                projectId: projectId,
                spanId: spanId,
                includeEvaluationResults: false,
            });
            if (!span) {
                res.status(404).json({
                    success: false,
                    message: 'Span not found',
                });
                return;
            }
            const names = span['Events.Name'] || span.eventsName || [];
            const timestamps = span['Events.Timestamp'] || span.eventsTimestamp || [];
            const attributes = span['Events.Attributes'] || span.eventsAttributes || [];
            const events = names.map((name, idx) => ({
                name,
                timestamp: timestamps[idx],
                attributes: attributes[idx],
            }));
            const filtered = eventName && typeof eventName === 'string'
                ? events.filter((e) => e.name === eventName)
                : events;
            res.json({
                success: true,
                data: filtered,
            });
        }
        catch (error) {
            logger_1.default.error(`get_span_events error: ${error}`);
            res.status(500).json({
                success: false,
                message: error instanceof Error ? error.message : 'Internal server error',
            });
        }
    }
    /**
     * 3.4 get_span_logs(span_id, limit?)
     * Not implemented (logs not wired yet).
     */
    static async getSpanLogs(req, res) {
        res.status(501).json({
            success: false,
            message: 'get_span_logs is not implemented yet.',
        });
    }
    /**
     * GET /api/argus/tools/get-spans-by-ids?projectId=xxx&orgId=xxx&spanIds=xxx&limit=xxx
     * Get multiple spans by their IDs
     */
    static async getSpansByIds(req, res) {
        try {
            const { projectId, spanIds: spanIdsStr, limit: limitStr, filters: filtersStr } = req.query;
            if (!projectId) {
                res.status(400).json({
                    success: false,
                    message: 'projectId is required in query parameters',
                });
                return;
            }
            // Parse spanIds from query param (can be comma-separated or JSON array)
            let spanIds = [];
            if (spanIdsStr) {
                if (typeof spanIdsStr === 'string') {
                    // Try parsing as JSON first, then fall back to comma-separated
                    try {
                        spanIds = JSON.parse(spanIdsStr);
                    }
                    catch {
                        spanIds = spanIdsStr.split(',').map((id) => id.trim()).filter(Boolean);
                    }
                }
                else if (Array.isArray(spanIdsStr)) {
                    spanIds = spanIdsStr;
                }
            }
            if (!spanIds || spanIds.length === 0) {
                res.status(400).json({
                    success: false,
                    message: 'spanIds is required in query parameters (comma-separated or JSON array)',
                });
                return;
            }
            const limit = limitStr ? Number(limitStr) : 100;
            const filters = filtersStr
                ? typeof filtersStr === 'string'
                    ? JSON.parse(filtersStr)
                    : filtersStr
                : undefined;
            // Only use includeEvaluationResults from filters (no legacy include param)
            const includeEvaluationResults = filters?.includeEvaluationResults === true;
            const queryParams = {
                projectId: projectId,
                spanIds: spanIds.slice(0, limit),
                includeEvaluationResults,
                evalResultsFilters: filters?.evalResultsFilters,
            };
            const spans = await (0, clickhouseQueries_1.querySpans)(queryParams);
            res.json({
                success: true,
                data: {
                    spans,
                    count: spans.length,
                },
            });
        }
        catch (error) {
            logger_1.default.error(`Get spans by IDs error: ${error}`);
            res.status(500).json({
                success: false,
                message: error instanceof Error ? error.message : 'Internal server error',
            });
        }
    }
    /**
     * GET /api/argus/tools/get-sessions?projectId=xxx&orgId=xxx&filters=xxx&sorting=xxx&limit=xxx&offset=xxx
     * Get sessions with filters (default: 30 days, limit 100)
     */
    static async getSessions(req, res) {
        try {
            const { projectId, filters: filtersStr, sorting: sortingStr, limit: limitStr, offset: offsetStr } = req.query;
            // Parse JSON strings from query params
            const filters = filtersStr ? (typeof filtersStr === 'string' ? JSON.parse(filtersStr) : filtersStr) : undefined;
            const sorting = sortingStr ? (typeof sortingStr === 'string' ? JSON.parse(sortingStr) : sortingStr) : undefined;
            const limit = limitStr ? Number(limitStr) : undefined;
            const offset = offsetStr ? Number(offsetStr) : 0;
            // Apply default filters
            const { filters: finalFilters, limit: finalLimit, sorting: finalSorting, dateRangeApplied } = TruPulseToolController.applyDefaults(filters, limit, sorting);
            if (!projectId) {
                res.status(400).json({
                    success: false,
                    message: 'projectId is required in query parameters',
                });
                return;
            }
            // Query ClickHouse
            const queryParams = {
                projectId: projectId,
                ...finalFilters,
                includeEvaluationResults: finalFilters?.includeEvaluationResults === true,
                evalResultsFilters: filters?.evalResultsFilters,
                sorting: finalSorting,
                limit: finalLimit,
                offset,
            };
            const sessions = await (0, clickhouseQueries_1.querySessions)(queryParams);
            res.json({
                success: true,
                data: {
                    sessions,
                    count: sessions.length,
                    total: sessions.length,
                    metadata: {
                        dateRange: {
                            ...finalFilters.dateRange,
                            applied: dateRangeApplied,
                        },
                        limit: finalLimit,
                        offset,
                        filters: finalFilters,
                        sorting: finalSorting,
                    },
                },
            });
        }
        catch (error) {
            logger_1.default.error(`Get sessions error: ${error}`);
            res.status(500).json({
                success: false,
                message: error instanceof Error ? error.message : 'Internal server error',
            });
        }
    }
    /**
     * GET /api/argus/tools/get-session-by-id?projectId=xxx&orgId=xxx&sessionId=xxx
     * Get all session records by ID (multiple records can exist for a single sessionId with different trace/span data)
     */
    static async getSessionById(req, res) {
        try {
            const { projectId, sessionId, filters: filtersStr } = req.query;
            if (!projectId) {
                res.status(400).json({
                    success: false,
                    message: 'projectId is required in query parameters',
                });
                return;
            }
            if (!sessionId) {
                res.status(400).json({
                    success: false,
                    message: 'sessionId is required in query parameters',
                });
                return;
            }
            // Parse includeEvaluationResults and evalResultsFilters if provided
            const filters = filtersStr ? (typeof filtersStr === 'string' ? JSON.parse(filtersStr) : filtersStr) : undefined;
            const sessions = await (0, clickhouseQueries_1.querySessionById)({
                projectId: projectId,
                sessionId: sessionId,
                includeEvaluationResults: filters?.includeEvaluationResults === true,
                evalResultsFilters: filters?.evalResultsFilters,
            });
            if (!sessions || sessions.length === 0) {
                res.status(404).json({
                    success: false,
                    message: 'Session not found',
                });
                return;
            }
            res.json({
                success: true,
                data: {
                    sessions, // Array of all session records for this sessionId
                    count: sessions.length,
                },
            });
        }
        catch (error) {
            logger_1.default.error(`Get session by ID error: ${error}`);
            res.status(500).json({
                success: false,
                message: error instanceof Error ? error.message : 'Internal server error',
            });
        }
    }
    /**
     * 3.2 search_sessions(filter, time_range, sort, page)
     * Aliases getSessions with page/pageSize semantics.
     * GET /api/argus/tools/search-sessions
     */
    static async searchSessions(req, res) {
        try {
            const { projectId, filters: filtersStr, sorting: sortingStr, page: pageStr, pageSize: pageSizeStr, } = req.query;
            const page = pageStr ? Number(pageStr) : 1;
            const pageSize = pageSizeStr ? Number(pageSizeStr) : 100;
            const offset = (page - 1) * pageSize;
            const filters = filtersStr
                ? typeof filtersStr === 'string'
                    ? JSON.parse(filtersStr)
                    : filtersStr
                : undefined;
            const sorting = sortingStr
                ? typeof sortingStr === 'string'
                    ? JSON.parse(sortingStr)
                    : sortingStr
                : undefined;
            const { filters: finalFilters, limit: finalLimit, sorting: finalSorting } = TruPulseToolController.applyDefaults(filters, pageSize, sorting, 'createdAt');
            // Only use includeEvaluationResults from filters (no legacy include param)
            const includeEvaluationResults = filters?.includeEvaluationResults === true;
            const evalResultsFilters = filters?.evalResultsFilters;
            const sessionParams = {
                projectId: projectId,
                applicationId: finalFilters.applicationId,
                sessionId: finalFilters.sessionId,
                dateRange: finalFilters.dateRange,
                status: finalFilters.status,
                includeEvaluationResults,
                evalResultsFilters,
                sorting: finalSorting,
                limit: finalLimit,
                offset,
            };
            const [sessions, total] = await Promise.all([
                (0, clickhouseQueries_1.querySessions)(sessionParams),
                (0, clickhouseQueries_1.countSessions)(sessionParams),
            ]);
            res.json({
                success: true,
                data: {
                    sessions,
                    page,
                    pageSize: finalLimit,
                    total,
                },
            });
        }
        catch (error) {
            logger_1.default.error(`search_sessions error: ${error}`);
            res.status(500).json({
                success: false,
                message: error instanceof Error ? error.message : 'Internal server error',
            });
        }
    }
    /**
     * 3.2 get_session(session_id, include={summary,timeline,traces,aggregates})
     * GET /api/argus/tools/get-session
     */
    static async getSession(req, res) {
        try {
            const { projectId, sessionId, include, filters: filtersStr } = req.query;
            if (!sessionId) {
                res.status(400).json({
                    success: false,
                    message: 'sessionId is required in query parameters',
                });
                return;
            }
            const filters = filtersStr
                ? typeof filtersStr === 'string'
                    ? JSON.parse(filtersStr)
                    : filtersStr
                : undefined;
            // Only use includeEvaluationResults from filters (no legacy include param)
            const includeEvaluationResults = filters?.includeEvaluationResults === true;
            const records = await (0, clickhouseQueries_1.querySessionById)({
                projectId: projectId,
                sessionId: sessionId,
                includeEvaluationResults,
                evalResultsFilters: filters?.evalResultsFilters,
            });
            if (!records || records.length === 0) {
                res.status(404).json({
                    success: false,
                    message: 'Session not found',
                });
                return;
            }
            const timestamps = records.map((r) => new Date(r.Timestamp));
            const startTime = new Date(Math.min(...timestamps.map((d) => d.getTime())));
            const endTime = new Date(Math.max(...timestamps.map((d) => d.getTime())));
            const traceIds = new Set(records.map((r) => r.TraceId).filter(Boolean));
            const spanIds = new Set(records.map((r) => r.SpanId).filter(Boolean));
            const summary = {
                sessionId,
                startTime,
                endTime,
                durationMs: endTime.getTime() - startTime.getTime(),
                traceCount: traceIds.size,
                spanCount: spanIds.size,
                applicationId: records[0]?.ServiceName,
                status: records.some((r) => r.StatusCode === 'STATUS_CODE_ERROR')
                    ? 'failed'
                    : 'completed',
            };
            let timeline;
            if (typeof include === 'string' && include.split(',').includes('timeline')) {
                timeline = records
                    .map((r) => ({
                    timestamp: r.Timestamp,
                    traceId: r.TraceId,
                    spanId: r.SpanId,
                    name: r.SpanName,
                    status: r.StatusCode,
                }))
                    .sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
            }
            res.json({
                success: true,
                data: {
                    summary,
                    timeline: timeline ?? null,
                    // traces / aggregates can be built by separate APIs
                },
            });
        }
        catch (error) {
            logger_1.default.error(`get_session error: ${error}`);
            res.status(500).json({
                success: false,
                message: error instanceof Error ? error.message : 'Internal server error',
            });
        }
    }
    /**
     * 3.2 get_session_traces(session_id, filter?, page?)
     * GET /api/argus/tools/get-session-traces
     */
    static async getSessionTraces(req, res) {
        try {
            const { projectId, sessionId } = req.query;
            if (!sessionId) {
                res.status(400).json({
                    success: false,
                    message: 'sessionId is required in query parameters',
                });
                return;
            }
            const sessionRecords = await (0, clickhouseQueries_1.querySessionById)({
                projectId: projectId,
                sessionId: sessionId,
            });
            if (!sessionRecords || sessionRecords.length === 0) {
                res.status(404).json({
                    success: false,
                    message: 'Session not found',
                });
                return;
            }
            const traceIds = Array.from(new Set(sessionRecords.map((r) => r.TraceId).filter(Boolean)));
            if (traceIds.length === 0) {
                res.json({
                    success: true,
                    data: {
                        traces: [],
                    },
                });
                return;
            }
            const traces = await (0, clickhouseQueries_1.queryTraces)({
                projectId: projectId,
                traceIds,
                limit: traceIds.length,
                offset: 0,
            });
            res.json({
                success: true,
                data: {
                    traces,
                },
            });
        }
        catch (error) {
            logger_1.default.error(`get_session_traces error: ${error}`);
            res.status(500).json({
                success: false,
                message: error instanceof Error ? error.message : 'Internal server error',
            });
        }
    }
    /**
     * 3.2 get_session_graph(session_id)
     * Returns a simple DAG-ish representation built from spans in the session.
     * GET /api/argus/tools/get-session-graph
     */
    static async getSessionGraph(req, res) {
        try {
            const { projectId, sessionId } = req.query;
            if (!sessionId) {
                res.status(400).json({
                    success: false,
                    message: 'sessionId is required in query parameters',
                });
                return;
            }
            const sessionRecords = await (0, clickhouseQueries_1.querySessionById)({
                projectId: projectId,
                sessionId: sessionId,
                includeEvaluationResults: false,
            });
            if (!sessionRecords || sessionRecords.length === 0) {
                res.status(404).json({
                    success: false,
                    message: 'Session not found',
                });
                return;
            }
            const nodes = sessionRecords.map((r) => ({
                spanId: r.SpanId,
                parentSpanId: r.ParentSpanId,
                traceId: r.TraceId,
                name: r.SpanName,
                timestamp: r.Timestamp,
                status: r.StatusCode,
            }));
            const edges = nodes
                .filter((n) => n.parentSpanId)
                .map((n) => ({
                from: n.parentSpanId,
                to: n.spanId,
            }));
            res.json({
                success: true,
                data: {
                    nodes,
                    edges,
                },
            });
        }
        catch (error) {
            logger_1.default.error(`get_session_graph error: ${error}`);
            res.status(500).json({
                success: false,
                message: error instanceof Error ? error.message : 'Internal server error',
            });
        }
    }
    /**
     * GET /api/argus/tools/get-sessions-by-ids?projectId=xxx&orgId=xxx&sessionIds=xxx&limit=xxx
     * Get multiple sessions by their IDs
     */
    static async getSessionsByIds(req, res) {
        try {
            const { projectId, sessionIds: sessionIdsStr, limit: limitStr, include, filters: filtersStr } = req.query;
            if (!projectId) {
                res.status(400).json({
                    success: false,
                    message: 'projectId is required in query parameters',
                });
                return;
            }
            // Parse sessionIds from query param (can be comma-separated or JSON array)
            let sessionIds = [];
            if (sessionIdsStr) {
                if (typeof sessionIdsStr === 'string') {
                    // Try parsing as JSON first, then fall back to comma-separated
                    try {
                        sessionIds = JSON.parse(sessionIdsStr);
                    }
                    catch {
                        sessionIds = sessionIdsStr.split(',').map((id) => id.trim()).filter(Boolean);
                    }
                }
                else if (Array.isArray(sessionIdsStr)) {
                    sessionIds = sessionIdsStr;
                }
            }
            if (!sessionIds || sessionIds.length === 0) {
                res.status(400).json({
                    success: false,
                    message: 'sessionIds is required in query parameters (comma-separated or JSON array)',
                });
                return;
            }
            const limit = limitStr ? Number(limitStr) : 100;
            const filters = filtersStr
                ? typeof filtersStr === 'string'
                    ? JSON.parse(filtersStr)
                    : filtersStr
                : undefined;
            // Check for legacy 'include' parameter (can be comma-separated) or new 'includeEvaluationResults' in filters
            const includeSet = new Set((typeof include === 'string' ? include.split(',') : include || []));
            const includeEvaluationResults = filters?.includeEvaluationResults === true || includeSet.has('evalResults');
            const queryParams = {
                projectId: projectId,
                sessionIds: sessionIds.slice(0, limit),
                includeEvaluationResults,
                evalResultsFilters: filters?.evalResultsFilters,
            };
            const sessions = await (0, clickhouseQueries_1.querySessions)(queryParams);
            res.json({
                success: true,
                data: {
                    sessions,
                    count: sessions.length,
                },
            });
        }
        catch (error) {
            logger_1.default.error(`Get sessions by IDs error: ${error}`);
            res.status(500).json({
                success: false,
                message: error instanceof Error ? error.message : 'Internal server error',
            });
        }
    }
}
exports.TruPulseToolController = TruPulseToolController;
//# sourceMappingURL=truPulseToolController.js.map