// Tests for @agor/stores notification store
// The store uses Svelte 5 runes ($state) so we can't import directly in vitest.
// We test the pure logic and types that can be validated without the Svelte compiler.

import { describe, it, expect, vi, beforeEach } from 'vitest';

// Mock the backend module
vi.mock('../../../src/lib/backend/backend', () => ({
  getBackend: vi.fn(() => ({
    sendDesktopNotification: vi.fn(),
  })),
}));

// Since the store uses $state (Svelte 5 rune), we cannot import it directly.
// Instead we replicate the pure functions and test them.

// ── Replicated pure logic from notifications.svelte.ts ──────────────────────

type ToastType = 'info' | 'success' | 'warning' | 'error';
type NotificationType = 'agent_complete' | 'agent_error' | 'task_review' | 'wake_event' | 'conflict' | 'system';

function notificationTypeToToast(type: NotificationType): ToastType {
  switch (type) {
    case 'agent_complete': return 'success';
    case 'agent_error': return 'error';
    case 'task_review': return 'info';
    case 'wake_event': return 'info';
    case 'conflict': return 'warning';
    case 'system': return 'info';
  }
}

function notificationUrgency(type: NotificationType): 'low' | 'normal' | 'critical' {
  switch (type) {
    case 'agent_error': return 'critical';
    case 'conflict': return 'normal';
    case 'system': return 'normal';
    default: return 'low';
  }
}

// Rate limiter logic (replicated)
const RATE_LIMIT_WINDOW_MS = 30_000;
const RATE_LIMIT_MAX_PER_TYPE = 3;

function createRateLimiter() {
  const recentToasts = new Map<ToastType, number[]>();
  return {
    isRateLimited(type: ToastType, now = Date.now()): boolean {
      const timestamps = recentToasts.get(type) ?? [];
      const recent = timestamps.filter(t => now - t < RATE_LIMIT_WINDOW_MS);
      recentToasts.set(type, recent);
      if (recent.length >= RATE_LIMIT_MAX_PER_TYPE) return true;
      recent.push(now);
      return false;
    },
  };
}

describe('notification type mapping', () => {
  it('maps agent_complete to success toast', () => {
    expect(notificationTypeToToast('agent_complete')).toBe('success');
  });

  it('maps agent_error to error toast', () => {
    expect(notificationTypeToToast('agent_error')).toBe('error');
  });

  it('maps conflict to warning toast', () => {
    expect(notificationTypeToToast('conflict')).toBe('warning');
  });

  it('maps system to info toast', () => {
    expect(notificationTypeToToast('system')).toBe('info');
  });
});

describe('notification urgency', () => {
  it('agent_error is critical', () => {
    expect(notificationUrgency('agent_error')).toBe('critical');
  });

  it('conflict is normal', () => {
    expect(notificationUrgency('conflict')).toBe('normal');
  });

  it('task_review is low', () => {
    expect(notificationUrgency('task_review')).toBe('low');
  });
});

describe('rate limiter', () => {
  it('allows first 3 toasts of same type', () => {
    const rl = createRateLimiter();
    const now = Date.now();
    expect(rl.isRateLimited('info', now)).toBe(false);
    expect(rl.isRateLimited('info', now + 1)).toBe(false);
    expect(rl.isRateLimited('info', now + 2)).toBe(false);
  });

  it('blocks 4th toast of same type within window', () => {
    const rl = createRateLimiter();
    const now = Date.now();
    rl.isRateLimited('error', now);
    rl.isRateLimited('error', now + 1);
    rl.isRateLimited('error', now + 2);
    expect(rl.isRateLimited('error', now + 3)).toBe(true);
  });

  it('allows toast of different type', () => {
    const rl = createRateLimiter();
    const now = Date.now();
    rl.isRateLimited('error', now);
    rl.isRateLimited('error', now + 1);
    rl.isRateLimited('error', now + 2);
    // Different type should not be rate limited
    expect(rl.isRateLimited('info', now + 3)).toBe(false);
  });

  it('resets after window expires', () => {
    const rl = createRateLimiter();
    const now = Date.now();
    rl.isRateLimited('warning', now);
    rl.isRateLimited('warning', now + 1);
    rl.isRateLimited('warning', now + 2);
    // After window expires, should allow again
    expect(rl.isRateLimited('warning', now + RATE_LIMIT_WINDOW_MS + 1)).toBe(false);
  });
});
