import * as fs from 'node:fs';
import * as path from 'node:path';
import type { DataSource, Repository } from 'typeorm';
import { describe, expect, it, vi } from 'vitest';

import { User } from '@core/identity/domain/entities/User';
import { EmailAddress } from '@core/identity/domain/value-objects/EmailAddress';
import { PasswordHash } from '@core/identity/domain/value-objects/PasswordHash';
import { UserId } from '@core/identity/domain/value-objects/UserId';
import { UserOrmEntity } from '../entities/UserOrmEntity';
import { UserOrmMapper } from '../mappers/UserOrmMapper';
import { TypeOrmAuthRepository } from './TypeOrmAuthRepository';

describe('TypeOrmAuthRepository', () => {
  it('does not construct its own mapper dependencies', () => {
    const sourcePath = path.resolve(__dirname, 'TypeOrmAuthRepository.ts');
    const source = fs.readFileSync(sourcePath, 'utf8');

    expect(source).not.toMatch(/new\s+UserOrmMapper\s*\(/);
    expect(source).not.toMatch(/=\s*new\s+UserOrmMapper\s*\(/);
  });

  it('requires mapper injection via constructor (no default mapper)', () => {
    expect(TypeOrmAuthRepository.length).toBe(2);
  });

  it('uses the injected mapper at runtime (DB-free)', async () => {
    const ormRepo = {
      findOne: vi.fn().mockResolvedValue({ id: 'u1', email: 'test@example.com' }),
      save: vi.fn().mockResolvedValue({ id: 'u1' }),
    } as unknown as Repository<UserOrmEntity>;

    const dataSource = {
      getRepository: vi.fn().mockReturnValue(ormRepo),
    } as unknown as DataSource;

    const mapper = {
      toDomain: vi.fn().mockReturnValue({ 
        getId: () => ({ value: 'u1' }),
        getEmail: () => 'test@example.com',
        getDisplayName: () => 'Test User',
        getPasswordHash: () => ({ value: 'hash' }),
        getPrimaryDriverId: () => null,
      }),
      toOrmEntity: vi.fn().mockReturnValue({ id: 'u1', email: 'test@example.com' }),
    } as unknown as UserOrmMapper;

    const repo = new TypeOrmAuthRepository(dataSource, mapper);

    // Test findByEmail
    const email = EmailAddress.create('TEST@EXAMPLE.COM');
    const user = await repo.findByEmail(email);

    expect(dataSource.getRepository).toHaveBeenCalledTimes(1);
    expect(ormRepo.findOne).toHaveBeenCalledWith({ where: { email: 'test@example.com' } });
    expect(mapper.toDomain).toHaveBeenCalledTimes(1);
    expect(user).toBeDefined();

    // Test save
    const userId = UserId.create();
    const passwordHash = PasswordHash.fromHash('hash');
    const domainUser = User.create({
      id: userId,
      email: 'test@example.com',
      displayName: 'John Smith',
      passwordHash,
    });

    await repo.save(domainUser);
    expect(ormRepo.save).toHaveBeenCalled();
  });
});