import { z } from 'zod';
import { mcpDb } from '../db/supabase.js';
import { ROIEngine } from '../core/calculators/roi-engine.js';
import { FINANCIAL_CONSTANTS } from '../core/calculators/financial-utils.js';
import { ProjectCreateSchema } from '../schemas/project.js';
import { UseCaseCreateSchema } from '../schemas/use-case.js';
import { structuredLogger, PerformanceTracker, createToolLogger } from '../utils/structured-logger.js';
import { DatabaseError, CalculationError, ValidationError, ConfigurationError } from '../utils/errors.js';
import { validateUseCase, validateFinancialAmount, validateMonths } from '../utils/validators.js';
import { DutchBenchmarkValidator } from '../services/dutch-benchmark-validator.js';
// Enhanced Transaction manager with correlation IDs
class TransactionManager {
private logger: ReturnType<typeof createToolLogger>;
private rollbackStack: Array<() => Promise<void>> = [];
constructor(correlationId: string) {
this.logger = createToolLogger('TransactionManager', correlationId);
}
async executeWithRollback<T>(
operation: () => Promise<T>,
rollbackOperation: () => Promise<void>,
operationName: string
): Promise<T> {
const tracker = new PerformanceTracker(
`transaction.${operationName}`,
this.logger.correlationId
);
try {
this.logger.debug(`Executing operation: ${operationName}`);
const result = await operation();
this.rollbackStack.push(rollbackOperation);
tracker.end(true);
return result;
} catch (error) {
tracker.error(error as Error);
structuredLogger.logStructuredError(error as Error, {
component: 'TransactionManager',
operation: operationName,
correlationId: this.logger.correlationId
});
throw error;
}
}
async rollbackAll(): Promise<void> {
const tracker = new PerformanceTracker(
'transaction.rollback',
this.logger.correlationId
);
this.logger.info('Starting rollback', { operations: this.rollbackStack.length });
// Execute rollbacks in reverse order
while (this.rollbackStack.length > 0) {
const rollback = this.rollbackStack.pop()!;
try {
await rollback();
tracker.checkpoint(`Rollback ${this.rollbackStack.length + 1} completed`);
} catch (error) {
this.logger.error('Rollback operation failed', error as Error);
// Continue with other rollbacks even if one fails
}
}
tracker.end(true);
}
}
// Enhanced Retry manager with correlation IDs
class RetryManager {
private logger: ReturnType<typeof createToolLogger>;
constructor(correlationId: string) {
this.logger = createToolLogger('RetryManager', correlationId);
}
async retry<T>(
operation: () => Promise<T>,
options: {
maxAttempts?: number;
backoffMs?: number;
operationName: string;
} = { maxAttempts: 3, backoffMs: 1000, operationName: 'operation' }
): Promise<T> {
const { maxAttempts = 3, backoffMs = 1000, operationName } = options;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
const tracker = new PerformanceTracker(
`retry.${operationName}.attempt${attempt}`,
this.logger.correlationId
);
try {
this.logger.debug(`Attempt ${attempt} of ${maxAttempts} for ${operationName}`);
const result = await operation();
tracker.end(true);
return result;
} catch (error) {
tracker.error(error as Error);
if (attempt === maxAttempts) {
this.logger.error(`All ${maxAttempts} attempts failed for ${operationName}`, error as Error);
throw error;
}
const delay = backoffMs * Math.pow(2, attempt - 1); // Exponential backoff
this.logger.warn(`Attempt ${attempt} failed, retrying in ${delay}ms`, {
error: (error as Error).message,
nextDelay: delay
});
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw new Error('Retry logic failed unexpectedly');
}
}
// Main predict ROI function with correlation ID support
export async function predictROIWithCorrelation(
params: any,
correlationId?: string
): Promise<any> {
// Create main logger with correlation ID
const logger = createToolLogger('predict-roi', correlationId);
const tracker = new PerformanceTracker('predict-roi.total', logger.correlationId);
logger.info('Starting ROI prediction', {
hasProject: !!params.project,
useCaseCount: params.use_cases?.length || 0,
enableBenchmarks: params.enable_benchmarks
});
const transaction = new TransactionManager(logger.correlationId);
const retryManager = new RetryManager(logger.correlationId);
try {
// Step 1: Validate input
tracker.checkpoint('validation-start');
const validatedParams = await validateInput(params, logger);
tracker.checkpoint('validation-complete');
// Step 2: Create project
tracker.checkpoint('project-creation-start');
const projectId = await transaction.executeWithRollback(
async () => {
const result = await retryManager.retry(
() => createProject(validatedParams, logger),
{ operationName: 'create-project' }
);
return result;
},
async () => {
logger.debug('Rolling back project creation', { projectId });
await mcpDb.from('projects').delete().eq('id', projectId);
},
'create-project'
);
tracker.checkpoint('project-creation-complete');
// Step 3: Create use cases
tracker.checkpoint('use-cases-creation-start');
const useCaseResults = await Promise.all(
validatedParams.use_cases.map((useCase: any, index: number) =>
transaction.executeWithRollback(
async () => {
const childLogger = logger.child(`use-case-${index}`, {
useCaseName: useCase.name
});
return createUseCase(projectId, useCase, childLogger);
},
async () => {
logger.debug('Rolling back use case creation', {
useCaseName: useCase.name
});
},
`create-use-case-${index}`
)
)
);
tracker.checkpoint('use-cases-creation-complete');
// Step 4: Run calculations
tracker.checkpoint('calculations-start');
const calculations = await performCalculations(
projectId,
validatedParams,
useCaseResults,
logger
);
tracker.checkpoint('calculations-complete');
// Step 5: Apply Dutch validation if enabled
if (validatedParams.enable_benchmarks) {
tracker.checkpoint('dutch-validation-start');
const validationLogger = logger.child('dutch-validation');
const validator = new DutchBenchmarkValidator(process.env.PERPLEXITY_API_KEY!);
const validation = await validator.validateProjectInputs({
industry: validatedParams.project.industry,
useCases: validatedParams.use_cases,
implementationCosts: validatedParams.implementation_costs,
timelineMonths: validatedParams.timeline_months
});
validationLogger.info('Dutch validation completed', {
isValid: validation.isValid,
issueCount: validation.validationIssues.length
});
tracker.checkpoint('dutch-validation-complete');
}
// Success!
tracker.end(true);
logger.info('ROI prediction completed successfully', {
projectId,
totalInvestment: calculations.financial_metrics.total_investment,
expectedROI: calculations.financial_metrics.expected_roi,
paybackPeriod: calculations.financial_metrics.payback_period_months
});
return calculations;
} catch (error) {
tracker.error(error as Error);
// Log structured error with full context
structuredLogger.logStructuredError(error as Error, {
component: 'predict-roi',
operation: 'predict-roi',
correlationId: logger.correlationId,
params: {
projectName: params.project?.name,
useCaseCount: params.use_cases?.length
}
});
// Attempt rollback
logger.error('ROI prediction failed, initiating rollback', error as Error);
await transaction.rollbackAll();
throw error;
}
}
// Helper functions with logger support
async function validateInput(
params: any,
logger: ReturnType<typeof createToolLogger>
): Promise<any> {
const validationLogger = logger.child('validation');
validationLogger.debug('Validating input parameters');
try {
// Validation logic here
return params; // Simplified for example
} catch (error) {
validationLogger.error('Input validation failed', error as Error);
throw new ValidationError('Invalid input parameters', { params });
}
}
async function createProject(
params: any,
logger: ReturnType<typeof createToolLogger>
): Promise<string> {
const dbLogger = structuredLogger.createDb('insert', 'projects', logger.correlationId);
dbLogger.debug('Creating project', { projectName: params.project.name });
// Database operation here
return 'project-id'; // Simplified for example
}
async function createUseCase(
projectId: string,
useCase: any,
logger: ReturnType<typeof createToolLogger>
): Promise<any> {
logger.debug('Creating use case', {
projectId,
useCaseName: useCase.name
});
// Database operation here
return { id: 'use-case-id', ...useCase }; // Simplified for example
}
async function performCalculations(
projectId: string,
params: any,
useCaseResults: any[],
logger: ReturnType<typeof createToolLogger>
): Promise<any> {
const calcLogger = logger.child('calculations');
const tracker = new PerformanceTracker(
'roi-calculations',
logger.correlationId
);
calcLogger.info('Starting ROI calculations', {
useCaseCount: useCaseResults.length
});
try {
// Calculation logic here
tracker.checkpoint('npv-calculation');
tracker.checkpoint('irr-calculation');
tracker.checkpoint('payback-calculation');
tracker.checkpoint('monte-carlo-simulation');
tracker.end(true);
return {
financial_metrics: {
total_investment: 100000,
expected_roi: 150,
payback_period_months: 18
}
};
} catch (error) {
tracker.error(error as Error);
throw error;
}
}