import { describe, test } from "node:test";
import assert from "node:assert/strict";
import { detectAlerts, detectWeeklyAlerts } from "../src/utils/alerts.js";
import type { PostAnalytics } from "../src/models/types.js";

/**
 * Helper function to create PostAnalytics with default values.
 */
function makePost(overrides: Partial<PostAnalytics> = {}): PostAnalytics {
  return {
    id: `post-${Math.random().toString(36).substr(2, 9)}`,
    title: "Test Post",
    publishedDate: "2026-01-15",
    metrics: {
      impressions: 1000,
      reactions: 50,
      comments: 10,
      shares: 5,
      clicks: 100,
      engagementRate: 5.0,
    },
    importedAt: new Date().toISOString(),
    exportSource: "LinkedIn",
    ...overrides,
  };
}

describe("alerts", () => {
  describe("detectAlerts", () => {
    test("should find spike posts", () => {
      // Create posts with one outlier high value
      // Need value that's > 2.0 standard deviations from mean (not >=)
      // Using more base values to create a scenario where outlier exceeds threshold
      const posts = [
        makePost({ id: "1", title: "Normal Post 1", metrics: { impressions: 1000, reactions: 50, comments: 10, shares: 5, clicks: 100, engagementRate: 5.0 } }),
        makePost({ id: "2", title: "Normal Post 2", metrics: { impressions: 1200, reactions: 50, comments: 10, shares: 5, clicks: 100, engagementRate: 5.0 } }),
        makePost({ id: "3", title: "Normal Post 3", metrics: { impressions: 1100, reactions: 50, comments: 10, shares: 5, clicks: 100, engagementRate: 5.0 } }),
        makePost({ id: "4", title: "Normal Post 4", metrics: { impressions: 900, reactions: 50, comments: 10, shares: 5, clicks: 100, engagementRate: 5.0 } }),
        makePost({ id: "5", title: "Normal Post 5", metrics: { impressions: 1050, reactions: 50, comments: 10, shares: 5, clicks: 100, engagementRate: 5.0 } }),
        makePost({ id: "6", title: "Viral Post", metrics: { impressions: 10000, reactions: 50, comments: 10, shares: 5, clicks: 100, engagementRate: 5.0 } }),
      ];

      const alerts = detectAlerts(posts, "impressions");

      assert.ok(alerts.length > 0, "Should detect at least one alert");
      const spikeAlert = alerts.find(a => a.type === "spike");
      assert.ok(spikeAlert, "Should have a spike alert");
      assert.equal(spikeAlert.severity, "info");
      assert.equal(spikeAlert.postId, "6");
      assert.ok(spikeAlert.message.includes("Viral Post"));
    });

    test("should find drop posts", () => {
      // Create posts with one outlier low value
      const posts = [
        makePost({ id: "1", title: "Normal Post 1", metrics: { impressions: 10000, reactions: 50, comments: 10, shares: 5, clicks: 100, engagementRate: 5.0 } }),
        makePost({ id: "2", title: "Normal Post 2", metrics: { impressions: 10000, reactions: 50, comments: 10, shares: 5, clicks: 100, engagementRate: 5.0 } }),
        makePost({ id: "3", title: "Normal Post 3", metrics: { impressions: 10000, reactions: 50, comments: 10, shares: 5, clicks: 100, engagementRate: 5.0 } }),
        makePost({ id: "4", title: "Low Reach Post", metrics: { impressions: 100, reactions: 50, comments: 10, shares: 5, clicks: 100, engagementRate: 5.0 } }),
      ];

      const alerts = detectAlerts(posts, "impressions");

      assert.equal(alerts.length, 1);
      assert.equal(alerts[0].type, "drop");
      assert.equal(alerts[0].severity, "warning");
      assert.equal(alerts[0].postId, "4");
      assert.ok(alerts[0].message.includes("Low Reach Post"));
    });

    test("should return empty for uniform data", () => {
      const posts = [
        makePost({ id: "1", metrics: { impressions: 5000, reactions: 50, comments: 10, shares: 5, clicks: 100, engagementRate: 5.0 } }),
        makePost({ id: "2", metrics: { impressions: 5000, reactions: 50, comments: 10, shares: 5, clicks: 100, engagementRate: 5.0 } }),
        makePost({ id: "3", metrics: { impressions: 5000, reactions: 50, comments: 10, shares: 5, clicks: 100, engagementRate: 5.0 } }),
      ];

      const alerts = detectAlerts(posts, "impressions");

      assert.equal(alerts.length, 0);
    });

    test("should handle empty posts array", () => {
      const alerts = detectAlerts([]);
      assert.equal(alerts.length, 0);
    });

    test("should sort alerts by severity", () => {
      // Create scenario with multiple alerts of different severities
      // For this, we'd need to manually create alerts with different severities
      // Since detectAlerts only produces "info" spikes and "warning" drops,
      // let's just verify the sorting works with what we have
      const posts = [
        makePost({ id: "1", metrics: { impressions: 5000, reactions: 50, comments: 10, shares: 5, clicks: 100, engagementRate: 5.0 } }),
        makePost({ id: "2", metrics: { impressions: 5000, reactions: 50, comments: 10, shares: 5, clicks: 100, engagementRate: 5.0 } }),
        makePost({ id: "3", metrics: { impressions: 100, reactions: 50, comments: 10, shares: 5, clicks: 100, engagementRate: 5.0 } }), // Drop
        makePost({ id: "4", metrics: { impressions: 50000, reactions: 50, comments: 10, shares: 5, clicks: 100, engagementRate: 5.0 } }), // Spike
      ];

      const alerts = detectAlerts(posts, "impressions");

      // Should have drop (warning) first, then spike (info)
      if (alerts.length > 1) {
        assert.equal(alerts[0].severity, "warning");
        assert.equal(alerts[1].severity, "info");
      }
    });
  });

  describe("detectWeeklyAlerts", () => {
    test("should detect critical drop in impressions", () => {
      const current = { impressions: 1000, engagementRate: 5.0 };
      const previous = { impressions: 3000, engagementRate: 5.0 }; // -66.7% drop

      const alerts = detectWeeklyAlerts(current, previous);

      const impressionAlerts = alerts.filter((a) => a.metric === "impressions");
      assert.ok(impressionAlerts.length > 0);
      assert.equal(impressionAlerts[0].severity, "critical");
      assert.equal(impressionAlerts[0].type, "drop");
    });

    test("should detect warning drop in impressions", () => {
      const current = { impressions: 6000, engagementRate: 5.0 };
      const previous = { impressions: 10000, engagementRate: 5.0 }; // -40% drop

      const alerts = detectWeeklyAlerts(current, previous);

      const impressionAlerts = alerts.filter((a) => a.metric === "impressions");
      assert.ok(impressionAlerts.length > 0);
      assert.equal(impressionAlerts[0].severity, "warning");
      assert.equal(impressionAlerts[0].type, "drop");
    });

    test("should detect spike in impressions", () => {
      const current = { impressions: 25000, engagementRate: 5.0 };
      const previous = { impressions: 10000, engagementRate: 5.0 }; // +150% increase

      const alerts = detectWeeklyAlerts(current, previous);

      const impressionAlerts = alerts.filter((a) => a.metric === "impressions");
      assert.ok(impressionAlerts.length > 0);
      assert.equal(impressionAlerts[0].severity, "info");
      assert.equal(impressionAlerts[0].type, "spike");
    });

    test("should detect critical drop in engagement rate", () => {
      const current = { impressions: 10000, engagementRate: 2.0 };
      const previous = { impressions: 10000, engagementRate: 6.0 }; // -66.7% drop

      const alerts = detectWeeklyAlerts(current, previous);

      const engagementAlerts = alerts.filter((a) => a.metric === "engagementRate");
      assert.ok(engagementAlerts.length > 0);
      assert.equal(engagementAlerts[0].severity, "critical");
      assert.equal(engagementAlerts[0].type, "drop");
    });

    test("should detect warning drop in engagement rate", () => {
      const current = { impressions: 10000, engagementRate: 3.0 };
      const previous = { impressions: 10000, engagementRate: 5.0 }; // -40% drop

      const alerts = detectWeeklyAlerts(current, previous);

      const engagementAlerts = alerts.filter((a) => a.metric === "engagementRate");
      assert.ok(engagementAlerts.length > 0);
      assert.equal(engagementAlerts[0].severity, "warning");
      assert.equal(engagementAlerts[0].type, "drop");
    });

    test("should detect spike in engagement rate", () => {
      const current = { impressions: 10000, engagementRate: 12.0 };
      const previous = { impressions: 10000, engagementRate: 5.0 }; // +140% increase

      const alerts = detectWeeklyAlerts(current, previous);

      const engagementAlerts = alerts.filter((a) => a.metric === "engagementRate");
      assert.ok(engagementAlerts.length > 0);
      assert.equal(engagementAlerts[0].severity, "info");
      assert.equal(engagementAlerts[0].type, "spike");
    });

    test("should return empty for stable metrics", () => {
      const current = { impressions: 10000, engagementRate: 5.0 };
      const previous = { impressions: 10200, engagementRate: 5.1 }; // Small changes

      const alerts = detectWeeklyAlerts(current, previous);

      assert.equal(alerts.length, 0);
    });

    test("should handle multiple alerts and sort by severity", () => {
      const current = { impressions: 1000, engagementRate: 2.0 };
      const previous = { impressions: 3000, engagementRate: 6.0 }; // Both critical drops

      const alerts = detectWeeklyAlerts(current, previous);

      assert.ok(alerts.length >= 2);
      // All should be critical
      alerts.forEach((alert) => {
        assert.equal(alert.severity, "critical");
      });
    });
  });
});
