import { describe, it, expect } from 'vitest';
import { mergeTimeline, type TimelineEntry } from './phishing-timeline';

describe('mergeTimeline', () => {
  const reports = [
    { createdAt: '2026-07-15T00:00:00.000Z', reportId: 'report-1', ticketNumber: 'T-0001', companyName: 'Acme' },
    { createdAt: '2026-07-15T04:00:00.000Z', reportId: 'report-2', ticketNumber: 'T-0002', companyName: 'Acme' },
  ];
  const classifications = [
    { createdAt: '2026-07-15T02:00:00.000Z', verdict: 'THREAT', confidence: '85' },
  ];
  const auditEvents = [
    { createdAt: '2026-07-15T01:00:00.000Z', eventType: 'remediation_approved', actor: 'lorentz@wulfconsulting.com', payload: { actions: ['block_sender'] } },
    { createdAt: '2026-07-15T05:00:00.000Z', eventType: 'remediation_completed', actor: null, payload: {} },
  ];

  it('returns a single array sorted ascending by `at` across all 3 sources', () => {
    const result = mergeTimeline(reports, classifications, auditEvents);

    expect(result).toHaveLength(5);
    const timestamps = result.map((entry) => new Date(entry.at).getTime());
    for (let i = 1; i < timestamps.length; i++) {
      expect(timestamps[i]).toBeGreaterThanOrEqual(timestamps[i - 1]);
    }
  });

  it('output length equals the sum of the three input array lengths', () => {
    const result = mergeTimeline(reports, classifications, auditEvents);
    expect(result).toHaveLength(reports.length + classifications.length + auditEvents.length);
  });

  it('carries a discriminant `kind` and the source-specific fields for each entry', () => {
    const result = mergeTimeline(reports, classifications, auditEvents);

    const reportEntry = result.find((e) => e.kind === 'report' && e.reportId === 'report-1');
    expect(reportEntry).toMatchObject({ kind: 'report', reportId: 'report-1', ticketNumber: 'T-0001', companyName: 'Acme' });

    const classificationEntry = result.find((e) => e.kind === 'classification');
    expect(classificationEntry).toMatchObject({ kind: 'classification', verdict: 'THREAT', confidence: '85' });

    const auditEntry = result.find((e) => e.kind === 'audit' && e.eventType === 'remediation_approved');
    expect(auditEntry).toMatchObject({
      kind: 'audit',
      eventType: 'remediation_approved',
      actor: 'lorentz@wulfconsulting.com',
      payload: { actions: ['block_sender'] },
    });
  });

  it('preserves a stable tie-break order (report, then classification, then audit) on identical timestamps and does not throw', () => {
    const tiedAt = '2026-07-15T10:00:00.000Z';
    const tiedReports = [{ createdAt: tiedAt, reportId: 'r', ticketNumber: 'T', companyName: 'C' }];
    const tiedClassifications = [{ createdAt: tiedAt, verdict: 'SPAM', confidence: '50' }];
    const tiedAudit = [{ createdAt: tiedAt, eventType: 'campaign_marked_false_positive', actor: null, payload: {} }];

    let result: TimelineEntry[] = [];
    expect(() => {
      result = mergeTimeline(tiedReports, tiedClassifications, tiedAudit);
    }).not.toThrow();

    expect(result.map((e) => e.kind)).toEqual(['report', 'classification', 'audit']);
  });
});
