import { describe, it, expect, vi, Mocked } from 'vitest';
import { AnalyticsService } from './AnalyticsService';
import { AnalyticsApiClient } from '@/lib/api/analytics/AnalyticsApiClient';
import { RecordPageViewOutputViewModel } from '@/lib/view-models/RecordPageViewOutputViewModel';
import { RecordEngagementOutputViewModel } from '@/lib/view-models/RecordEngagementOutputViewModel';

describe('AnalyticsService', () => {
  let mockApiClient: Mocked<AnalyticsApiClient>;
  let service: AnalyticsService;

  beforeEach(() => {
    mockApiClient = {
      recordPageView: vi.fn(),
      recordEngagement: vi.fn(),
    } as Mocked<AnalyticsApiClient>;

    service = new AnalyticsService(mockApiClient);
  });

  describe('recordPageView', () => {
    it('should call apiClient.recordPageView with correct input', async () => {
      const input = {
        path: '/dashboard',
        userId: 'user-123',
      };

      const expectedOutput = { pageViewId: 'pv-123' };
      mockApiClient.recordPageView.mockResolvedValue(expectedOutput);

      const result = await service.recordPageView(input);

      expect(mockApiClient.recordPageView).toHaveBeenCalledWith({
        entityType: 'page',
        entityId: '/dashboard',
        visitorType: 'user',
        sessionId: 'temp-session',
        path: '/dashboard',
        userId: 'user-123',
      });
      expect(result).toBeInstanceOf(RecordPageViewOutputViewModel);
      expect(result.pageViewId).toEqual('pv-123');
    });

    it('should call apiClient.recordPageView without userId when not provided', async () => {
      const input = {
        path: '/home',
      };

      const expectedOutput = { pageViewId: 'pv-456' };
      mockApiClient.recordPageView.mockResolvedValue(expectedOutput);

      const result = await service.recordPageView(input);

      expect(mockApiClient.recordPageView).toHaveBeenCalledWith({
        entityType: 'page',
        entityId: '/home',
        visitorType: 'guest',
        sessionId: 'temp-session',
        path: '/home',
      });
      expect(result).toBeInstanceOf(RecordPageViewOutputViewModel);
      expect(result.pageViewId).toEqual('pv-456');
    });
  });

  describe('recordEngagement', () => {
    it('should call apiClient.recordEngagement with correct input', async () => {
      const input = {
        eventType: 'button_click',
        userId: 'user-123',
        metadata: { buttonId: 'submit', page: '/form' },
      };

      const expectedOutput = { eventId: 'event-123', engagementWeight: 1.5 };
      mockApiClient.recordEngagement.mockResolvedValue(expectedOutput);

      const result = await service.recordEngagement(input);

      expect(mockApiClient.recordEngagement).toHaveBeenCalledWith({
        action: 'button_click',
        entityType: 'ui_element',
        entityId: 'unknown',
        actorType: 'user',
        sessionId: 'temp-session',
        eventType: 'button_click',
        userId: 'user-123',
        metadata: { buttonId: 'submit', page: '/form' },
      });
      expect(result).toBeInstanceOf(RecordEngagementOutputViewModel);
      expect(result.eventId).toEqual('event-123');
      expect(result.engagementWeight).toEqual(1.5);
    });

    it('should call apiClient.recordEngagement without optional fields', async () => {
      const input = {
        eventType: 'page_load',
      };

      const expectedOutput = { eventId: 'event-456', engagementWeight: 0.5 };
      mockApiClient.recordEngagement.mockResolvedValue(expectedOutput);

      const result = await service.recordEngagement(input);

      expect(mockApiClient.recordEngagement).toHaveBeenCalledWith({
        action: 'page_load',
        entityType: 'ui_element',
        entityId: 'unknown',
        actorType: 'guest',
        sessionId: 'temp-session',
        eventType: 'page_load',
      });
      expect(result).toBeInstanceOf(RecordEngagementOutputViewModel);
      expect(result.eventId).toEqual('event-456');
      expect(result.engagementWeight).toEqual(0.5);
    });
  });
});