import type { StoredUser } from '@core/identity/domain/repositories/UserRepository';
import type { Logger } from '@core/shared/domain/Logger';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { InMemoryUserRepository } from './InMemoryUserRepository';

describe('InMemoryUserRepository', () => {
  let repository: InMemoryUserRepository;
  let mockLogger: Logger;

  beforeEach(() => {
    mockLogger = {
      debug: vi.fn(),
      info: vi.fn(),
      warn: vi.fn(),
      error: vi.fn(),
    } as unknown as Logger;

    const seeded: StoredUser[] = [
      {
        id: 'u1',
        email: 'seed@example.com',
        displayName: 'Seed',
        passwordHash: 'hash',
        salt: 'salt',
        createdAt: new Date('2025-01-01T00:00:00.000Z'),
      },
    ];

    repository = new InMemoryUserRepository(mockLogger, seeded);
  });

  it('finds by email and id', async () => {
    expect((await repository.findByEmail('seed@example.com'))?.id).toBe('u1');
    expect((await repository.findById('u1'))?.email).toBe('seed@example.com');
    expect(await repository.findByEmail('missing@example.com')).toBeNull();
  });

  it('creates and rejects duplicate emails', async () => {
    const user: StoredUser = {
      id: 'u2',
      email: 'new@example.com',
      displayName: 'New',
      passwordHash: 'hash2',
      salt: 'salt2',
      createdAt: new Date(),
    };

    await repository.create(user);
    expect(await repository.emailExists('new@example.com')).toBe(true);

    await expect(repository.create({ ...user, id: 'u3' })).rejects.toThrow('Email already exists');
  });

  it('updates and maintains email index', async () => {
    const existing = await repository.findById('u1');
    expect(existing).not.toBeNull();

    const updated: StoredUser = {
      ...(existing as StoredUser),
      email: 'changed@example.com',
      displayName: 'Changed',
    };

    await repository.update(updated);

    expect(await repository.findByEmail('seed@example.com')).toBeNull();
    expect((await repository.findByEmail('changed@example.com'))?.displayName).toBe('Changed');
  });
});
