import { describe, it, expect, beforeEach } from "vitest";
import bcrypt from "bcrypt";
import { InMemoryUsersRepository } from "../repositories/in-memory-users-repository";
import { RegisterUserUseCase } from "./register-user-use-case";
import { AppError } from "@/core/errors/app-error";

let usersRepository: InMemoryUsersRepository;
let sut: RegisterUserUseCase;

describe("Register User Use Case", () => {
  beforeEach(() => {
    usersRepository = new InMemoryUsersRepository();
    sut = new RegisterUserUseCase(usersRepository);
  });

  it("should register a new user with valid data", async () => {
    const { user } = await sut.execute({
      body: {
        name: "Novo Usuário",
        email: "novo@exemplo.com",
        password: "Password@123",
        confirmPassword: "Password@123",
      },
    });

    expect(user.id).toBeDefined();
    expect(user.name).toBe("Novo Usuário");
    expect(user.email).toBe("novo@exemplo.com");

    const userInDb = await usersRepository.findByEmail("novo@exemplo.com");
    expect(userInDb).not.toBeNull();
    const isPasswordHashed = await bcrypt.compare("Password@123", userInDb!.passwordHash);
    expect(isPasswordHashed).toBe(true);
  });

  it("should reject when password and confirmPassword do not match", async () => {
    await expect(
      sut.execute({
        body: {
          name: "Usuário Teste",
          email: "mismatch@exemplo.com",
          password: "Password@123",
          confirmPassword: "Password@999",
        },
      })
    ).rejects.toThrow("As senhas não coincidem");
  });

  it("should reject password without special characters", async () => {
    await expect(
      sut.execute({
        body: {
          name: "Usuário Teste",
          email: "nospecial@exemplo.com",
          password: "Password123",
          confirmPassword: "Password123",
        },
      })
    ).rejects.toBeInstanceOf(AppError);
  });

  it("should reject password without numbers", async () => {
    await expect(
      sut.execute({
        body: {
          name: "Usuário Teste",
          email: "nonumbers@exemplo.com",
          password: "Password@abc",
          confirmPassword: "Password@abc",
        },
      })
    ).rejects.toBeInstanceOf(AppError);
  });

  it("should reject password shorter than 6 characters", async () => {
    await expect(
      sut.execute({
        body: {
          name: "Usuário Teste",
          email: "short@exemplo.com",
          password: "P@12",
          confirmPassword: "P@12",
        },
      })
    ).rejects.toBeInstanceOf(AppError);
  });

  it("should not allow registration with an existing email", async () => {
    await sut.execute({
      body: {
        name: "Primeiro Usuário",
        email: "existente@exemplo.com",
        password: "Password@123",
        confirmPassword: "Password@123",
      },
    });

    await expect(
      sut.execute({
        body: {
          name: "Segundo Usuário",
          email: "existente@exemplo.com",
          password: "Password@456",
          confirmPassword: "Password@456",
        },
      })
    ).rejects.toBeInstanceOf(AppError);
  });

  it("should reject invalid email", async () => {
    await expect(
      sut.execute({
        body: {
          name: "Usuário Teste",
          email: "email-invalido",
          password: "Password@123",
          confirmPassword: "Password@123",
        },
      })
    ).rejects.toBeInstanceOf(AppError);
  });
});
