import { vi } from "vitest";
import { PerformanceMonitor } from "@/performance/PerformanceMonitor.js";

describe("PerformanceMonitor", () => {
  let monitor;
  let mockConfig;

  beforeEach(() => {
    vi.clearAllMocks();

    mockConfig = {
      collectInterval: 1000,
      retentionPeriod: 3600000,
      alertThresholds: {
        responseTime: 2000,
        errorRate: 0.05,
        cacheHitRate: 0.8,
        memoryUsage: 80,
        cpuUsage: 80,
      },
      enableRealTimeMonitoring: false, // Disable to avoid timers in tests
      enableHistoricalData: true,
      enableAlerts: true,
    };

    monitor = new PerformanceMonitor(mockConfig);
  });

  afterEach(() => {
    if (monitor) {
      monitor.stop();
    }
  });

  describe("constructor", () => {
    it("should initialize with default config", () => {
      const defaultMonitor = new PerformanceMonitor();
      expect(defaultMonitor).toBeDefined();
      expect(defaultMonitor.getMetrics()).toBeDefined();
      // Clean up to prevent open handles
      defaultMonitor.stop();
    });

    it("should initialize with custom config", () => {
      expect(monitor).toBeDefined();
      expect(monitor.getMetrics()).toBeDefined();
    });

    it("should initialize metrics to zero", () => {
      const metrics = monitor.getMetrics();
      expect(metrics.requests.total).toBe(0);
      expect(metrics.requests.successful).toBe(0);
      expect(metrics.requests.failed).toBe(0);
      expect(metrics.cache.hits).toBe(0);
      expect(metrics.cache.misses).toBe(0);
      expect(metrics.system.activeConnections).toBeGreaterThanOrEqual(0);
      expect(metrics.system.concurrentRequests).toBe(0);
      expect(metrics.system.uptime).toBeGreaterThanOrEqual(0);
    });
  });

  describe("metric collection", () => {
    it("should record successful request metrics", () => {
      monitor.recordRequest(150, true, "wp_list_posts");

      const metrics = monitor.getMetrics();
      expect(metrics.requests.total).toBe(1);
      expect(metrics.requests.successful).toBe(1);
      expect(metrics.requests.failed).toBe(0);
      expect(metrics.requests.averageResponseTime).toBe(150);
      expect(metrics.tools.toolUsageCount["wp_list_posts"]).toBe(1);
    });

    it("should record failed request metrics", () => {
      monitor.recordRequest(300, false, "wp_list_posts");

      const metrics = monitor.getMetrics();
      expect(metrics.requests.total).toBe(1);
      expect(metrics.requests.successful).toBe(0);
      expect(metrics.requests.failed).toBe(1);
      expect(metrics.requests.averageResponseTime).toBe(300);
    });

    it("should calculate response time percentiles", () => {
      // Record multiple requests to get percentile calculations
      const responseTimes = [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000];
      responseTimes.forEach((time) => {
        monitor.recordRequest(time, true);
      });

      const metrics = monitor.getMetrics();
      expect(metrics.requests.total).toBe(10);
      expect(metrics.requests.averageResponseTime).toBe(550);
      expect(metrics.requests.minResponseTime).toBe(100);
      expect(metrics.requests.maxResponseTime).toBe(1000);
      expect(metrics.requests.p50ResponseTime).toBeGreaterThan(0);
      expect(metrics.requests.p95ResponseTime).toBeGreaterThan(0);
      expect(metrics.requests.p99ResponseTime).toBeGreaterThan(0);
    });

    it("should update cache metrics", () => {
      const cacheStats = {
        hits: 75,
        misses: 25,
        hitRate: 0.75,
        totalSize: 100,
        memoryUsageMB: 50,
        evictions: 5,
        averageCacheTime: 10,
      };

      monitor.updateCacheMetrics(cacheStats);

      const metrics = monitor.getMetrics();
      expect(metrics.cache.hits).toBe(75);
      expect(metrics.cache.misses).toBe(25);
      expect(metrics.cache.hitRate).toBe(0.75);
      expect(metrics.cache.totalSize).toBe(100);
      expect(metrics.cache.memoryUsageMB).toBeCloseTo(0.09765625, 5);
      expect(metrics.cache.evictions).toBe(5);
      expect(metrics.cache.averageCacheTime).toBe(0.5);
    });

    it("should track tool usage", () => {
      monitor.recordRequest(100, true, "wp_list_posts");
      monitor.recordRequest(200, true, "wp_get_post");
      monitor.recordRequest(150, true, "wp_list_posts");

      const metrics = monitor.getMetrics();
      expect(metrics.tools.toolUsageCount["wp_list_posts"]).toBe(2);
      expect(metrics.tools.toolUsageCount["wp_get_post"]).toBe(1);
      expect(metrics.tools.mostUsedTool).toBe("wp_list_posts");

      // Check tool performance tracking
      expect(metrics.tools.toolPerformance["wp_list_posts"]).toBeDefined();
      expect(metrics.tools.toolPerformance["wp_list_posts"].callCount).toBe(2);
      expect(metrics.tools.toolPerformance["wp_list_posts"].averageTime).toBe(125);
      expect(metrics.tools.toolPerformance["wp_list_posts"].successRate).toBe(1);
    });

    it("should track most used tool", () => {
      monitor.recordRequest(100, true, "wp_list_posts");
      monitor.recordRequest(200, true, "wp_get_post");
      monitor.recordRequest(150, true, "wp_list_posts");
      monitor.recordRequest(180, true, "wp_list_posts");

      const metrics = monitor.getMetrics();
      expect(metrics.tools.mostUsedTool).toBe("wp_list_posts");
    });
  });

  describe("alerting system", () => {
    it("should generate alerts for high response times", () => {
      // Record a request with high response time
      monitor.recordRequest(3000, true); // Above 2000ms threshold

      const alerts = monitor.getAlerts();
      expect(alerts.length).toBeGreaterThan(0);

      const responseTimeAlert = alerts.find((a) => a.category === "performance");
      expect(responseTimeAlert).toBeDefined();
      expect(responseTimeAlert.severity).toBe("warning");
    });

    it("should generate alerts for high error rates", () => {
      // Record several failed requests to trigger error rate alert
      for (let i = 0; i < 10; i++) {
        monitor.recordRequest(100, false); // 100% failure rate
      }

      const alerts = monitor.getAlerts();
      expect(alerts.length).toBeGreaterThan(0);

      const errorRateAlert = alerts.find((a) => a.message.includes("error rate") || a.message.includes("Error rate"));
      expect(errorRateAlert).toBeDefined();
      expect(errorRateAlert.severity).toBe("error");
    });

    it("should generate alerts for low cache hit rates", () => {
      const cacheStats = {
        hits: 10,
        misses: 90,
        hitRate: 0.1, // Below 0.8 threshold
        totalSize: 100,
      };

      monitor.updateCacheMetrics(cacheStats);

      const alerts = monitor.getAlerts();
      // Test that alerts are generated appropriately
      expect(Array.isArray(alerts)).toBe(true);

      // Filter cache alerts and verify they are warnings
      const cacheAlerts = alerts.filter((a) => a.category === "cache" || a.metric === "cacheHitRate");
      cacheAlerts.forEach((alert) => {
        expect(alert.severity).toBe("warning");
      });
    });

    it("should filter alerts by severity", () => {
      // Generate alerts of different severities
      monitor.recordRequest(3000, true); // Warning
      for (let i = 0; i < 10; i++) {
        monitor.recordRequest(100, false); // Error
      }

      const warningAlerts = monitor.getAlerts("warning");
      const errorAlerts = monitor.getAlerts("error");

      expect(warningAlerts.every((a) => a.severity === "warning")).toBe(true);
      expect(errorAlerts.every((a) => a.severity === "error")).toBe(true);
    });

    it("should clear alerts", () => {
      monitor.recordRequest(3000, true); // Generate an alert

      let alerts = monitor.getAlerts();
      expect(alerts.length).toBeGreaterThan(0);

      monitor.clearAlerts();
      alerts = monitor.getAlerts();
      expect(alerts).toHaveLength(0);
    });
  });

  describe("historical data", () => {
    it("should store historical data when enabled", () => {
      monitor.recordRequest(100, true);
      monitor.recordRequest(200, true);

      const historicalData = monitor.getHistoricalData();
      expect(Array.isArray(historicalData)).toBe(true);
      // Note: Historical data is stored based on collection intervals
      // so it might be empty in a quick test
    });

    it("should filter historical data by time range", () => {
      const now = Date.now();
      const oneHourAgo = now - 3600000;

      const historicalData = monitor.getHistoricalData(oneHourAgo, now);
      expect(Array.isArray(historicalData)).toBe(true);
    });

    it("should return empty array when historical data is disabled", () => {
      const monitorWithoutHistory = new PerformanceMonitor({
        enableHistoricalData: false,
      });

      const historicalData = monitorWithoutHistory.getHistoricalData();
      expect(historicalData).toEqual([]);

      monitorWithoutHistory.stop();
    });
  });

  describe("reporting", () => {
    it("should generate performance insights", () => {
      monitor.recordRequest(100, true);
      monitor.recordRequest(200, true);

      const insights = monitor.generateInsights();
      expect(insights).toHaveProperty("summary");
      expect(insights).toHaveProperty("health");
      expect(insights).toHaveProperty("recommendations");
      expect(insights).toHaveProperty("trends");
      expect(insights.health).toMatch(/excellent|good|warning|critical/);
    });

    it("should export data as JSON", () => {
      monitor.recordRequest(100, true);

      const jsonData = monitor.exportData("json");
      expect(typeof jsonData).toBe("string");
      expect(() => JSON.parse(jsonData)).not.toThrow();

      const parsedData = JSON.parse(jsonData);
      expect(parsedData).toHaveProperty("currentMetrics");
      expect(parsedData).toHaveProperty("historicalData");
      expect(parsedData).toHaveProperty("alerts");
      expect(parsedData).toHaveProperty("config");
      expect(parsedData).toHaveProperty("generatedAt");
    });

    it("should export data as CSV", () => {
      monitor.recordRequest(100, true);

      const csvData = monitor.exportData("csv");
      expect(typeof csvData).toBe("string");
      expect(csvData).toContain("Metric,Value");
    });
  });

  describe("edge cases", () => {
    it("should handle zero requests gracefully", () => {
      const metrics = monitor.getMetrics();
      expect(metrics.requests.total).toBe(0);
      expect(metrics.requests.averageResponseTime).toBe(0);
      expect(metrics.requests.requestsPerSecond).toBe(0);
    });

    it("should handle negative response times", () => {
      monitor.recordRequest(-100, true);

      const metrics = monitor.getMetrics();
      expect(metrics.requests.total).toBe(1);
      // The implementation should handle negative times appropriately
    });

    it("should handle invalid tool names", () => {
      monitor.recordRequest(100, true, "");
      monitor.recordRequest(100, true, null);
      monitor.recordRequest(100, true, undefined);

      const metrics = monitor.getMetrics();
      expect(metrics.requests.total).toBe(3);
    });

    it("should handle concurrent metric updates", async () => {
      const promises = [];

      for (let i = 0; i < 100; i++) {
        promises.push(
          new Promise((resolve) => {
            monitor.recordRequest(100 + i, true, `tool_${i % 10}`);
            resolve();
          }),
        );
      }

      await Promise.all(promises);

      const metrics = monitor.getMetrics();
      expect(metrics.requests.total).toBe(100);
    });
  });

  describe("cleanup", () => {
    it("should stop monitoring and cleanup resources", () => {
      expect(() => monitor.stop()).not.toThrow();
    });
  });
});
