import { vi, type Mock } from 'vitest';
import { GetCurrentSessionUseCase } from './GetCurrentSessionUseCase';
import { User } from '../../domain/entities/User';
import { IUserRepository, StoredUser } from '../../domain/repositories/IUserRepository';
import type { Logger, UseCaseOutputPort } from '@core/shared/application';

type GetCurrentSessionOutput = {
  user: User;
};

describe('GetCurrentSessionUseCase', () => {
  let useCase: GetCurrentSessionUseCase;
  let mockUserRepo: {
    findByEmail: Mock;
    findById: Mock;
    create: Mock;
    update: Mock;
    emailExists: Mock;
  };
  let logger: Logger;
  let output: UseCaseOutputPort<GetCurrentSessionOutput> & { present: Mock };

  beforeEach(() => {
    mockUserRepo = {
      findByEmail: vi.fn(),
      findById: vi.fn(),
      create: vi.fn(),
      update: vi.fn(),
      emailExists: vi.fn(),
    };
    logger = {
      debug: vi.fn(),
      info: vi.fn(),
      warn: vi.fn(),
      error: vi.fn(),
    } as unknown as Logger;
    output = {
      present: vi.fn(),
    };
    useCase = new GetCurrentSessionUseCase(
      mockUserRepo as IUserRepository,
      logger,
      output,
    );
  });

  it('should return User when user exists', async () => {
    const userId = 'user-123';
    const storedUser: StoredUser = {
      id: userId,
      email: 'test@example.com',
      displayName: 'Test User',
      passwordHash: 'hash',
      salt: 'salt',
      primaryDriverId: 'driver-123',
      createdAt: new Date(),
    };
    mockUserRepo.findById.mockResolvedValue(storedUser);

    const result = await useCase.execute({ userId });

    expect(mockUserRepo.findById).toHaveBeenCalledWith(userId);
    expect(result.isOk()).toBe(true);
    expect(output.present).toHaveBeenCalled();
    const callArgs = output.present.mock.calls?.[0]?.[0];
    expect(callArgs?.user).toBeInstanceOf(User);
    expect(callArgs?.user.getId().value).toBe(userId);
    expect(callArgs?.user.getDisplayName()).toBe('Test User');
  });

  it('should return error when user does not exist', async () => {
    const userId = 'user-123';
    mockUserRepo.findById.mockResolvedValue(null);

    const result = await useCase.execute({ userId });

    expect(mockUserRepo.findById).toHaveBeenCalledWith(userId);
    expect(result.isErr()).toBe(true);
  });
});