import { AdminUser } from '@core/admin/domain/entities/AdminUser';
import { AuthorizationService } from '@core/admin/domain/services/AuthorizationService';
import { Result } from '@core/shared/domain/Result';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { GetDashboardStatsUseCase } from './GetDashboardStatsUseCase';

// Mock dependencies
const mockAdminUserRepo = {
  findById: vi.fn(),
  list: vi.fn(),
};

describe('GetDashboardStatsUseCase', () => {
  describe('TDD - Test First', () => {
    let useCase: GetDashboardStatsUseCase;

    beforeEach(() => {
      vi.clearAllMocks();
      useCase = new GetDashboardStatsUseCase(mockAdminUserRepo as never);
    });

    describe('execute', () => {
      it('should return error when actor is not found', async () => {
        // Arrange
        mockAdminUserRepo.findById.mockResolvedValue(null);

        // Act
        const result = await useCase.execute({ actorId: 'actor-1' });

        // Assert
        expect(result.isErr()).toBe(true);
        const error = result.unwrapErr();
        expect(error.code).toBe('AUTHORIZATION_ERROR');
        expect(error.details.message).toBe('Actor not found');
      });

      it('should return error when actor is not authorized to view dashboard', async () => {
        // Arrange
        const actor = AdminUser.create({
          id: 'actor-1',
          email: 'actor@example.com',
          displayName: 'Actor',
          roles: ['user'],
          status: 'active',
        });

        mockAdminUserRepo.findById.mockResolvedValue(actor);
        mockAdminUserRepo.list.mockResolvedValue({ users: [] });

        // Act
        const result = await useCase.execute({ actorId: 'actor-1' });

        // Assert
        expect(result.isErr()).toBe(true);
        const error = result.unwrapErr();
        expect(error.code).toBe('AUTHORIZATION_ERROR');
        expect(error.details.message).toBe('User is not authorized to view dashboard');
      });

      it('should return empty stats when no users exist', async () => {
        // Arrange
        const actor = AdminUser.create({
          id: 'actor-1',
          email: 'actor@example.com',
          displayName: 'Actor',
          roles: ['owner'],
          status: 'active',
        });

        mockAdminUserRepo.findById.mockResolvedValue(actor);
        mockAdminUserRepo.list.mockResolvedValue({ users: [] });

        // Act
        const result = await useCase.execute({ actorId: 'actor-1' });

        // Assert
        expect(result.isOk()).toBe(true);
        const stats = result.unwrap();
        expect(stats.totalUsers).toBe(0);
        expect(stats.activeUsers).toBe(0);
        expect(stats.suspendedUsers).toBe(0);
        expect(stats.deletedUsers).toBe(0);
        expect(stats.systemAdmins).toBe(0);
        expect(stats.recentLogins).toBe(0);
        expect(stats.newUsersToday).toBe(0);
        expect(stats.userGrowth).toEqual([]);
        expect(stats.roleDistribution).toEqual([]);
        expect(stats.statusDistribution).toEqual({
          active: 0,
          suspended: 0,
          deleted: 0,
        });
        expect(stats.activityTimeline).toEqual([]);
      });

      it('should return correct stats when users exist', async () => {
        // Arrange
        const actor = AdminUser.create({
          id: 'actor-1',
          email: 'actor@example.com',
          displayName: 'Actor',
          roles: ['owner'],
          status: 'active',
        });

        const user1 = AdminUser.create({
          id: 'user-1',
          email: 'user1@example.com',
          displayName: 'User 1',
          roles: ['user'],
          status: 'active',
          createdAt: new Date(),
          updatedAt: new Date(),
        });

        const user2 = AdminUser.create({
          id: 'user-2',
          email: 'user2@example.com',
          displayName: 'User 2',
          roles: ['admin'],
          status: 'suspended',
          createdAt: new Date(),
          updatedAt: new Date(),
        });

        const user3 = AdminUser.create({
          id: 'user-3',
          email: 'user3@example.com',
          displayName: 'User 3',
          roles: ['owner'],
          status: 'deleted',
          createdAt: new Date(),
          updatedAt: new Date(),
        });

        mockAdminUserRepo.findById.mockResolvedValue(actor);
        mockAdminUserRepo.list.mockResolvedValue({ users: [user1, user2, user3] });

        // Act
        const result = await useCase.execute({ actorId: 'actor-1' });

        // Assert
        expect(result.isOk()).toBe(true);
        const stats = result.unwrap();
        expect(stats.totalUsers).toBe(3);
        expect(stats.activeUsers).toBe(1);
        expect(stats.suspendedUsers).toBe(1);
        expect(stats.deletedUsers).toBe(1);
        expect(stats.systemAdmins).toBe(2); // actor + user3
        expect(stats.recentLogins).toBe(0); // no recent logins
        expect(stats.newUsersToday).toBe(3); // all created today
        expect(stats.userGrowth).toHaveLength(7);
        expect(stats.roleDistribution).toHaveLength(3);
        expect(stats.statusDistribution).toEqual({
          active: 1,
          suspended: 1,
          deleted: 1,
        });
        expect(stats.activityTimeline).toHaveLength(7);
      });

      it('should count recent logins correctly', async () => {
        // Arrange
        const actor = AdminUser.create({
          id: 'actor-1',
          email: 'actor@example.com',
          displayName: 'Actor',
          roles: ['owner'],
          status: 'active',
        });

        const recentLoginUser = AdminUser.create({
          id: 'user-1',
          email: 'user1@example.com',
          displayName: 'User 1',
          roles: ['user'],
          status: 'active',
          createdAt: new Date(Date.now() - 86400000 * 2), // 2 days ago
          updatedAt: new Date(Date.now() - 86400000 * 2),
          lastLoginAt: new Date(Date.now() - 3600000), // 1 hour ago
        });

        const oldLoginUser = AdminUser.create({
          id: 'user-2',
          email: 'user2@example.com',
          displayName: 'User 2',
          roles: ['user'],
          status: 'active',
          createdAt: new Date(Date.now() - 86400000 * 2),
          updatedAt: new Date(Date.now() - 86400000 * 2),
          lastLoginAt: new Date(Date.now() - 86400000 * 2), // 2 days ago
        });

        mockAdminUserRepo.findById.mockResolvedValue(actor);
        mockAdminUserRepo.list.mockResolvedValue({ users: [recentLoginUser, oldLoginUser] });

        // Act
        const result = await useCase.execute({ actorId: 'actor-1' });

        // Assert
        expect(result.isOk()).toBe(true);
        const stats = result.unwrap();
        expect(stats.recentLogins).toBe(1);
      });

      it('should count new users today correctly', async () => {
        // Arrange
        const actor = AdminUser.create({
          id: 'actor-1',
          email: 'actor@example.com',
          displayName: 'Actor',
          roles: ['owner'],
          status: 'active',
        });

        const todayUser = AdminUser.create({
          id: 'user-1',
          email: 'user1@example.com',
          displayName: 'User 1',
          roles: ['user'],
          status: 'active',
          createdAt: new Date(),
          updatedAt: new Date(),
        });

        const yesterdayUser = AdminUser.create({
          id: 'user-2',
          email: 'user2@example.com',
          displayName: 'User 2',
          roles: ['user'],
          status: 'active',
          createdAt: new Date(Date.now() - 86400000),
          updatedAt: new Date(Date.now() - 86400000),
        });

        mockAdminUserRepo.findById.mockResolvedValue(actor);
        mockAdminUserRepo.list.mockResolvedValue({ users: [todayUser, yesterdayUser] });

        // Act
        const result = await useCase.execute({ actorId: 'actor-1' });

        // Assert
        expect(result.isOk()).toBe(true);
        const stats = result.unwrap();
        expect(stats.newUsersToday).toBe(1);
      });

      it('should calculate role distribution correctly', async () => {
        // Arrange
        const actor = AdminUser.create({
          id: 'actor-1',
          email: 'actor@example.com',
          displayName: 'Actor',
          roles: ['owner'],
          status: 'active',
        });

        const user1 = AdminUser.create({
          id: 'user-1',
          email: 'user1@example.com',
          displayName: 'User 1',
          roles: ['user'],
          status: 'active',
          createdAt: new Date(),
          updatedAt: new Date(),
        });

        const user2 = AdminUser.create({
          id: 'user-2',
          email: 'user2@example.com',
          displayName: 'User 2',
          roles: ['admin'],
          status: 'active',
          createdAt: new Date(),
          updatedAt: new Date(),
        });

        const user3 = AdminUser.create({
          id: 'user-3',
          email: 'user3@example.com',
          displayName: 'User 3',
          roles: ['owner'],
          status: 'active',
          createdAt: new Date(),
          updatedAt: new Date(),
        });

        mockAdminUserRepo.findById.mockResolvedValue(actor);
        mockAdminUserRepo.list.mockResolvedValue({ users: [user1, user2, user3] });

        // Act
        const result = await useCase.execute({ actorId: 'actor-1' });

        // Assert
        expect(result.isOk()).toBe(true);
        const stats = result.unwrap();
        expect(stats.roleDistribution).toHaveLength(3);
        expect(stats.roleDistribution).toContainEqual({
          label: 'Owner',
          value: 2,
          color: 'text-purple-500',
        });
        expect(stats.roleDistribution).toContainEqual({
          label: 'Admin',
          value: 1,
          color: 'text-blue-500',
        });
        expect(stats.roleDistribution).toContainEqual({
          label: 'User',
          value: 1,
          color: 'text-gray-500',
        });
      });

      it('should handle users with multiple roles', async () => {
        // Arrange
        const actor = AdminUser.create({
          id: 'actor-1',
          email: 'actor@example.com',
          displayName: 'Actor',
          roles: ['owner'],
          status: 'active',
        });

        const user1 = AdminUser.create({
          id: 'user-1',
          email: 'user1@example.com',
          displayName: 'User 1',
          roles: ['user', 'admin'],
          status: 'active',
          createdAt: new Date(),
          updatedAt: new Date(),
        });

        mockAdminUserRepo.findById.mockResolvedValue(actor);
        mockAdminUserRepo.list.mockResolvedValue({ users: [user1] });

        // Act
        const result = await useCase.execute({ actorId: 'actor-1' });

        // Assert
        expect(result.isOk()).toBe(true);
        const stats = result.unwrap();
        expect(stats.roleDistribution).toHaveLength(2);
        expect(stats.roleDistribution).toContainEqual({
          label: 'User',
          value: 1,
          color: 'text-gray-500',
        });
        expect(stats.roleDistribution).toContainEqual({
          label: 'Admin',
          value: 1,
          color: 'text-blue-500',
        });
      });

      it('should calculate user growth for last 7 days', async () => {
        // Arrange
        const actor = AdminUser.create({
          id: 'actor-1',
          email: 'actor@example.com',
          displayName: 'Actor',
          roles: ['owner'],
          status: 'active',
        });

        const today = new Date();
        const yesterday = new Date(today);
        yesterday.setDate(yesterday.getDate() - 1);
        const twoDaysAgo = new Date(today);
        twoDaysAgo.setDate(twoDaysAgo.getDate() - 2);

        const user1 = AdminUser.create({
          id: 'user-1',
          email: 'user1@example.com',
          displayName: 'User 1',
          roles: ['user'],
          status: 'active',
          createdAt: today,
          updatedAt: today,
        });

        const user2 = AdminUser.create({
          id: 'user-2',
          email: 'user2@example.com',
          displayName: 'User 2',
          roles: ['user'],
          status: 'active',
          createdAt: yesterday,
          updatedAt: yesterday,
        });

        const user3 = AdminUser.create({
          id: 'user-3',
          email: 'user3@example.com',
          displayName: 'User 3',
          roles: ['user'],
          status: 'active',
          createdAt: twoDaysAgo,
          updatedAt: twoDaysAgo,
        });

        mockAdminUserRepo.findById.mockResolvedValue(actor);
        mockAdminUserRepo.list.mockResolvedValue({ users: [user1, user2, user3] });

        // Act
        const result = await useCase.execute({ actorId: 'actor-1' });

        // Assert
        expect(result.isOk()).toBe(true);
        const stats = result.unwrap();
        expect(stats.userGrowth).toHaveLength(7);
        
        // Check that today has 1 user
        const todayEntry = stats.userGrowth[6];
        expect(todayEntry.value).toBe(1);
        
        // Check that yesterday has 1 user
        const yesterdayEntry = stats.userGrowth[5];
        expect(yesterdayEntry.value).toBe(1);
        
        // Check that two days ago has 1 user
        const twoDaysAgoEntry = stats.userGrowth[4];
        expect(twoDaysAgoEntry.value).toBe(1);
      });

      it('should calculate activity timeline for last 7 days', async () => {
        // Arrange
        const actor = AdminUser.create({
          id: 'actor-1',
          email: 'actor@example.com',
          displayName: 'Actor',
          roles: ['owner'],
          status: 'active',
        });

        const today = new Date();
        const yesterday = new Date(today);
        yesterday.setDate(yesterday.getDate() - 1);

        const newUser = AdminUser.create({
          id: 'user-1',
          email: 'user1@example.com',
          displayName: 'User 1',
          roles: ['user'],
          status: 'active',
          createdAt: today,
          updatedAt: today,
        });

        const recentLoginUser = AdminUser.create({
          id: 'user-2',
          email: 'user2@example.com',
          displayName: 'User 2',
          roles: ['user'],
          status: 'active',
          createdAt: yesterday,
          updatedAt: yesterday,
          lastLoginAt: new Date(Date.now() - 3600000), // 1 hour ago
        });

        mockAdminUserRepo.findById.mockResolvedValue(actor);
        mockAdminUserRepo.list.mockResolvedValue({ users: [newUser, recentLoginUser] });

        // Act
        const result = await useCase.execute({ actorId: 'actor-1' });

        // Assert
        expect(result.isOk()).toBe(true);
        const stats = result.unwrap();
        expect(stats.activityTimeline).toHaveLength(7);
        
        // Check today's entry
        const todayEntry = stats.activityTimeline[6];
        expect(todayEntry.newUsers).toBe(1);
        expect(todayEntry.logins).toBe(1);
        
        // Check yesterday's entry
        const yesterdayEntry = stats.activityTimeline[5];
        expect(yesterdayEntry.newUsers).toBe(0);
        expect(yesterdayEntry.logins).toBe(0);
      });

      it('should handle repository errors gracefully', async () => {
        // Arrange
        const actor = AdminUser.create({
          id: 'actor-1',
          email: 'actor@example.com',
          displayName: 'Actor',
          roles: ['owner'],
          status: 'active',
        });

        mockAdminUserRepo.findById.mockResolvedValue(actor);
        mockAdminUserRepo.list.mockRejectedValue(new Error('Database connection failed'));

        // Act
        const result = await useCase.execute({ actorId: 'actor-1' });

        // Assert
        expect(result.isErr()).toBe(true);
        const error = result.unwrapErr();
        expect(error.code).toBe('REPOSITORY_ERROR');
        expect(error.details.message).toBe('Database connection failed');
      });

      it('should handle non-Error exceptions', async () => {
        // Arrange
        const actor = AdminUser.create({
          id: 'actor-1',
          email: 'actor@example.com',
          displayName: 'Actor',
          roles: ['owner'],
          status: 'active',
        });

        mockAdminUserRepo.findById.mockResolvedValue(actor);
        mockAdminUserRepo.list.mockRejectedValue('String error');

        // Act
        const result = await useCase.execute({ actorId: 'actor-1' });

        // Assert
        expect(result.isErr()).toBe(true);
        const error = result.unwrapErr();
        expect(error.code).toBe('REPOSITORY_ERROR');
        expect(error.details.message).toBe('Failed to get dashboard stats');
      });

      it('should work with owner role', async () => {
        // Arrange
        const actor = AdminUser.create({
          id: 'actor-1',
          email: 'actor@example.com',
          displayName: 'Actor',
          roles: ['owner'],
          status: 'active',
        });

        mockAdminUserRepo.findById.mockResolvedValue(actor);
        mockAdminUserRepo.list.mockResolvedValue({ users: [] });

        // Act
        const result = await useCase.execute({ actorId: 'actor-1' });

        // Assert
        expect(result.isOk()).toBe(true);
      });

      it('should work with admin role', async () => {
        // Arrange
        const actor = AdminUser.create({
          id: 'actor-1',
          email: 'actor@example.com',
          displayName: 'Actor',
          roles: ['admin'],
          status: 'active',
        });

        mockAdminUserRepo.findById.mockResolvedValue(actor);
        mockAdminUserRepo.list.mockResolvedValue({ users: [] });

        // Act
        const result = await useCase.execute({ actorId: 'actor-1' });

        // Assert
        expect(result.isOk()).toBe(true);
      });

      it('should reject user role', async () => {
        // Arrange
        const actor = AdminUser.create({
          id: 'actor-1',
          email: 'actor@example.com',
          displayName: 'Actor',
          roles: ['user'],
          status: 'active',
        });

        mockAdminUserRepo.findById.mockResolvedValue(actor);
        mockAdminUserRepo.list.mockResolvedValue({ users: [] });

        // Act
        const result = await useCase.execute({ actorId: 'actor-1' });

        // Assert
        expect(result.isErr()).toBe(true);
        const error = result.unwrapErr();
        expect(error.code).toBe('AUTHORIZATION_ERROR');
      });

      it('should handle suspended actor', async () => {
        // Arrange
        const actor = AdminUser.create({
          id: 'actor-1',
          email: 'actor@example.com',
          displayName: 'Actor',
          roles: ['owner'],
          status: 'suspended',
        });

        mockAdminUserRepo.findById.mockResolvedValue(actor);
        mockAdminUserRepo.list.mockResolvedValue({ users: [] });

        // Act
        const result = await useCase.execute({ actorId: 'actor-1' });

        // Assert
        expect(result.isErr()).toBe(true);
        const error = result.unwrapErr();
        expect(error.code).toBe('AUTHORIZATION_ERROR');
      });

      it('should handle deleted actor', async () => {
        // Arrange
        const actor = AdminUser.create({
          id: 'actor-1',
          email: 'actor@example.com',
          displayName: 'Actor',
          roles: ['owner'],
          status: 'deleted',
        });

        mockAdminUserRepo.findById.mockResolvedValue(actor);
        mockAdminUserRepo.list.mockResolvedValue({ users: [] });

        // Act
        const result = await useCase.execute({ actorId: 'actor-1' });

        // Assert
        expect(result.isErr()).toBe(true);
        const error = result.unwrapErr();
        expect(error.code).toBe('AUTHORIZATION_ERROR');
      });

      it('should handle large number of users efficiently', async () => {
        // Arrange
        const actor = AdminUser.create({
          id: 'actor-1',
          email: 'actor@example.com',
          displayName: 'Actor',
          roles: ['owner'],
          status: 'active',
        });

        const users = Array.from({ length: 1000 }, (_, i) =>
          AdminUser.create({
            id: `user-${i}`,
            email: `user${i}@example.com`,
            displayName: `User ${i}`,
            roles: i % 3 === 0 ? ['owner'] : i % 3 === 1 ? ['admin'] : ['user'],
            status: i % 4 === 0 ? 'suspended' : i % 4 === 1 ? 'deleted' : 'active',
            createdAt: new Date(Date.now() - i * 3600000),
            updatedAt: new Date(Date.now() - i * 3600000),
            lastLoginAt: i % 10 === 0 ? new Date(Date.now() - i * 3600000) : undefined,
          })
        );

        mockAdminUserRepo.findById.mockResolvedValue(actor);
        mockAdminUserRepo.list.mockResolvedValue({ users });

        // Act
        const result = await useCase.execute({ actorId: 'actor-1' });

        // Assert
        expect(result.isOk()).toBe(true);
        const stats = result.unwrap();
        expect(stats.totalUsers).toBe(1000);
        expect(stats.activeUsers).toBe(500);
        expect(stats.suspendedUsers).toBe(250);
        expect(stats.deletedUsers).toBe(250);
        expect(stats.systemAdmins).toBe(334); // owner + admin
        expect(stats.recentLogins).toBe(100); // 10% of users
        expect(stats.userGrowth).toHaveLength(7);
        expect(stats.roleDistribution).toHaveLength(3);
        expect(stats.activityTimeline).toHaveLength(7);
      });
    });
  });
});
