import { apiClient } from './api'; export interface MonitoringSchedule { _id: string; rut: string; companyName?: string; frequency: 'minute' | 'daily' | 'weekly' | 'monthly'; isActive: boolean; lastExecuted?: string; nextExecution: string; executionCount: number; lastExecutionStatus?: 'success' | 'error' | 'pending'; lastExecutionError?: string; createdAt: string; updatedAt: string; tenantId?: { _id: string; name: string; }; } export interface CreateMonitoringScheduleRequest { rut: string; frequency: 'minute' | 'daily' | 'weekly' | 'monthly'; companyName?: string; } export interface UpdateMonitoringScheduleRequest { frequency?: 'minute' | 'daily' | 'weekly' | 'monthly'; isActive?: boolean; companyName?: string; } export interface MonitoringSchedulesResponse { schedules: MonitoringSchedule[]; total: number; pages: number; currentPage: number; } export interface MonitoringStatistics { tenantStats: Array<{ tenantId: string; tenantName: string; totalSchedules: number; activeSchedules: number; inactiveSchedules: number; lastExecutionCounts: { success: number; error: number; pending: number; }; }>; globalStats: { totalSchedules: number; activeSchedules: number; totalExecutions: number; successfulExecutions: number; failedExecutions: number; }; } export interface RiskChangeNotification { _id: string; rut: string; tenantId: string; scheduleId: string; companyName?: string; previousRisk: any; newRisk: any; changeDetectedAt: string; notificationSent: boolean; notificationMethod: string; createdAt: string; updatedAt: string; } export interface MonitoringExecution { _id: string; scheduleId: string; rut: string; tenantId: string; executedAt: string; status: 'success' | 'error' | 'pending' | 'running'; startedAt: string; completedAt?: string; duration?: number; error?: string; errorDetails?: { code?: string; message?: string; stack?: string; }; riskAssessment?: any; previousRisk?: any; riskChanged: boolean; sheriffLogId?: string; executionDetails: { requestData?: any; responseData?: any; apiCalls?: Array<{ endpoint: string; method: string; timestamp: string; duration: number; status: number; error?: string; }>; processingSteps?: Array<{ step: string; timestamp: string; duration: number; status: 'success' | 'error' | 'skipped'; details?: any; }>; }; metadata?: { userAgent?: string; ipAddress?: string; triggeredBy: 'schedule' | 'manual' | 'api'; triggeredByUserId?: string; }; createdAt: string; updatedAt: string; } export interface MonitoringExecutionsResponse { executions: MonitoringExecution[]; total: number; pages: number; currentPage: number; } class MonitoringService { /** * Get monitoring schedules for the current tenant */ async getMonitoringSchedules(page = 1, limit = 10): Promise { const response = await apiClient.get( `/monitoring?page=${page}&limit=${limit}` ); return response.data; } /** * Get all monitoring schedules across all tenants (superuser only) */ async getAllMonitoringSchedules(page = 1, limit = 10): Promise { const response = await apiClient.get( `/monitoring/all?page=${page}&limit=${limit}` ); return response.data; } /** * Get a specific monitoring schedule by ID */ async getMonitoringSchedule(scheduleId: string): Promise { // Validate schedule ID if (!scheduleId || scheduleId === 'undefined' || scheduleId.trim() === '') { throw new Error('Schedule ID is required and cannot be undefined or empty'); } // Validate ObjectId format (24 hex characters) if (!/^[0-9a-fA-F]{24}$/.test(scheduleId)) { throw new Error('Schedule ID must be a valid 24-character hex string'); } const response = await apiClient.get(`/monitoring/${scheduleId}`); return response.data; } /** * Create a new monitoring schedule */ async createMonitoringSchedule(data: CreateMonitoringScheduleRequest): Promise { const response = await apiClient.post('/monitoring', data); return response.data; } /** * Update an existing monitoring schedule */ async updateMonitoringSchedule( scheduleId: string, data: UpdateMonitoringScheduleRequest ): Promise { // Validate schedule ID if (!scheduleId || scheduleId === 'undefined' || scheduleId.trim() === '') { throw new Error('Schedule ID is required and cannot be undefined or empty'); } // Validate ObjectId format (24 hex characters) if (!/^[0-9a-fA-F]{24}$/.test(scheduleId)) { throw new Error('Schedule ID must be a valid 24-character hex string'); } const response = await apiClient.put(`/monitoring/${scheduleId}`, data); return response.data; } /** * Delete a monitoring schedule */ async deleteMonitoringSchedule(scheduleId: string): Promise<{ message: string }> { // Validate schedule ID if (!scheduleId || scheduleId === 'undefined' || scheduleId.trim() === '') { throw new Error('Schedule ID is required and cannot be undefined or empty'); } // Validate ObjectId format (24 hex characters) if (!/^[0-9a-fA-F]{24}$/.test(scheduleId)) { throw new Error('Schedule ID must be a valid 24-character hex string'); } const response = await apiClient.delete<{ message: string }>(`/monitoring/${scheduleId}`); return response.data; } /** * Manually trigger a monitoring execution */ async triggerMonitoring(scheduleId: string): Promise<{ message: string }> { // Validate schedule ID if (!scheduleId || scheduleId === 'undefined' || scheduleId.trim() === '') { throw new Error('Schedule ID is required and cannot be undefined or empty'); } // Validate ObjectId format (24 hex characters) if (!/^[0-9a-fA-F]{24}$/.test(scheduleId)) { throw new Error('Schedule ID must be a valid 24-character hex string'); } const response = await apiClient.post<{ message: string }>(`/monitoring/${scheduleId}/trigger`); return response.data; } /** * Get monitoring statistics (superuser only) */ async getMonitoringStatistics(): Promise { const response = await apiClient.get('/monitoring/statistics'); return response.data; } /** * Get risk change notifications for the current tenant */ async getRiskChangeNotifications(page = 1, limit = 10): Promise<{ notifications: RiskChangeNotification[]; total: number; pages: number; currentPage: number; }> { const response = await apiClient.get( `/monitoring/risk-changes?page=${page}&limit=${limit}` ); return response.data; } /** * Mark a risk change notification as read */ async markRiskChangeNotificationAsRead(notificationId: string): Promise<{ message: string }> { const response = await apiClient.patch<{ message: string }>( `/monitoring/risk-changes/${notificationId}/read` ); return response.data; } /** * Get monitoring execution history for a specific schedule */ /** * Get monitoring execution history for a schedule */ async getMonitoringExecutions( scheduleId: string, page = 1, limit = 20 ): Promise { const response = await apiClient.get( `/monitoring/${scheduleId}/executions?page=${page}&limit=${limit}` ); return response.data; } /** * Get specific monitoring execution details */ async getMonitoringExecution(executionId: string): Promise { const response = await apiClient.get( `/monitoring/executions/${executionId}` ); return response.data; } /** * Get monitoring execution history for a schedule (legacy method for backward compatibility) */ async getMonitoringHistory( scheduleId: string, page = 1, limit = 10 ): Promise<{ executions: Array<{ _id: string; scheduleId: string; executedAt: string; status: 'success' | 'error' | 'pending'; error?: string; riskAssessment?: any; previousRisk?: any; }>; total: number; pages: number; currentPage: number; }> { // Use the new endpoint but transform the response for backward compatibility const response = await this.getMonitoringExecutions(scheduleId, page, limit); return { executions: response.executions.map(exec => ({ _id: exec._id, scheduleId: exec.scheduleId, executedAt: exec.executedAt, status: exec.status === 'running' ? 'pending' : exec.status as 'success' | 'error' | 'pending', error: exec.error, riskAssessment: exec.riskAssessment, previousRisk: exec.previousRisk })), total: response.total, pages: response.pages, currentPage: response.currentPage }; } /** * Setup monitoring (legacy function for backward compatibility) */ async setupMonitoring(data: { rut: string; frequency: string }): Promise<{ message: string }> { const response = await apiClient.post<{ message: string }>('/monitoring', { rut: data.rut, frequency: data.frequency as 'minute' | 'daily' | 'weekly' | 'monthly' }); return response.data; } /** * Pause a monitoring schedule */ async pauseMonitoringSchedule(scheduleId: string): Promise { return this.updateMonitoringSchedule(scheduleId, { isActive: false }); } /** * Resume a monitoring schedule */ async resumeMonitoringSchedule(scheduleId: string): Promise { return this.updateMonitoringSchedule(scheduleId, { isActive: true }); } /** * Get monitoring schedule by RUT */ async getMonitoringScheduleByRut(rut: string): Promise { try { const response = await apiClient.get(`/monitoring/rut/${rut}`); return response.data; } catch (error: any) { if (error.response?.status === 404) { return null; } throw error; } } /** * Bulk create monitoring schedules */ async bulkCreateMonitoringSchedules( schedules: CreateMonitoringScheduleRequest[] ): Promise<{ created: MonitoringSchedule[]; errors: Array<{ rut: string; error: string }>; }> { const response = await apiClient.post('/monitoring/bulk', { schedules }); return response.data; } /** * Export monitoring schedules to CSV */ async exportMonitoringSchedules(): Promise { const response = await apiClient.get('/monitoring/export', { responseType: 'blob' }); return response.data; } } export const monitoringService = new MonitoringService(); export default monitoringService;