/**
 * Cross-product telemetry tests — multi-product query endpoints.
 */

import { describe, it, expect, beforeAll } from 'vitest';
import {
  queryCrossProductSummary,
  queryCrossProductDaily,
  queryCrossProductClusters,
} from './repository.js';
import { getCollection } from '../../lib/datastore.js';
import type { TelemetryEventDoc, TelemetryErrorCluster } from './types.js';

function eventsCollection() {
  return getCollection<TelemetryEventDoc>('telemetry_events', '/pk');
}

function clustersCollection() {
  return getCollection<TelemetryErrorCluster>('telemetry_error_clusters', '/pk');
}

function makeEvent(
  productId: string,
  overrides: Partial<TelemetryEventDoc> = {}
): TelemetryEventDoc {
  const id = `evt_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
  return {
    id,
    productId,
    anonymousInstallId: '660e8400-e29b-41d4-a716-446655440001',
    sessionId: 'sess_test',
    platform: 'ios',
    channel: 'mobile_app',
    osFamily: 'ios',
    appVersion: '1.0.0',
    buildNumber: '1',
    releaseChannel: 'prod',
    eventType: 'info',
    module: 'core',
    eventName: 'app_started',
    occurredAt: '2026-03-19T10:00:00.000Z',
    pk: `${productId}:202603:ios`,
    receivedAt: new Date().toISOString(),
    ttl: 2592000,
    ...overrides,
  };
}

function makeCluster(
  productId: string,
  overrides: Partial<TelemetryErrorCluster> = {}
): TelemetryErrorCluster {
  const fp = Math.random().toString(36).slice(2, 10);
  return {
    id: `${fp}:202603`,
    pk: `${productId}:ios:core`,
    productId,
    fingerprint: fp,
    platform: 'ios',
    channel: 'mobile_app',
    module: 'core',
    eventName: 'crash',
    affectedVersions: [
      { appVersion: '1.0.0', buildNumber: '1', count: 1, lastSeenAt: new Date().toISOString() },
    ],
    firstSeenAt: new Date().toISOString(),
    lastSeenAt: new Date().toISOString(),
    totalCount: 5,
    affectedUserIds: [],
    affectedInstallIds: ['inst1'],
    affectedOsFamilies: ['ios'],
    severity: 'error',
    status: 'open',
    ttl: 7776000,
    ...overrides,
  };
}

describe('cross-product telemetry', () => {
  beforeAll(async () => {
    // Seed events for two products
    const events = [
      makeEvent('product-a', {
        eventType: 'info',
        module: 'auth',
        occurredAt: '2026-03-18T10:00:00.000Z',
      }),
      makeEvent('product-a', {
        eventType: 'error',
        module: 'auth',
        occurredAt: '2026-03-18T11:00:00.000Z',
      }),
      makeEvent('product-a', {
        eventType: 'info',
        module: 'sync',
        occurredAt: '2026-03-19T10:00:00.000Z',
      }),
      makeEvent('product-b', {
        eventType: 'info',
        module: 'core',
        occurredAt: '2026-03-19T10:00:00.000Z',
        platform: 'web' as const,
      }),
      makeEvent('product-b', {
        eventType: 'fatal',
        module: 'core',
        occurredAt: '2026-03-19T11:00:00.000Z',
        platform: 'web' as const,
      }),
    ];
    for (const e of events) {
      await eventsCollection().upsert(e);
    }

    // Seed clusters
    await clustersCollection().upsert(makeCluster('product-a', { totalCount: 10 }));
    await clustersCollection().upsert(makeCluster('product-b', { totalCount: 3 }));
  });

  it('queryCrossProductSummary returns per-product stats', async () => {
    const results = await queryCrossProductSummary(['product-a', 'product-b']);

    expect(results).toHaveLength(2);

    const a = results.find(r => r.productId === 'product-a')!;
    expect(a.totalEvents).toBeGreaterThanOrEqual(3);
    expect(a.errorEvents).toBeGreaterThanOrEqual(1);
    expect(a.errorRate).toBeGreaterThan(0);
    expect(a.topModules.length).toBeGreaterThan(0);

    const b = results.find(r => r.productId === 'product-b')!;
    expect(b.totalEvents).toBeGreaterThanOrEqual(2);
    expect(b.errorEvents).toBeGreaterThanOrEqual(1);
  });

  it('queryCrossProductDaily returns daily breakdown', async () => {
    const results = await queryCrossProductDaily(['product-a', 'product-b']);

    expect(results.length).toBeGreaterThan(0);
    // Should have entries for both products
    const productIds = new Set(results.map(r => r.productId));
    expect(productIds.has('product-a')).toBe(true);
    expect(productIds.has('product-b')).toBe(true);

    // Verify date format
    for (const r of results) {
      expect(r.date).toMatch(/^\d{4}-\d{2}-\d{2}$/);
      expect(r.count).toBeGreaterThan(0);
    }
  });

  it('queryCrossProductClusters returns merged cluster list', async () => {
    const clusters = await queryCrossProductClusters(['product-a', 'product-b'], 10);

    expect(clusters.length).toBeGreaterThanOrEqual(2);
    // Should be sorted by totalCount descending
    for (let i = 1; i < clusters.length; i++) {
      expect(clusters[i - 1].totalCount).toBeGreaterThanOrEqual(clusters[i].totalCount);
    }
  });

  it('queryCrossProductSummary with date filter narrows results', async () => {
    const results = await queryCrossProductSummary(
      ['product-a'],
      '2026-03-19T00:00:00.000Z',
      '2026-03-19T23:59:59.000Z'
    );

    expect(results).toHaveLength(1);
    const a = results[0];
    // Only the March 19th events should be included
    expect(a.totalEvents).toBeGreaterThanOrEqual(1);
  });

  it('queryCrossProductSummary for unknown product returns zero counts', async () => {
    const results = await queryCrossProductSummary(['nonexistent-product']);

    expect(results).toHaveLength(1);
    expect(results[0].totalEvents).toBe(0);
    expect(results[0].errorEvents).toBe(0);
    expect(results[0].errorRate).toBe(0);
  });

  it('queryCrossProductClusters respects limit', async () => {
    const clusters = await queryCrossProductClusters(['product-a', 'product-b'], 1);

    expect(clusters).toHaveLength(1);
    // Should be the highest count cluster
    expect(clusters[0].totalCount).toBeGreaterThanOrEqual(3);
  });
});
