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

vi.mock('./config.js', () => ({
  config: { PLATFORM_SERVICE_URL: 'http://localhost:4003' },
}));
vi.mock('./product-config.js', () => ({
  PRODUCT_ID: 'clawcowork',
}));

import {
  postAuditEvents,
  postTelemetryEvents,
  postUsageRecords,
  pollFlags,
} from './platform-client.js';

const mockFetch = vi.fn();
globalThis.fetch = mockFetch;

beforeEach(() => {
  mockFetch.mockReset();
});

afterEach(() => {
  vi.restoreAllMocks();
});

describe('platform-client', () => {
  describe('postAuditEvents', () => {
    it('posts each audit entry individually', async () => {
      mockFetch.mockResolvedValue({ ok: true, json: () => Promise.resolve({ accepted: true }) });

      const result = await postAuditEvents([
        { userId: 'u1', action: 'task.submit', category: 'task' },
        { userId: 'u2', action: 'task.cancel', category: 'task' },
      ]);

      expect(result).toEqual({ posted: 2, errors: 0 });
      expect(mockFetch).toHaveBeenCalledTimes(2);
      expect(mockFetch.mock.calls[0][0]).toBe('http://localhost:4003/audit');
    });

    it('counts errors without throwing', async () => {
      mockFetch
        .mockResolvedValueOnce({ ok: true, json: () => Promise.resolve({ accepted: true }) })
        .mockResolvedValueOnce({ ok: false, status: 500, text: () => Promise.resolve('fail') });

      const result = await postAuditEvents([
        { userId: 'u1', action: 'a1' },
        { userId: 'u2', action: 'a2' },
      ]);
      expect(result).toEqual({ posted: 1, errors: 1 });
    });
  });

  describe('postTelemetryEvents', () => {
    it('posts batch to /telemetry/events', async () => {
      mockFetch.mockResolvedValue({
        ok: true,
        json: () => Promise.resolve({ accepted: 3, rejected: 0 }),
      });

      const events = [
        { id: 'e1', name: 'task.submit', timestamp: '2026-01-01T00:00:00Z' },
        { id: 'e2', name: 'task.complete', timestamp: '2026-01-01T00:01:00Z' },
        { id: 'e3', name: 'task.cancel', timestamp: '2026-01-01T00:02:00Z' },
      ];
      const result = await postTelemetryEvents(events);

      expect(result).toEqual({ accepted: 3, rejected: 0 });
      expect(mockFetch).toHaveBeenCalledTimes(1);
      const body = JSON.parse(mockFetch.mock.calls[0][1].body);
      expect(body.productId).toBe('clawcowork');
      expect(body.events).toHaveLength(3);
    });

    it('returns zero counts for empty array', async () => {
      const result = await postTelemetryEvents([]);
      expect(result).toEqual({ accepted: 0, rejected: 0 });
      expect(mockFetch).not.toHaveBeenCalled();
    });
  });

  describe('postUsageRecords', () => {
    it('posts each record individually', async () => {
      mockFetch.mockResolvedValue({ ok: true, json: () => Promise.resolve({}) });

      const result = await postUsageRecords([
        { userId: 'u1', date: '2026-01-01', model: 'claude-sonnet', inputTokens: 100 },
      ]);
      expect(result).toEqual({ posted: 1, errors: 0 });
      expect(mockFetch.mock.calls[0][0]).toBe('http://localhost:4003/usage');
    });
  });

  describe('pollFlags', () => {
    it('calls GET /flags/poll and returns flags', async () => {
      mockFetch.mockResolvedValue({
        ok: true,
        json: () => Promise.resolve({ flags: { sandbox_enabled: true }, productId: 'clawcowork' }),
      });

      const result = await pollFlags();
      expect(result.flags.sandbox_enabled).toBe(true);
      expect(result.productId).toBe('clawcowork');
      expect(mockFetch.mock.calls[0][0]).toBe('http://localhost:4003/flags/poll');
    });

    it('appends platform query param when provided', async () => {
      mockFetch.mockResolvedValue({
        ok: true,
        json: () => Promise.resolve({ flags: {}, productId: 'clawcowork' }),
      });

      await pollFlags('macos');
      expect(mockFetch.mock.calls[0][0]).toBe('http://localhost:4003/flags/poll?platform=macos');
    });

    it('throws on non-ok response', async () => {
      mockFetch.mockResolvedValue({
        ok: false,
        status: 503,
        text: () => Promise.resolve('service unavailable'),
      });

      await expect(pollFlags()).rejects.toThrow('platform-service GET /flags/poll → 503');
    });
  });
});
