import prisma from "../config/prisma.js";
import { Prisma } from "../generated/prisma/client.js";

export class UserRepository {
  async findByEmail(email: string) {
    return prisma.user.findUnique({
      where: { email },
      include: { role: true },
    });
  }

  async findByMobileNo(mobileNo: string) {
    return prisma.user.findUnique({
      where: { mobileNo },
      include: { role: true },
    });
  }

  async create(data: Prisma.UserUncheckedCreateInput) {
    try {
      return await prisma.user.create({ data });
    } catch (error: any) {
      if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002") {
        const target = error.meta?.target;
        const targetStr = Array.isArray(target) ? target.join(",") : String(target || "");
        if (targetStr.includes("mobileNo") || error.message.includes("users_mobileNo_key")) {
          throw new Error("Mobile number already in use");
        }
        if (targetStr.includes("email") || error.message.includes("users_email_key")) {
          throw new Error("Email already in use");
        }
      }
      throw error;
    }
  }

  async findAll(skip?: number, take?: number, search?: string, roleId?: bigint, status?: boolean) {
    const where: Prisma.UserWhereInput = {};

    if (search) {
      where.OR = [
        { name: { contains: search } },
        { email: { contains: search } },
        { mobileNo: { contains: search } },
      ];
    }

    if (roleId) {
      where.roleId = roleId;
    }

    if (status !== undefined) {
      where.status = status;
    }

    const [items, total] = await Promise.all([
      prisma.user.findMany({
        where,
        ...(skip !== undefined ? { skip } : {}),
        ...(take !== undefined ? { take } : {}),
        include: { role: true },
        orderBy: { id: 'desc' },
      }),
      prisma.user.count({ where }),
    ]);

    return { items, total };
  }

  async findById(id: bigint) {
    return prisma.user.findUnique({
      where: { id },
      include: { role: true },
    });
  }

  async update(id: bigint, data: Prisma.UserUncheckedUpdateInput) {
    try {
      return await prisma.user.update({ where: { id }, data });
    } catch (error: any) {
      if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002") {
        const target = error.meta?.target;
        const targetStr = Array.isArray(target) ? target.join(",") : String(target || "");
        if (targetStr.includes("mobileNo") || error.message.includes("users_mobileNo_key")) {
          throw new Error("Mobile number already in use");
        }
        if (targetStr.includes("email") || error.message.includes("users_email_key")) {
          throw new Error("Email already in use");
        }
      }
      throw error;
    }
  }

  async getRoles() {
    return prisma.role.findMany({
      select: {
        id: true,
        name: true,
      },
      orderBy: { name: 'asc' },
    });
  }

  async countByRoleId(roleId: bigint, excludeUserId?: bigint, onlyActive: boolean = false) {
    const where: Prisma.UserWhereInput = {
      roleId,
      ...(excludeUserId !== undefined ? { id: { not: excludeUserId } } : {}),
      ...(onlyActive ? { status: true } : {}),
    };

    return prisma.user.count({ where });
  }
}
