import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
import express, { Application } from 'express';
import request from 'supertest';
import { setupMetricsAPI } from '../../api/metrics.js';
import { PerformanceMonitor } from '../../../utils/performance-monitor.js';

// Mock the PerformanceMonitor
vi.mock('../../../utils/performance-monitor.js', () => ({
  PerformanceMonitor: {
    getInstance: vi.fn()
  }
}));

describe('Metrics API', () => {
  let app: Application;
  let mockPerformanceMonitor: any;

  beforeEach(() => {
    app = express();
    app.use(express.json());

    // Create mock performance monitor
    mockPerformanceMonitor = {
      getSystemMetrics: vi.fn(),
      getToolMetrics: vi.fn(),
      getQualityMetrics: vi.fn(),
      getPerformanceAlerts: vi.fn(),
      generateAlerts: vi.fn(), // Add missing method
      recordToolExecution: vi.fn(),
      recordModulePerformance: vi.fn(),
      recordAPICall: vi.fn(),
      exportMetrics: vi.fn(),
      recordQualityEvaluation: vi.fn(),
      getModuleMetrics: vi.fn(),
      analyzeBottlenecks: vi.fn(),
      generatePerformanceReport: vi.fn(),
      setAlertThreshold: vi.fn(),
      clearMetrics: vi.fn()
    };

    // Mock getInstance to return our mock
    (PerformanceMonitor.getInstance as any).mockReturnValue(mockPerformanceMonitor);

    // Setup the API with exportEnabled parameter
    setupMetricsAPI(app, mockPerformanceMonitor, true);
  });

  afterEach(() => {
    vi.clearAllMocks();
  });

  describe('GET /api/metrics/overview', () => {
    it('should return system metrics successfully', async () => {
      const mockSystemMetrics = {
        cpu: { usage: 45.5, count: 8 },
        memory: { used: 1024, total: 8192, percentage: 12.5 },
        uptime: 3600,
        timestamp: new Date().toISOString()
      };

      mockPerformanceMonitor.getSystemMetrics.mockResolvedValue(mockSystemMetrics);

      const response = await request(app).get('/api/metrics/overview');

      expect(response.status).toBe(200);
      expect(response.body).toEqual({
        success: true,
        data: mockSystemMetrics,
        timestamp: expect.any(String)
      });
      expect(mockPerformanceMonitor.getSystemMetrics).toHaveBeenCalled();
    });

    it('should handle errors gracefully', async () => {
      mockPerformanceMonitor.getSystemMetrics.mockRejectedValue(new Error('System error'));

      const response = await request(app).get('/api/metrics/overview');

      expect(response.status).toBe(500);
      expect(response.body).toEqual({
        success: false,
        error: 'Failed to fetch system metrics',
        message: 'System error'
      });
    });
  });

  describe('GET /api/metrics/tools', () => {
    it('should return tool metrics with filters', async () => {
      const mockToolMetrics = [
        {
          toolName: 'create_epic',
          executions: 10,
          avgResponseTime: 150,
          successRate: 90,
          lastExecuted: new Date().toISOString()
        }
      ];

      mockPerformanceMonitor.getToolMetrics.mockResolvedValue(mockToolMetrics);

      const response = await request(app)
        .get('/api/metrics/tools')
        .query({ toolName: 'create_epic', timeRange: '24h' });

      expect(response.status).toBe(200);
      expect(response.body.success).toBe(true);
      expect(response.body.data).toEqual({
        toolName: 'create_epic',
        metrics: mockToolMetrics,
        timeRange: '24h'
      });
      expect(mockPerformanceMonitor.getToolMetrics).toHaveBeenCalledWith('create_epic');
    });
  });

  describe('GET /api/metrics/quality', () => {
    it('should return quality metrics', async () => {
      const mockQualityMetrics = {
        testCoverage: 80,
        codeQuality: 85,
        buildSuccess: 95,
        deploymentFrequency: 5
      };

      mockPerformanceMonitor.getQualityMetrics.mockResolvedValue(mockQualityMetrics);

      const response = await request(app).get('/api/metrics/quality');

      expect(response.status).toBe(200);
      expect(response.body.data).toEqual({
        toolName: 'all',
        timeRange: '7d',
        qualityMetrics: mockQualityMetrics
      });
    });
  });

  describe('GET /api/metrics/alerts', () => {
    it('should return performance alerts filtered by severity', async () => {
      const mockAlerts = [
        {
          id: '1',
          severity: 'high',
          metric: 'cpu',
          message: 'High CPU usage',
          timestamp: new Date().toISOString()
        }
      ];

      mockPerformanceMonitor.generateAlerts.mockResolvedValue(mockAlerts);

      const response = await request(app)
        .get('/api/metrics/alerts')
        .query({ severity: 'high', active: 'true' });

      expect(response.status).toBe(200);
      expect(response.body.data).toEqual({
        alerts: mockAlerts,
        count: mockAlerts.length
      });
      expect(mockPerformanceMonitor.generateAlerts).toHaveBeenCalled();
    });
  });

  describe('POST /api/metrics/record', () => {
    it('should record tool execution metrics', async () => {
      const executionData = {
        tool: 'create_epic',
        success: true,
        duration: 150,
        metadata: { epicId: '123' }
      };

      const response = await request(app)
        .post('/api/metrics/record')
        .send(executionData);

      expect(response.status).toBe(200);
      expect(response.body.success).toBe(true);
      expect(mockPerformanceMonitor.recordToolExecution).toHaveBeenCalledWith(
        'create_epic',
        150,
        true
      );
    });

    it('should validate required fields', async () => {
      const response = await request(app)
        .post('/api/metrics/record')
        .send({ tool: 'test_tool' }); // missing success field

      expect(response.status).toBe(400);
      expect(response.body.error).toBe('Tool name and success status are required');
    });
  });

  describe('GET /api/metrics/export', () => {
    it('should export metrics for specified time range', async () => {
      const mockExportData = {
        timeRange: { start: '2024-01-01', end: '2024-01-31' },
        metrics: { tool: [], system: [], quality: [] }
      };

      mockPerformanceMonitor.exportMetrics.mockResolvedValue(mockExportData);

      const response = await request(app)
        .get('/api/metrics/export')
        .query({ format: 'json', startDate: '2024-01-01', endDate: '2024-01-31' });

      expect(response.status).toBe(200);
      expect(response.body.data).toHaveProperty('exportPath');
      expect(response.body.data).toHaveProperty('format', 'json');
      expect(mockPerformanceMonitor.exportMetrics).toHaveBeenCalledWith('json');
    });
  });

  describe('GET /api/metrics/trends', () => {
    it('should return performance trends (currently mock data)', async () => {
      const response = await request(app)
        .get('/api/metrics/trends')
        .query({ metric: 'response_time', timeRange: '7d' });

      expect(response.status).toBe(200);
      expect(response.body.success).toBe(true);
      expect(response.body.data).toHaveProperty('dataPoints');
      expect(response.body.data).toHaveProperty('timeRange', '7d');
      expect(response.body.data).toHaveProperty('metric', 'response_time');
    });
  });

  describe('POST /api/metrics/quality/evaluate', () => {
    it('should record quality evaluation', async () => {
      const qualityData = {
        toolName: 'test_tool',
        quality: 8,
        feedback: 'Good quality output'
      };

      const response = await request(app)
        .post('/api/metrics/quality/evaluate')
        .send(qualityData);

      expect(response.status).toBe(200);
      expect(mockPerformanceMonitor.recordQualityEvaluation).toHaveBeenCalledWith({
        toolName: 'test_tool',
        outputQuality: 8,
        appropriatenessScore: 8,
        completenessScore: 8,
        accuracyScore: 8,
        comments: 'Good quality output',
        evaluationCriteria: ['user_feedback']
      });
    });
  });

  describe('GET /api/metrics/modules', () => {
    it('should return module-specific metrics', async () => {
      const mockModuleMetrics = {
        'agile-management': { calls: 100, avgTime: 50 },
        'kanban': { calls: 150, avgTime: 30 }
      };

      mockPerformanceMonitor.getModuleMetrics.mockResolvedValue(mockModuleMetrics);

      const response = await request(app)
        .get('/api/metrics/modules')
        .query({ moduleName: 'agile-management' });

      expect(response.status).toBe(200);
      expect(response.body.data).toEqual(mockModuleMetrics);
      expect(mockPerformanceMonitor.getModuleMetrics).toHaveBeenCalledWith('agile-management');
    });
  });

  describe('GET /api/metrics/bottlenecks', () => {
    it('should analyze and return performance bottlenecks', async () => {
      const mockBottlenecks = {
        slowestTools: ['complex_analysis', 'generate_report'],
        highMemoryUsage: ['data_processing'],
        recommendations: ['Consider caching for complex_analysis']
      };

      mockPerformanceMonitor.analyzeBottlenecks.mockResolvedValue(mockBottlenecks);

      const response = await request(app)
        .get('/api/metrics/bottlenecks')
        .query({ threshold: '500' });

      expect(response.status).toBe(200);
      expect(response.body.data).toEqual(mockBottlenecks);
      expect(mockPerformanceMonitor.analyzeBottlenecks).toHaveBeenCalledWith({ threshold: 500 });
    });
  });

  describe('GET /api/metrics/report', () => {
    it('should generate performance report', async () => {
      const mockReport = {
        summary: 'System performing well',
        metrics: {},
        recommendations: []
      };

      mockPerformanceMonitor.generatePerformanceReport.mockResolvedValue(mockReport);

      const response = await request(app)
        .get('/api/metrics/report')
        .query({ format: 'json', includeRecommendations: 'true' });

      expect(response.status).toBe(200);
      expect(response.body.data).toEqual(mockReport);
      expect(mockPerformanceMonitor.generatePerformanceReport).toHaveBeenCalledWith({
        format: 'json',
        includeRecommendations: true
      });
    });
  });

  describe('DELETE /api/metrics/clear', () => {
    it('should clear metrics with confirmation', async () => {
      mockPerformanceMonitor.clearMetrics.mockResolvedValue({ cleared: true });

      const response = await request(app)
        .delete('/api/metrics/clear')
        .query({ confirm: 'yes', before: '2024-01-01' });

      expect(response.status).toBe(200);
      expect(response.body.success).toBe(true);
      expect(mockPerformanceMonitor.clearMetrics).toHaveBeenCalledWith({ before: '2024-01-01' });
    });

    it('should require confirmation', async () => {
      const response = await request(app).delete('/api/metrics/clear');

      expect(response.status).toBe(400);
      expect(response.body.error).toBe('Confirmation required to clear metrics');
    });
  });
});