import { Result } from '@core/shared/domain/Result';
import { describe, expect, it, vi } from 'vitest';
import { AnalyticsService } from './AnalyticsService';
import { GetAnalyticsMetricsPresenter } from './presenters/GetAnalyticsMetricsPresenter';
import { GetDashboardDataPresenter } from './presenters/GetDashboardDataPresenter';
import { RecordEngagementPresenter } from './presenters/RecordEngagementPresenter';
import { RecordPageViewPresenter } from './presenters/RecordPageViewPresenter';

describe('AnalyticsService', () => {
  it('recordPageView returns presenter response on success', async () => {
    const recordPageViewPresenter = new RecordPageViewPresenter();

    const recordPageViewUseCase = {
      execute: vi.fn(async () => {
        return Result.ok({ pageViewId: 'pv-1' });
      }),
    };

    const service = new AnalyticsService(
      recordPageViewUseCase as never,
      { execute: vi.fn() } as never,
      { execute: vi.fn() } as never,
      { execute: vi.fn() } as never,
      recordPageViewPresenter,
      new RecordEngagementPresenter(),
      new GetDashboardDataPresenter(),
      new GetAnalyticsMetricsPresenter(),
    );

    const dto = await service.recordPageView({
      entityType: 'league' as never,
      entityId: 'l1',
      visitorType: 'anonymous' as never,
      sessionId: 's1',
    });

    expect(dto).toEqual({ pageViewId: 'pv-1' });
    expect(recordPageViewUseCase.execute).toHaveBeenCalled();
  });

  it('recordPageView throws on use case error', async () => {
    const service = new AnalyticsService(
      { execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR', details: { message: 'nope' } })) } as never,
      { execute: vi.fn() } as never,
      { execute: vi.fn() } as never,
      { execute: vi.fn() } as never,
      new RecordPageViewPresenter(),
      new RecordEngagementPresenter(),
      new GetDashboardDataPresenter(),
      new GetAnalyticsMetricsPresenter(),
    );

    await expect(
      service.recordPageView({ entityType: 'league' as never, entityId: 'l1', visitorType: 'anonymous' as never, sessionId: 's1' }),
    ).rejects.toThrow('nope');
  });

  it('recordPageView throws with fallback message when no details.message', async () => {
    const service = new AnalyticsService(
      { execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR' } as never)) } as never,
      { execute: vi.fn() } as never,
      { execute: vi.fn() } as never,
      { execute: vi.fn() } as never,
      new RecordPageViewPresenter(),
      new RecordEngagementPresenter(),
      new GetDashboardDataPresenter(),
      new GetAnalyticsMetricsPresenter(),
    );

    await expect(
      service.recordPageView({ entityType: 'league' as never, entityId: 'l1', visitorType: 'anonymous' as never, sessionId: 's1' }),
    ).rejects.toThrow('Failed to record page view');
  });

  it('recordEngagement returns response on success', async () => {
    const recordEngagementPresenter = new RecordEngagementPresenter();
    const recordEngagementUseCase = {
      execute: vi.fn(async () => {
        return Result.ok({ eventId: 'e1', engagementWeight: 7 });
      }),
    };

    const service = new AnalyticsService(
      { execute: vi.fn() } as never,
      recordEngagementUseCase as never,
      { execute: vi.fn() } as never,
      { execute: vi.fn() } as never,
      new RecordPageViewPresenter(),
      recordEngagementPresenter,
      new GetDashboardDataPresenter(),
      new GetAnalyticsMetricsPresenter(),
    );

    const dto = await service.recordEngagement({
      action: 'click' as never,
      entityType: 'league' as never,
      entityId: 'l1',
      actorType: 'anonymous',
      sessionId: 's1',
    });

    expect(dto).toEqual({ eventId: 'e1', engagementWeight: 7 });
  });

  it('recordEngagement throws with details message on error', async () => {
    const service = new AnalyticsService(
      { execute: vi.fn() } as never,
      { execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR', details: { message: 'nope' } })) } as never,
      { execute: vi.fn() } as never,
      { execute: vi.fn() } as never,
      new RecordPageViewPresenter(),
      new RecordEngagementPresenter(),
      new GetDashboardDataPresenter(),
      new GetAnalyticsMetricsPresenter(),
    );

    await expect(
      service.recordEngagement({
        action: 'click' as never,
        entityType: 'league' as never,
        entityId: 'l1',
        actorType: 'anonymous',
        sessionId: 's1',
      }),
    ).rejects.toThrow('nope');
  });

  it('recordEngagement throws with fallback message when no details.message', async () => {
    const service = new AnalyticsService(
      { execute: vi.fn() } as never,
      { execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR' } as never)) } as never,
      { execute: vi.fn() } as never,
      { execute: vi.fn() } as never,
      new RecordPageViewPresenter(),
      new RecordEngagementPresenter(),
      new GetDashboardDataPresenter(),
      new GetAnalyticsMetricsPresenter(),
    );

    await expect(
      service.recordEngagement({
        action: 'click' as never,
        entityType: 'league' as never,
        entityId: 'l1',
        actorType: 'anonymous',
        sessionId: 's1',
      }),
    ).rejects.toThrow('Failed to record engagement');
  });

  it('getDashboardData returns response on success', async () => {
    const getDashboardDataPresenter = new GetDashboardDataPresenter();
    const getDashboardDataUseCase = {
      execute: vi.fn(async () => {
        return Result.ok({
          totalUsers: 1,
          activeUsers: 2,
          totalRaces: 3,
          totalLeagues: 4,
        });
      }),
    };

    const service = new AnalyticsService(
      { execute: vi.fn() } as never,
      { execute: vi.fn() } as never,
      getDashboardDataUseCase as never,
      { execute: vi.fn() } as never,
      new RecordPageViewPresenter(),
      new RecordEngagementPresenter(),
      getDashboardDataPresenter,
      new GetAnalyticsMetricsPresenter(),
    );

    await expect(service.getDashboardData()).resolves.toEqual({
      totalUsers: 1,
      activeUsers: 2,
      totalRaces: 3,
      totalLeagues: 4,
    });
  });

  it('getDashboardData throws with details message on error', async () => {
    const service = new AnalyticsService(
      { execute: vi.fn() } as never,
      { execute: vi.fn() } as never,
      { execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR', details: { message: 'boom' } })) } as never,
      { execute: vi.fn() } as never,
      new RecordPageViewPresenter(),
      new RecordEngagementPresenter(),
      new GetDashboardDataPresenter(),
      new GetAnalyticsMetricsPresenter(),
    );

    await expect(service.getDashboardData()).rejects.toThrow('boom');
  });

  it('getDashboardData throws with fallback message when no details.message', async () => {
    const service = new AnalyticsService(
      { execute: vi.fn() } as never,
      { execute: vi.fn() } as never,
      { execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR' } as never)) } as never,
      { execute: vi.fn() } as never,
      new RecordPageViewPresenter(),
      new RecordEngagementPresenter(),
      new GetDashboardDataPresenter(),
      new GetAnalyticsMetricsPresenter(),
    );

    await expect(service.getDashboardData()).rejects.toThrow('Failed to get dashboard data');
  });

  it('getAnalyticsMetrics returns response on success', async () => {
    const getAnalyticsMetricsPresenter = new GetAnalyticsMetricsPresenter();
    const getAnalyticsMetricsUseCase = {
      execute: vi.fn(async () => {
        return Result.ok({
          pageViews: 10,
          uniqueVisitors: 0,
          averageSessionDuration: 0,
          bounceRate: 0,
        });
      }),
    };

    const service = new AnalyticsService(
      { execute: vi.fn() } as never,
      { execute: vi.fn() } as never,
      { execute: vi.fn() } as never,
      getAnalyticsMetricsUseCase as never,
      new RecordPageViewPresenter(),
      new RecordEngagementPresenter(),
      new GetDashboardDataPresenter(),
      getAnalyticsMetricsPresenter,
    );

    await expect(service.getAnalyticsMetrics()).resolves.toEqual({
      pageViews: 10,
      uniqueVisitors: 0,
      averageSessionDuration: 0,
      bounceRate: 0,
    });
  });

  it('getAnalyticsMetrics throws with details message on error', async () => {
    const service = new AnalyticsService(
      { execute: vi.fn() } as never,
      { execute: vi.fn() } as never,
      { execute: vi.fn() } as never,
      { execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR', details: { message: 'boom' } })) } as never,
      new RecordPageViewPresenter(),
      new RecordEngagementPresenter(),
      new GetDashboardDataPresenter(),
      new GetAnalyticsMetricsPresenter(),
    );

    await expect(service.getAnalyticsMetrics()).rejects.toThrow('boom');
  });

  it('getAnalyticsMetrics throws with fallback message when no details.message', async () => {
    const service = new AnalyticsService(
      { execute: vi.fn() } as never,
      { execute: vi.fn() } as never,
      { execute: vi.fn() } as never,
      { execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR' } as never)) } as never,
      new RecordPageViewPresenter(),
      new RecordEngagementPresenter(),
      new GetDashboardDataPresenter(),
      new GetAnalyticsMetricsPresenter(),
    );

    await expect(service.getAnalyticsMetrics()).rejects.toThrow('Failed to get analytics metrics');
  });
});