import { Test, TestingModule } from '@nestjs/testing';
import { describe, expect, it, vi } from 'vitest';
import { AnalyticsProviders } from './AnalyticsProviders';
import { AnalyticsService } from './AnalyticsService';
import { RecordPageViewPresenter } from './presenters/RecordPageViewPresenter';
import { RecordEngagementPresenter } from './presenters/RecordEngagementPresenter';
import { GetDashboardDataPresenter } from './presenters/GetDashboardDataPresenter';
import { GetAnalyticsMetricsPresenter } from './presenters/GetAnalyticsMetricsPresenter';
import { RecordPageViewUseCase } from '@core/analytics/application/use-cases/RecordPageViewUseCase';
import { RecordEngagementUseCase } from '@core/analytics/application/use-cases/RecordEngagementUseCase';
import { GetDashboardDataUseCase } from '@core/analytics/application/use-cases/GetDashboardDataUseCase';
import { GetAnalyticsMetricsUseCase } from '@core/analytics/application/use-cases/GetAnalyticsMetricsUseCase';
import { ANALYTICS_ENGAGEMENT_REPOSITORY_TOKEN, ANALYTICS_PAGE_VIEW_REPOSITORY_TOKEN } from '../../persistence/analytics/AnalyticsPersistenceTokens';

describe('AnalyticsProviders', () => {
  describe('AnalyticsService', () => {
    it('should be defined as a provider', () => {
      const provider = AnalyticsProviders.find(p => p === AnalyticsService);
      expect(provider).toBeDefined();
    });
  });

  describe('RecordPageViewPresenter', () => {
    it('should be defined as a provider', () => {
      const provider = AnalyticsProviders.find(p => p === RecordPageViewPresenter);
      expect(provider).toBeDefined();
    });
  });

  describe('RecordEngagementPresenter', () => {
    it('should be defined as a provider', () => {
      const provider = AnalyticsProviders.find(p => p === RecordEngagementPresenter);
      expect(provider).toBeDefined();
    });
  });

  describe('GetDashboardDataPresenter', () => {
    it('should be defined as a provider', () => {
      const provider = AnalyticsProviders.find(p => p === GetDashboardDataPresenter);
      expect(provider).toBeDefined();
    });
  });

  describe('GetAnalyticsMetricsPresenter', () => {
    it('should be defined as a provider', () => {
      const provider = AnalyticsProviders.find(p => p === GetAnalyticsMetricsPresenter);
      expect(provider).toBeDefined();
    });
  });

  describe('RecordPageViewUseCase', () => {
    it('should be defined as a provider with useFactory', () => {
      const provider = AnalyticsProviders.find(
        p => typeof p === 'object' && 'provide' in p && p.provide === RecordPageViewUseCase,
      );
      expect(provider).toBeDefined();
      expect(provider).toHaveProperty('useFactory');
      expect(provider).toHaveProperty('inject');
    });

    it('should inject correct dependencies', () => {
      const provider = AnalyticsProviders.find(
        p => typeof p === 'object' && 'provide' in p && p.provide === RecordPageViewUseCase,
      ) as { inject: string[] };

      expect(provider.inject).toEqual([ANALYTICS_PAGE_VIEW_REPOSITORY_TOKEN, 'Logger']);
    });
  });

  describe('RecordEngagementUseCase', () => {
    it('should be defined as a provider with useFactory', () => {
      const provider = AnalyticsProviders.find(
        p => typeof p === 'object' && 'provide' in p && p.provide === RecordEngagementUseCase,
      );
      expect(provider).toBeDefined();
      expect(provider).toHaveProperty('useFactory');
      expect(provider).toHaveProperty('inject');
    });

    it('should inject correct dependencies', () => {
      const provider = AnalyticsProviders.find(
        p => typeof p === 'object' && 'provide' in p && p.provide === RecordEngagementUseCase,
      ) as { inject: string[] };

      expect(provider.inject).toEqual([ANALYTICS_ENGAGEMENT_REPOSITORY_TOKEN, 'Logger']);
    });
  });

  describe('GetDashboardDataUseCase', () => {
    it('should be defined as a provider with useFactory', () => {
      const provider = AnalyticsProviders.find(
        p => typeof p === 'object' && 'provide' in p && p.provide === GetDashboardDataUseCase,
      );
      expect(provider).toBeDefined();
      expect(provider).toHaveProperty('useFactory');
      expect(provider).toHaveProperty('inject');
    });

    it('should inject correct dependencies', () => {
      const provider = AnalyticsProviders.find(
        p => typeof p === 'object' && 'provide' in p && p.provide === GetDashboardDataUseCase,
      ) as { inject: string[] };

      expect(provider.inject).toEqual(['Logger']);
    });
  });

  describe('GetAnalyticsMetricsUseCase', () => {
    it('should be defined as a provider with useFactory', () => {
      const provider = AnalyticsProviders.find(
        p => typeof p === 'object' && 'provide' in p && p.provide === GetAnalyticsMetricsUseCase,
      );
      expect(provider).toBeDefined();
      expect(provider).toHaveProperty('useFactory');
      expect(provider).toHaveProperty('inject');
    });

    it('should inject correct dependencies', () => {
      const provider = AnalyticsProviders.find(
        p => typeof p === 'object' && 'provide' in p && p.provide === GetAnalyticsMetricsUseCase,
      ) as { inject: string[] };

      expect(provider.inject).toEqual(['Logger', ANALYTICS_PAGE_VIEW_REPOSITORY_TOKEN]);
    });
  });

  describe('useFactory functions', () => {
    it('should create RecordPageViewUseCase with correct dependencies', async () => {
      const module: TestingModule = await Test.createTestingModule({
        providers: [
          ...AnalyticsProviders,
          {
            provide: ANALYTICS_PAGE_VIEW_REPOSITORY_TOKEN,
            useValue: {
              save: vi.fn(),
              findById: vi.fn(),
            },
          },
          {
            provide: 'Logger',
            useValue: {
              info: vi.fn(),
              error: vi.fn(),
              warn: vi.fn(),
              debug: vi.fn(),
            },
          },
        ],
      }).compile();

      const useCase = module.get<RecordPageViewUseCase>(RecordPageViewUseCase);
      expect(useCase).toBeDefined();
      expect(useCase).toBeInstanceOf(RecordPageViewUseCase);
    });

    it('should create RecordEngagementUseCase with correct dependencies', async () => {
      const module: TestingModule = await Test.createTestingModule({
        providers: [
          ...AnalyticsProviders,
          {
            provide: ANALYTICS_ENGAGEMENT_REPOSITORY_TOKEN,
            useValue: {
              save: vi.fn(),
              findById: vi.fn(),
            },
          },
          {
            provide: 'Logger',
            useValue: {
              info: vi.fn(),
              error: vi.fn(),
              warn: vi.fn(),
              debug: vi.fn(),
            },
          },
        ],
      }).compile();

      const useCase = module.get<RecordEngagementUseCase>(RecordEngagementUseCase);
      expect(useCase).toBeDefined();
      expect(useCase).toBeInstanceOf(RecordEngagementUseCase);
    });

    it('should create GetDashboardDataUseCase with correct dependencies', async () => {
      const module: TestingModule = await Test.createTestingModule({
        providers: [
          ...AnalyticsProviders,
          {
            provide: 'Logger',
            useValue: {
              info: vi.fn(),
              error: vi.fn(),
              warn: vi.fn(),
              debug: vi.fn(),
            },
          },
        ],
      }).compile();

      const useCase = module.get<GetDashboardDataUseCase>(GetDashboardDataUseCase);
      expect(useCase).toBeDefined();
      expect(useCase).toBeInstanceOf(GetDashboardDataUseCase);
    });

    it('should create GetAnalyticsMetricsUseCase with correct dependencies', async () => {
      const module: TestingModule = await Test.createTestingModule({
        providers: [
          ...AnalyticsProviders,
          {
            provide: ANALYTICS_PAGE_VIEW_REPOSITORY_TOKEN,
            useValue: {
              save: vi.fn(),
              findById: vi.fn(),
              findAll: vi.fn(),
            },
          },
          {
            provide: 'Logger',
            useValue: {
              info: vi.fn(),
              error: vi.fn(),
              warn: vi.fn(),
              debug: vi.fn(),
            },
          },
        ],
      }).compile();

      const useCase = module.get<GetAnalyticsMetricsUseCase>(GetAnalyticsMetricsUseCase);
      expect(useCase).toBeDefined();
      expect(useCase).toBeInstanceOf(GetAnalyticsMetricsUseCase);
    });
  });
});
