import { PrismaClient } from '@crafted/database';
import {
  IUserRepository,
  User,
  Email,
  UserNotFoundError,
  Result,
  success,
  failure,
} from '@crafted/domain';

export class PrismaUserRepository implements IUserRepository {
  constructor(private prisma: PrismaClient) {}

  async findById(id: string): Promise<Result<User, UserNotFoundError>> {
    const dbUser = await this.prisma.user.findUnique({ where: { id } });

    if (!dbUser) {
      return failure(new UserNotFoundError(id));
    }

    return this.toDomain(dbUser);
  }

  async findByEmail(email: Email): Promise<Result<User, UserNotFoundError>> {
    const dbUser = await this.prisma.user.findUnique({
      where: { email: email.getValue() },
    });

    if (!dbUser) {
      return failure(new UserNotFoundError(email.getValue()));
    }

    return this.toDomain(dbUser);
  }

  async save(user: User): Promise<Result<User, Error>> {
    try {
      const dbUser = await this.prisma.user.upsert({
        where: { id: user.getId() },
        update: {
          name: user.getName(),
          email: user.getEmail().getValue(),
          updatedAt: new Date(),
        },
        create: {
          id: user.getId(),
          name: user.getName(),
          email: user.getEmail().getValue(),
        },
      });

      return this.toDomain(dbUser);
    } catch (error) {
      return failure(error as Error);
    }
  }

  async delete(id: string): Promise<Result<void, Error>> {
    try {
      await this.prisma.user.delete({ where: { id } });
      return success(undefined);
    } catch (error) {
      return failure(error as Error);
    }
  }

  private toDomain(dbUser: any): Result<User, Error> {
    const emailResult = Email.create(dbUser.email);
    if (emailResult.isFailure) {
      return failure(emailResult.error);
    }

    return success(
      User.reconstitute({
        id: dbUser.id,
        email: emailResult.value,
        name: dbUser.name || '',
        status: 'active',
        createdAt: dbUser.createdAt,
        updatedAt: dbUser.updatedAt,
      })
    );
  }
}
