import { Test } from '@nestjs/testing';
import { JwtModule } from '@nestjs/jwt';
import { ConfigModule } from '@nestjs/config';
import { ConflictException, UnauthorizedException } from '@nestjs/common';
import * as bcrypt from 'bcrypt';
import { AuthService } from './auth.service';
import { PrismaService } from '../prisma/prisma.service';

describe('AuthService', () => {
  let service: AuthService;
  let prisma: PrismaService;
  const createdUsernames: string[] = [];

  beforeAll(async () => {
    const moduleRef = await Test.createTestingModule({
      imports: [
        ConfigModule.forRoot({ isGlobal: true }),
        JwtModule.register({
          secret: 'test-secret',
          signOptions: { expiresIn: '1h' },
        }),
      ],
      providers: [AuthService, PrismaService],
    }).compile();

    service = moduleRef.get(AuthService);
    prisma = moduleRef.get(PrismaService);
    await prisma.onModuleInit();
  });

  afterAll(async () => {
    // Cleanup created test users
    if (createdUsernames.length) {
      await prisma.user.deleteMany({
        where: { username: { in: createdUsernames } },
      });
    }
    await prisma.onModuleDestroy();
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });

  describe('register', () => {
    it('creates a new user and stores a hashed password', async () => {
      const username = `test_reg_${Date.now()}`;
      createdUsernames.push(username);

      const user = await service.register({ username, password: 'plain-pwd' });

      expect(user.username).toBe(username);
      expect(user.id).toBeTruthy();

      const stored = await prisma.user.findUnique({ where: { username } });
      expect(stored).not.toBeNull();
      expect(stored?.passwordHash).not.toBe('plain-pwd');
      const matches = await bcrypt.compare('plain-pwd', stored!.passwordHash);
      expect(matches).toBe(true);
    });

    it('throws ConflictException for duplicate usernames', async () => {
      const username = `test_dup_${Date.now()}`;
      createdUsernames.push(username);

      await service.register({ username, password: 'pwd1234' });
      await expect(
        service.register({ username, password: 'pwd5678' }),
      ).rejects.toBeInstanceOf(ConflictException);
    });
  });

  describe('login', () => {
    it('returns an access token for valid credentials', async () => {
      const username = `test_login_${Date.now()}`;
      createdUsernames.push(username);
      await service.register({ username, password: 'correct-pwd' });

      const result = await service.login({ username, password: 'correct-pwd' });
      expect(result.accessToken).toEqual(expect.any(String));
      const parts = result.accessToken.split('.');
      expect(parts.length).toBe(3);
      expect(result.user.username).toBe(username);
    });

    it('throws UnauthorizedException for wrong password', async () => {
      const username = `test_wrong_${Date.now()}`;
      createdUsernames.push(username);
      await service.register({ username, password: 'right-pwd' });

      await expect(
        service.login({ username, password: 'wrong-pwd' }),
      ).rejects.toBeInstanceOf(UnauthorizedException);
    });

    it('throws UnauthorizedException for unknown user', async () => {
      await expect(
        service.login({ username: 'no-such-user-xyz', password: 'whatever' }),
      ).rejects.toBeInstanceOf(UnauthorizedException);
    });
  });
});
