Source: api/features/ZptAPI.js

import BaseAPI from '../common/BaseAPI.js';
import { v4 as uuidv4 } from 'uuid';
import NavigationEndpoint from '../../zpt/api/NavigationEndpoint.js';
import CorpuscleSelector from '../../zpt/selection/CorpuscleSelector.js';
import TiltProjector from '../../zpt/selection/TiltProjector.js';
import CorpuscleTransformer from '../../zpt/transform/CorpuscleTransformer.js';
import ParameterValidator from '../../zpt/parameters/ParameterValidator.js';
import ParameterNormalizer from '../../zpt/parameters/ParameterNormalizer.js';
import ZoomLevelMapper from '../../zpt/selection/ZoomLevelMapper.js';
import PanDomainFilter from '../../zpt/selection/PanDomainFilter.js';

// ZPT Ontology Integration
import { NamespaceUtils, getSPARQLPrefixes } from '../../zpt/ontology/ZPTNamespaces.js';
import { ZPTDataFactory } from '../../zpt/ontology/ZPTDataFactory.js';

/**
 * ZPT API handler for Zero-Point Traversal navigation operations
 * Integrates ZPT services into the main API server following BaseAPI patterns
 */
export default class ZptAPI extends BaseAPI {
    constructor(config = {}) {
        super(config);
        
        // Configuration
        this.maxConcurrentRequests = config.maxConcurrentRequests || 10;
        this.requestTimeoutMs = config.requestTimeoutMs || 120000; // 2 minutes
        this.defaultMaxTokens = config.defaultMaxTokens || 4000;
        this.supportedFormats = config.supportedFormats || ['json', 'markdown', 'structured'];
        this.supportedTokenizers = config.supportedTokenizers || ['cl100k_base', 'p50k_base', 'claude', 'llama'];
        
        // Core dependencies (will be injected)
        this.llmHandler = null;
        this.embeddingHandler = null;
        this.sparqlStore = null;
        this.ragnoCorpus = null; // Will need to be provided or created
        
        // ZPT components
        this.navigationEndpoint = null;
        this.corpuscleSelector = null;
        this.tiltProjector = null;
        this.transformer = null;
        this.validator = new ParameterValidator();
        this.normalizer = new ParameterNormalizer();
        this.zoomMapper = null;
        this.panFilter = null;
        
        // ZPT Ontology Integration
        this.zptDataFactory = null;
        this.navigationGraph = config.navigationGraph || 'http://purl.org/stuff/navigation';
        
        // Request tracking
        this.activeRequests = new Map();
        
        // Metrics tracking
        this.metrics = {
            navigations: 0,
            previews: 0,
            transformations: 0,
            errors: 0,
            avgProcessingTime: 0,
            cacheHits: 0,
            cacheMisses: 0
        };
    }

    async initialize() {
        await super.initialize();
        
        // Get dependencies from registry
        const registry = this.config.registry;
        if (!registry) {
            throw new Error('Registry is required for ZptAPI');
        }
        
        try {
            // Get core handlers
            this.llmHandler = registry.get('llm');
            this.embeddingHandler = registry.get('embedding');
            
            // Get storage - try to get SPARQL store if available
            try {
                const memoryManager = registry.get('memory');
                if (memoryManager && memoryManager.storage) {
                    this.sparqlStore = memoryManager.storage;
                }
            } catch (error) {
                this.logger.warn('SPARQL store not available, ZPT functionality will be limited');
            }
            
            // Create mock ragno corpus if SPARQL store available
            // In real implementation, this would query the actual Ragno corpus
            if (this.sparqlStore) {
                this.ragnoCorpus = await this._createCorpusInterface();
            }
            
            // Initialize ZPT components
            await this._initializeZPTComponents();
            
            // Initialize ZPT ontology integration
            this.zptDataFactory = new ZPTDataFactory({
                navigationGraph: this.navigationGraph
            });
            
            this.logger.info('ZptAPI initialized successfully with ontology integration');
        } catch (error) {
            this.logger.error('Failed to initialize ZptAPI:', error);
            throw error;
        }
    }

    /**
     * Initialize ZPT-specific components
     */
    async _initializeZPTComponents() {
        // Initialize zoom level mapper
        this.zoomMapper = new ZoomLevelMapper({
            sparqlStore: this.sparqlStore,
            defaultLevel: 'unit'
        });
        
        // Initialize pan domain filter
        this.panFilter = new PanDomainFilter({
            sparqlStore: this.sparqlStore,
            embeddingHandler: this.embeddingHandler
        });
        
        // Initialize corpuscle selector (main selection component)
        if (this.ragnoCorpus) {
            this.corpuscleSelector = new CorpuscleSelector(this.ragnoCorpus, {
                sparqlStore: this.sparqlStore,
                embeddingHandler: this.embeddingHandler,
                maxResults: 1000,
                enableCaching: true,
                enableZPTStorage: true,
                navigationGraph: this.navigationGraph
            });
        }
        
        // Initialize tilt projector (representation transformation)
        this.tiltProjector = new TiltProjector({
            embeddingHandler: this.embeddingHandler,
            textAnalyzer: this.llmHandler
        });
        
        // Initialize transformer (output formatting)
        this.transformer = new CorpuscleTransformer({
            enableCaching: true,
            enableMetrics: true,
            defaultFormat: 'structured',
            supportedFormats: this.supportedFormats
        });
        
        // Initialize navigation endpoint (orchestrator)
        if (this.corpuscleSelector && this.tiltProjector && this.transformer) {
            this.navigationEndpoint = new NavigationEndpoint({
                maxConcurrentRequests: this.maxConcurrentRequests,
                requestTimeoutMs: this.requestTimeoutMs,
                enableCaching: true,
                enableMetrics: true
            });
            
            // Initialize with dependencies
            await this.navigationEndpoint.initialize({
                ragnoCorpus: this.ragnoCorpus,
                sparqlStore: this.sparqlStore,
                embeddingHandler: this.embeddingHandler,
                llmHandler: this.llmHandler
            });
        }
    }

    /**
     * Execute a ZPT operation
     */
    async executeOperation(operation, params) {
        this._validateParams(params);
        
        const start = Date.now();
        const requestId = uuidv4();
        
        // Track active request
        this.activeRequests.set(requestId, {
            operation,
            startTime: start,
            params: { ...params, sensitiveData: '[REDACTED]' }
        });
        
        try {
            let result;
            
            switch (operation) {
                case 'navigate':
                    result = await this.navigate(params, requestId);
                    break;
                case 'preview':
                    result = await this.preview(params, requestId);
                    break;
                case 'options':
                    result = await this.getOptions(params, requestId);
                    break;
                case 'schema':
                    result = await this.getSchema(params, requestId);
                    break;
                case 'health':
                    result = await this.getHealth(params, requestId);
                    break;
                // ZPT Ontology Integration operations
                case 'convertParams':
                    result = await this.convertParams(params, requestId);
                    break;
                case 'storeSession':
                    result = await this.storeSession(params, requestId);
                    break;
                case 'getSessions':
                    result = await this.getSessions(params, requestId);
                    break;
                case 'getSession':
                    result = await this.getSession(params, requestId);
                    break;
                case 'getViews':
                    result = await this.getViews(params, requestId);
                    break;
                case 'getView':
                    result = await this.getView(params, requestId);
                    break;
                case 'analyzeNavigation':
                    result = await this.analyzeNavigation(params, requestId);
                    break;
                case 'getOntologyTerms':
                    result = await this.getOntologyTerms(params, requestId);
                    break;
                case 'validateOntology':
                    result = await this.validateOntology(params, requestId);
                    break;
                default:
                    throw new Error(`Unknown operation: ${operation}`);
            }
            
            const duration = Date.now() - start;
            this.metrics.avgProcessingTime = this._updateAverage(this.metrics.avgProcessingTime, duration);
            this._emitMetric(`zpt.operation.${operation}.duration`, duration);
            this._emitMetric(`zpt.operation.${operation}.count`, 1);
            
            return {
                ...result,
                requestId,
                processingTime: duration
            };
        } catch (error) {
            this.metrics.errors++;
            this._emitMetric(`zpt.operation.${operation}.errors`, 1);
            this.logger.error(`ZPT operation ${operation} failed:`, error);
            throw error;
        } finally {
            this.activeRequests.delete(requestId);
        }
    }

    /**
     * Main navigation operation
     */
    async navigate(params, requestId) {
        if (!this.navigationEndpoint) {
            throw new Error('Navigation endpoint not available without proper corpus/SPARQL setup');
        }
        
        this.logger.info(`Processing navigation request`, { requestId, params: this._sanitizeParams(params) });
        
        try {
            // Validate ZPT parameters
            const validationResult = this.validator.validate(params);
            if (!validationResult.valid) {
                throw new Error(`Parameter validation failed: ${validationResult.message}`);
            }
            
            // Normalize parameters
            const normalizedParams = this.normalizer.normalize(params);
            
            // Execute navigation pipeline via NavigationEndpoint
            const result = await this.navigationEndpoint.handleNavigate({
                body: normalizedParams,
                path: '/api/navigate',
                method: 'POST'
            }, requestId);
            
            this.metrics.navigations++;
            
            return {
                success: true,
                navigation: result.navigation,
                content: result.content,
                metadata: result.metadata,
                diagnostics: result.diagnostics
            };
        } catch (error) {
            // If NavigationEndpoint fails, try fallback implementation
            if (this.corpuscleSelector && this.tiltProjector && this.transformer) {
                return await this._executeNavigationFallback(params, requestId);
            }
            throw error;
        }
    }

    /**
     * Navigation preview (limited processing)
     */
    async preview(params, requestId) {
        this.logger.info(`Processing preview request`, { requestId });
        
        try {
            // Validate parameters
            const validationResult = this.validator.validate(params);
            if (!validationResult.valid) {
                throw new Error(`Parameter validation failed: ${validationResult.message}`);
            }
            
            const normalizedParams = this.normalizer.normalize(params);
            
            if (this.navigationEndpoint) {
                // Use NavigationEndpoint if available
                const result = await this.navigationEndpoint.handlePreview({
                    body: normalizedParams,
                    path: '/api/navigate/preview',
                    method: 'POST'
                }, requestId);
                
                this.metrics.previews++;
                return result;
            } else if (this.corpuscleSelector) {
                // Fallback to direct corpuscle selection
                const selectionResult = await this.corpuscleSelector.select(normalizedParams);
                
                this.metrics.previews++;
                return {
                    success: true,
                    preview: true,
                    navigation: normalizedParams,
                    summary: {
                        corpuscleCount: selectionResult.corpuscles?.length || 0,
                        selectionTime: selectionResult.metadata?.selectionTime || 0,
                        estimatedTokens: this._estimateTokens(selectionResult.corpuscles || []),
                        complexity: selectionResult.metadata?.complexity || 'unknown'
                    },
                    corpuscles: (selectionResult.corpuscles || []).slice(0, 5) // Preview first 5
                };
            } else {
                throw new Error('Preview not available without corpus selector');
            }
        } catch (error) {
            this.logger.error('Preview failed:', error);
            throw new Error(`Preview failed: ${error.message}`);
        }
    }

    /**
     * Get available navigation options
     */
    async getOptions(params = {}, requestId) {
        return {
            success: true,
            options: {
                zoomLevels: ['entity', 'unit', 'text', 'community', 'corpus'],
                tiltRepresentations: ['embedding', 'keywords', 'graph', 'temporal'],
                outputFormats: this.supportedFormats,
                encodingStrategies: ['structured', 'compact', 'inline'],
                maxTokenLimits: {
                    'gpt-4': 128000,
                    'gpt-3.5-turbo': 16385,
                    'claude-3-opus': 200000,
                    'claude-3-sonnet': 200000,
                    'llama': 4096,
                    'mistral': 32000
                },
                supportedTokenizers: this.supportedTokenizers,
                panDomains: {
                    topic: 'Subject area filtering',
                    entity: 'Specific entity constraints',
                    temporal: 'Time-based boundaries',
                    geographic: 'Location-based limits'
                },
                transformOptions: {
                    chunkStrategies: ['semantic', 'fixed', 'adaptive'],
                    metadataInclusion: ['minimal', 'standard', 'comprehensive'],
                    compressionLevels: ['none', 'light', 'aggressive']
                }
            }
        };
    }

    /**
     * Get parameter schema and examples
     */
    async getSchema(params = {}, requestId) {
        return {
            success: true,
            schema: this.validator.getSchema(),
            examples: {
                basic: {
                    zoom: 'unit',
                    tilt: 'keywords',
                    transform: { 
                        maxTokens: this.defaultMaxTokens, 
                        format: 'structured' 
                    }
                },
                advanced: {
                    zoom: 'entity',
                    pan: {
                        topic: 'machine-learning',
                        temporal: {
                            start: '2024-01-01',
                            end: '2024-12-31'
                        }
                    },
                    tilt: 'embedding',
                    transform: {
                        maxTokens: 8000,
                        format: 'json',
                        includeMetadata: true,
                        chunkStrategy: 'semantic',
                        tokenizer: 'cl100k_base'
                    }
                },
                minimal: {
                    zoom: 'text'
                }
            },
            documentation: {
                zoom: 'Abstraction level: entity (specific elements), unit (semantic chunks), text (full content), community (summaries), corpus (overview)',
                pan: 'Domain filtering: topic (subject constraints), entity (specific scope), temporal (time bounds), geographic (location limits)',
                tilt: 'Representation format: embedding (vectors), keywords (terms), graph (relationships), temporal (sequences)',
                transform: 'Output options: maxTokens (budget), format (structure), tokenizer (model), metadata (context)'
            },
            defaults: this.validator.getDefaults()
        };
    }

    /**
     * Get ZPT system health
     */
    async getHealth(params = {}, requestId) {
        const health = {
            status: 'healthy',
            components: {
                validator: { status: !!this.validator ? 'healthy' : 'unavailable' },
                normalizer: { status: !!this.normalizer ? 'healthy' : 'unavailable' },
                corpuscleSelector: { status: !!this.corpuscleSelector ? 'healthy' : 'unavailable' },
                tiltProjector: { status: !!this.tiltProjector ? 'healthy' : 'unavailable' },
                transformer: { status: !!this.transformer ? 'healthy' : 'unavailable' },
                navigationEndpoint: { status: !!this.navigationEndpoint ? 'healthy' : 'unavailable' },
                sparqlStore: { status: !!this.sparqlStore ? 'healthy' : 'unavailable' },
                embeddingHandler: { status: !!this.embeddingHandler ? 'healthy' : 'unavailable' },
                llmHandler: { status: !!this.llmHandler ? 'healthy' : 'unavailable' }
            },
            capabilities: {
                fullNavigation: !!this.navigationEndpoint,
                corpuscleSelection: !!this.corpuscleSelector,
                tiltProjection: !!this.tiltProjector,
                transformation: !!this.transformer,
                sparqlIntegration: !!this.sparqlStore,
                embeddingSearch: !!this.embeddingHandler,
                llmAnalysis: !!this.llmHandler
            },
            activeRequests: this.activeRequests.size,
            metrics: this.metrics
        };
        
        // Determine overall health
        const criticalComponents = ['validator', 'normalizer'];
        const hasCriticalIssues = criticalComponents.some(comp => 
            health.components[comp].status !== 'healthy'
        );
        
        const hasCapabilities = health.capabilities.corpuscleSelection || 
                               health.capabilities.fullNavigation;
        
        if (hasCriticalIssues) {
            health.status = 'unhealthy';
        } else if (!hasCapabilities) {
            health.status = 'degraded';
        }
        
        return {
            success: true,
            health
        };
    }

    /**
     * Fallback navigation implementation when NavigationEndpoint is not available
     */
    async _executeNavigationFallback(params, requestId) {
        this.logger.info('Using fallback navigation implementation', { requestId });
        
        const normalizedParams = this.normalizer.normalize(params);
        
        // Stage 1: Corpuscle Selection
        const selectionStart = Date.now();
        const selectionResult = await this.corpuscleSelector.select(normalizedParams);
        const selectionTime = Date.now() - selectionStart;
        
        // Stage 2: Tilt Projection
        const projectionStart = Date.now();
        const projectionResult = await this.tiltProjector.project(
            selectionResult.corpuscles || [],
            normalizedParams.tilt || 'keywords',
            {
                embeddingHandler: this.embeddingHandler,
                textAnalyzer: this.llmHandler
            }
        );
        const projectionTime = Date.now() - projectionStart;
        
        // Stage 3: Transformation
        const transformStart = Date.now();
        const transformResult = await this.transformer.transform(
            projectionResult,
            selectionResult,
            normalizedParams.transform || {}
        );
        const transformTime = Date.now() - transformStart;
        
        this.metrics.navigations++;
        
        return {
            success: true,
            navigation: normalizedParams,
            content: transformResult.content,
            metadata: {
                pipeline: {
                    selectionTime,
                    projectionTime,
                    transformTime,
                    totalTime: selectionTime + projectionTime + transformTime
                },
                selection: selectionResult.metadata,
                projection: projectionResult.metadata,
                transformation: transformResult.metadata,
                fallbackUsed: true
            },
            diagnostics: {
                requestId,
                stages: ['selection', 'projection', 'transformation'],
                performance: { selectionTime, projectionTime, transformTime },
                implementation: 'fallback'
            }
        };
    }

    /**
     * Create a basic corpus interface for ZPT components
     */
    async _createCorpusInterface() {
        // This is a simplified interface - in real implementation would query Ragno corpus
        return {
            // Mock corpus interface
            query: async (criteria) => {
                this.logger.debug('Querying corpus with criteria:', criteria);
                return []; // Empty results for now
            },
            getMetadata: async () => ({
                totalUnits: 0,
                totalEntities: 0,
                lastUpdated: new Date().toISOString()
            })
        };
    }

    /**
     * Estimate tokens for corpuscles (simple heuristic)
     */
    _estimateTokens(corpuscles) {
        return corpuscles.reduce((total, corpuscle) => {
            const content = Object.values(corpuscle.content || {}).join(' ');
            return total + Math.ceil(content.length / 4); // Rough 4 chars per token
        }, 0);
    }

    /**
     * Update running average
     */
    _updateAverage(currentAvg, newValue) {
        const count = this.metrics.navigations + this.metrics.previews + 1;
        return (currentAvg * (count - 1) + newValue) / count;
    }

    /**
     * Sanitize parameters for logging (remove sensitive data)
     */
    _sanitizeParams(params) {
        const sanitized = { ...params };
        // Remove or mask any sensitive fields
        if (sanitized.apiKey) sanitized.apiKey = '[REDACTED]';
        if (sanitized.token) sanitized.token = '[REDACTED]';
        return sanitized;
    }

    /**
     * Store interaction (inherited from BaseAPI)
     */
    async storeInteraction(interaction) {
        // ZPT doesn't directly store interactions, but processes navigation requests
        throw new Error('Use navigate operation to process ZPT navigation requests');
    }

    /**
     * Retrieve interactions (inherited from BaseAPI)
     */
    async retrieveInteractions(query) {
        // Return navigation history if implemented
        throw new Error('Navigation history retrieval not yet implemented');
    }

    /**
     * Get ZPT API metrics
     */
    async getMetrics() {
        const baseMetrics = await super.getMetrics();
        
        return {
            ...baseMetrics,
            zpt: this.metrics,
            components: {
                navigationEndpoint: !!this.navigationEndpoint,
                corpuscleSelector: !!this.corpuscleSelector,
                tiltProjector: !!this.tiltProjector,
                transformer: !!this.transformer,
                sparqlStore: !!this.sparqlStore,
                ragnoCorpus: !!this.ragnoCorpus
            },
            activeRequests: Array.from(this.activeRequests.values())
        };
    }

    async shutdown() {
        this.logger.info('Shutting down ZptAPI');
        
        // Wait for active requests to complete (with timeout)
        const shutdownTimeout = 30000; // 30 seconds
        const startTime = Date.now();
        
        while (this.activeRequests.size > 0 && Date.now() - startTime < shutdownTimeout) {
            await new Promise(resolve => setTimeout(resolve, 1000));
        }
        
        if (this.activeRequests.size > 0) {
            this.logger.warn(`Forced shutdown with ${this.activeRequests.size} active requests`);
        }
        
        // Shutdown navigation endpoint if available
        if (this.navigationEndpoint && typeof this.navigationEndpoint.shutdown === 'function') {
            await this.navigationEndpoint.shutdown();
        }
        
        // Cleanup other components
        if (this.transformer && typeof this.transformer.dispose === 'function') {
            await this.transformer.dispose();
        }
        
        if (this.corpuscleSelector && typeof this.corpuscleSelector.dispose === 'function') {
            await this.corpuscleSelector.dispose();
        }
        
        this.activeRequests.clear();
        
        await super.shutdown();
    }

    // ========================
    // ZPT Ontology Integration Methods
    // ========================

    /**
     * Convert string-based navigation parameters to ZPT ontology URIs
     */
    async convertParams(params, requestId) {
        this.logger.info(`Converting parameters to ZPT URIs`, { requestId });
        
        try {
            const converted = {};
            
            // Convert zoom level
            if (params.zoom) {
                const zoomURI = NamespaceUtils.resolveStringToURI('zoom', params.zoom);
                if (zoomURI) {
                    converted.zoomURI = zoomURI.value;
                }
            }
            
            // Convert tilt representation
            if (params.tilt) {
                const tiltURI = NamespaceUtils.resolveStringToURI('tilt', params.tilt);
                if (tiltURI) {
                    converted.tiltURI = tiltURI.value;
                }
            }
            
            // Convert pan domains
            if (params.pan?.domains) {
                converted.panURIs = params.pan.domains
                    .map(domain => NamespaceUtils.resolveStringToURI('pan', domain))
                    .filter(uri => uri !== null)
                    .map(uri => uri.value);
            }
            
            return {
                originalParams: params,
                convertedParams: converted,
                conversionCount: Object.keys(converted).length
            };
        } catch (error) {
            this.logger.error('Parameter conversion failed:', error);
            throw new Error(`Parameter conversion failed: ${error.message}`);
        }
    }

    /**
     * Store a navigation session with RDF metadata
     */
    async storeSession(params, requestId) {
        this.logger.info(`Storing navigation session`, { requestId });
        
        if (!this.zptDataFactory) {
            throw new Error('ZPT Data Factory not available');
        }
        
        try {
            const sessionConfig = {
                agentURI: params.agentURI || `http://example.org/agents/api_user_${requestId}`,
                startTime: new Date(params.startTime || Date.now()),
                purpose: params.purpose || 'API navigation session',
                userAgent: params.userAgent || 'Semem ZPT API',
                sessionType: params.sessionType || 'api'
            };
            
            const session = this.zptDataFactory.createNavigationSession(sessionConfig);
            
            // Store in SPARQL if available
            if (this.sparqlStore && params.storeInSPARQL !== false) {
                await this._storeRDFInSPARQL(session.quads, 'navigation session');
            }
            
            return {
                sessionURI: session.uri.value,
                quadsGenerated: session.quads.length,
                storedInSPARQL: !!this.sparqlStore,
                metadata: sessionConfig
            };
        } catch (error) {
            this.logger.error('Session storage failed:', error);
            throw new Error(`Session storage failed: ${error.message}`);
        }
    }

    /**
     * Get list of navigation sessions
     */
    async getSessions(params, requestId) {
        this.logger.info(`Retrieving navigation sessions`, { requestId });
        
        if (!this.sparqlStore) {
            throw new Error('SPARQL store required for session retrieval');
        }
        
        try {
            const query = getSPARQLPrefixes(['zpt', 'prov']) + `
                SELECT ?session ?purpose ?startTime ?agent WHERE {
                    GRAPH <${this.navigationGraph}> {
                        ?session a zpt:NavigationSession ;
                            zpt:hasPurpose ?purpose ;
                            prov:startedAtTime ?startTime ;
                            prov:wasAssociatedWith ?agent .
                    }
                }
                ORDER BY DESC(?startTime)
                LIMIT ${params.limit || 20}
            `;
            
            const result = await this._executeSPARQLQuery(query);
            const sessions = this._parseSPARQLResults(result);
            
            return {
                sessions: sessions.map(s => ({
                    sessionURI: s.session,
                    purpose: s.purpose,
                    startTime: s.startTime,
                    agentURI: s.agent
                })),
                totalCount: sessions.length
            };
        } catch (error) {
            this.logger.error('Session retrieval failed:', error);
            throw new Error(`Session retrieval failed: ${error.message}`);
        }
    }

    /**
     * Get specific navigation session details
     */
    async getSession(params, requestId) {
        this.logger.info(`Retrieving session details`, { requestId, sessionId: params.sessionId });
        
        if (!this.sparqlStore) {
            throw new Error('SPARQL store required for session retrieval');
        }
        
        if (!params.sessionId) {
            throw new Error('Session ID is required');
        }
        
        try {
            const query = getSPARQLPrefixes(['zpt', 'prov']) + `
                SELECT ?property ?value WHERE {
                    GRAPH <${this.navigationGraph}> {
                        <${params.sessionId}> ?property ?value .
                    }
                }
            `;
            
            const result = await this._executeSPARQLQuery(query);
            const properties = this._parseSPARQLResults(result);
            
            // Get associated views
            const viewsQuery = getSPARQLPrefixes(['zpt']) + `
                SELECT ?view ?query ?timestamp WHERE {
                    GRAPH <${this.navigationGraph}> {
                        ?view a zpt:NavigationView ;
                            zpt:partOfSession <${params.sessionId}> ;
                            zpt:answersQuery ?query ;
                            zpt:navigationTimestamp ?timestamp .
                    }
                }
                ORDER BY ?timestamp
            `;
            
            const viewsResult = await this._executeSPARQLQuery(viewsQuery);
            const views = this._parseSPARQLResults(viewsResult);
            
            return {
                sessionURI: params.sessionId,
                properties: properties.reduce((acc, p) => {
                    acc[p.property.split('/').pop()] = p.value;
                    return acc;
                }, {}),
                associatedViews: views.map(v => ({
                    viewURI: v.view,
                    query: v.query,
                    timestamp: v.timestamp
                })),
                viewCount: views.length
            };
        } catch (error) {
            this.logger.error('Session detail retrieval failed:', error);
            throw new Error(`Session detail retrieval failed: ${error.message}`);
        }
    }

    /**
     * Get list of navigation views
     */
    async getViews(params, requestId) {
        this.logger.info(`Retrieving navigation views`, { requestId });
        
        if (!this.sparqlStore) {
            throw new Error('SPARQL store required for view retrieval');
        }
        
        try {
            const query = getSPARQLPrefixes(['zpt']) + `
                SELECT ?view ?query ?session ?timestamp WHERE {
                    GRAPH <${this.navigationGraph}> {
                        ?view a zpt:NavigationView ;
                            zpt:answersQuery ?query ;
                            zpt:partOfSession ?session ;
                            zpt:navigationTimestamp ?timestamp .
                    }
                }
                ORDER BY DESC(?timestamp)
                LIMIT ${params.limit || 20}
            `;
            
            const result = await this._executeSPARQLQuery(query);
            const views = this._parseSPARQLResults(result);
            
            return {
                views: views.map(v => ({
                    viewURI: v.view,
                    query: v.query,
                    sessionURI: v.session,
                    timestamp: v.timestamp
                })),
                totalCount: views.length
            };
        } catch (error) {
            this.logger.error('View retrieval failed:', error);
            throw new Error(`View retrieval failed: ${error.message}`);
        }
    }

    /**
     * Get specific navigation view details
     */
    async getView(params, requestId) {
        this.logger.info(`Retrieving view details`, { requestId, viewId: params.viewId });
        
        if (!this.sparqlStore) {
            throw new Error('SPARQL store required for view retrieval');
        }
        
        if (!params.viewId) {
            throw new Error('View ID is required');
        }
        
        try {
            const query = getSPARQLPrefixes(['zpt']) + `
                SELECT ?property ?value WHERE {
                    GRAPH <${this.navigationGraph}> {
                        <${params.viewId}> ?property ?value .
                    }
                }
            `;
            
            const result = await this._executeSPARQLQuery(query);
            const properties = this._parseSPARQLResults(result);
            
            return {
                viewURI: params.viewId,
                properties: properties.reduce((acc, p) => {
                    acc[p.property.split('/').pop()] = p.value;
                    return acc;
                }, {}),
                propertyCount: properties.length
            };
        } catch (error) {
            this.logger.error('View detail retrieval failed:', error);
            throw new Error(`View detail retrieval failed: ${error.message}`);
        }
    }

    /**
     * Analyze navigation patterns and effectiveness
     */
    async analyzeNavigation(params, requestId) {
        this.logger.info(`Analyzing navigation patterns`, { requestId });
        
        if (!this.sparqlStore) {
            throw new Error('SPARQL store required for navigation analysis');
        }
        
        try {
            // Get navigation statistics
            const statsQuery = getSPARQLPrefixes(['zpt']) + `
                SELECT 
                    (COUNT(DISTINCT ?session) as ?sessionCount)
                    (COUNT(DISTINCT ?view) as ?viewCount)
                    (AVG(STRLEN(?query)) as ?avgQueryLength)
                WHERE {
                    GRAPH <${this.navigationGraph}> {
                        ?session a zpt:NavigationSession .
                        ?view a zpt:NavigationView ;
                            zpt:partOfSession ?session ;
                            zpt:answersQuery ?query .
                    }
                }
            `;
            
            const statsResult = await this._executeSPARQLQuery(statsQuery);
            const stats = this._parseSPARQLResults(statsResult)[0] || {};
            
            // Get zoom level distribution
            const zoomQuery = getSPARQLPrefixes(['zpt']) + `
                SELECT ?zoomLevel (COUNT(*) as ?count) WHERE {
                    GRAPH <${this.navigationGraph}> {
                        ?view a zpt:NavigationView ;
                            zpt:hasZoomState ?zoomState .
                        ?zoomState zpt:atZoomLevel ?zoomLevel .
                    }
                }
                GROUP BY ?zoomLevel
                ORDER BY DESC(?count)
            `;
            
            const zoomResult = await this._executeSPARQLQuery(zoomQuery);
            const zoomDistribution = this._parseSPARQLResults(zoomResult);
            
            return {
                navigationStats: {
                    totalSessions: parseInt(stats.sessionCount || 0),
                    totalViews: parseInt(stats.viewCount || 0),
                    averageQueryLength: parseFloat(stats.avgQueryLength || 0).toFixed(1)
                },
                zoomLevelDistribution: zoomDistribution.map(z => ({
                    zoomLevel: z.zoomLevel.split('/').pop(),
                    count: parseInt(z.count),
                    uri: z.zoomLevel
                })),
                analysisTimestamp: new Date().toISOString()
            };
        } catch (error) {
            this.logger.error('Navigation analysis failed:', error);
            throw new Error(`Navigation analysis failed: ${error.message}`);
        }
    }

    /**
     * Get available ZPT ontology terms
     */
    async getOntologyTerms(params, requestId) {
        this.logger.info(`Retrieving ZPT ontology terms`, { requestId });
        
        try {
            const ontologyTerms = {
                zoomLevels: [
                    { string: 'entity', uri: 'http://purl.org/stuff/zpt/EntityLevel', description: 'Individual concepts and named entities' },
                    { string: 'unit', uri: 'http://purl.org/stuff/zpt/UnitLevel', description: 'Semantic units and text passages' },
                    { string: 'text', uri: 'http://purl.org/stuff/zpt/TextLevel', description: 'Raw text elements and fragments' },
                    { string: 'community', uri: 'http://purl.org/stuff/zpt/CommunityLevel', description: 'Topic clusters and concept groups' },
                    { string: 'corpus', uri: 'http://purl.org/stuff/zpt/CorpusLevel', description: 'Entire corpus view' },
                    { string: 'micro', uri: 'http://purl.org/stuff/zpt/MicroLevel', description: 'Sub-entity components' }
                ],
                tiltProjections: [
                    { string: 'keywords', uri: 'http://purl.org/stuff/zpt/KeywordProjection', description: 'Keyword-based analysis and matching' },
                    { string: 'embedding', uri: 'http://purl.org/stuff/zpt/EmbeddingProjection', description: 'Vector similarity using embeddings' },
                    { string: 'graph', uri: 'http://purl.org/stuff/zpt/GraphProjection', description: 'Graph structure and connectivity analysis' },
                    { string: 'temporal', uri: 'http://purl.org/stuff/zpt/TemporalProjection', description: 'Time-based organization and sequencing' }
                ],
                panDomains: [
                    { string: 'topic', uri: 'http://purl.org/stuff/zpt/TopicDomain', description: 'Subject/topic constraints' },
                    { string: 'entity', uri: 'http://purl.org/stuff/zpt/EntityDomain', description: 'Entity-based filtering' },
                    { string: 'temporal', uri: 'http://purl.org/stuff/zpt/TemporalDomain', description: 'Time period constraints' },
                    { string: 'geographic', uri: 'http://purl.org/stuff/zpt/GeospatialDomain', description: 'Location-based filtering' }
                ],
                namespaces: {
                    zpt: 'http://purl.org/stuff/zpt/',
                    prov: 'http://www.w3.org/ns/prov#',
                    ragno: 'http://purl.org/stuff/ragno/'
                }
            };
            
            return {
                ontologyTerms,
                totalTerms: ontologyTerms.zoomLevels.length + ontologyTerms.tiltProjections.length + ontologyTerms.panDomains.length
            };
        } catch (error) {
            this.logger.error('Ontology terms retrieval failed:', error);
            throw new Error(`Ontology terms retrieval failed: ${error.message}`);
        }
    }

    /**
     * Validate parameters against ZPT ontology
     */
    async validateOntology(params, requestId) {
        this.logger.info(`Validating ontology parameters`, { requestId });
        
        try {
            const validation = {
                valid: true,
                errors: [],
                warnings: [],
                convertedParams: {}
            };
            
            // Validate zoom parameter
            if (params.zoom) {
                const zoomURI = NamespaceUtils.resolveStringToURI('zoom', params.zoom);
                if (zoomURI) {
                    validation.convertedParams.zoomURI = zoomURI.value;
                } else {
                    validation.valid = false;
                    validation.errors.push(`Invalid zoom level: ${params.zoom}`);
                }
            }
            
            // Validate tilt parameter
            if (params.tilt) {
                const tiltURI = NamespaceUtils.resolveStringToURI('tilt', params.tilt);
                if (tiltURI) {
                    validation.convertedParams.tiltURI = tiltURI.value;
                } else {
                    validation.valid = false;
                    validation.errors.push(`Invalid tilt projection: ${params.tilt}`);
                }
            }
            
            // Validate pan domains
            if (params.pan?.domains) {
                const panURIs = [];
                const invalidDomains = [];
                
                params.pan.domains.forEach(domain => {
                    const domainURI = NamespaceUtils.resolveStringToURI('pan', domain);
                    if (domainURI) {
                        panURIs.push(domainURI.value);
                    } else {
                        invalidDomains.push(domain);
                    }
                });
                
                if (panURIs.length > 0) {
                    validation.convertedParams.panURIs = panURIs;
                }
                
                if (invalidDomains.length > 0) {
                    validation.warnings.push(`Unknown pan domains: ${invalidDomains.join(', ')}`);
                }
            }
            
            return {
                validation,
                isValid: validation.valid,
                errorCount: validation.errors.length,
                warningCount: validation.warnings.length
            };
        } catch (error) {
            this.logger.error('Ontology validation failed:', error);
            throw new Error(`Ontology validation failed: ${error.message}`);
        }
    }

    // ========================
    // Helper Methods for RDF Operations
    // ========================

    /**
     * Store RDF quads in SPARQL endpoint
     */
    async _storeRDFInSPARQL(quads, description) {
        if (!this.sparqlStore) {
            throw new Error('SPARQL store not available');
        }
        
        try {
            const triples = quads.map(quad => {
                const obj = this._formatRDFObject(quad.object);
                return `        <${quad.subject.value}> <${quad.predicate.value}> ${obj} .`;
            }).join('\n');
            
            const insertQuery = getSPARQLPrefixes(['zpt', 'prov']) + `
                INSERT DATA {
                    GRAPH <${this.navigationGraph}> {
${triples}
                    }
                }
            `;
            
            await this._executeSPARQLUpdate(insertQuery);
            this.logger.info(`Stored ${quads.length} RDF quads for ${description}`);
        } catch (error) {
            this.logger.error(`Failed to store RDF quads for ${description}:`, error);
            throw error;
        }
    }

    /**
     * Format RDF object for SPARQL
     */
    _formatRDFObject(object) {
        if (object.termType === 'Literal') {
            let formatted = `"${object.value.replace(/"/g, '\\"')}"`;
            if (object.datatype) {
                formatted += `^^<${object.datatype.value}>`;
            } else if (object.language) {
                formatted += `@${object.language}`;
            }
            return formatted;
        } else {
            return `<${object.value}>`;
        }
    }

    /**
     * Execute SPARQL query
     */
    async _executeSPARQLQuery(query) {
        if (!this.sparqlStore || !this.sparqlStore._executeSparqlQuery) {
            throw new Error('SPARQL query execution not available');
        }
        
        return await this.sparqlStore._executeSparqlQuery(
            query,
            this.sparqlStore.endpoint.query
        );
    }

    /**
     * Execute SPARQL update
     */
    async _executeSPARQLUpdate(update) {
        if (!this.sparqlStore || !this.sparqlStore._executeSparqlUpdate) {
            throw new Error('SPARQL update execution not available');
        }
        
        return await this.sparqlStore._executeSparqlUpdate(
            update,
            this.sparqlStore.endpoint.update
        );
    }

    /**
     * Parse SPARQL results to simple objects
     */
    _parseSPARQLResults(result) {
        if (!result.results || !result.results.bindings) {
            return [];
        }
        
        return result.results.bindings.map(binding => {
            const obj = {};
            for (const [key, value] of Object.entries(binding)) {
                obj[key] = value.value;
            }
            return obj;
        });
    }
}