import { db } from "@/lib/db"
import bcrypt from "bcryptjs"
import type { Prisma } from "@prisma/client"

import { UserInput, UserUpdateInput } from "./schema"
import { User, UserWithRoles, UserListItem } from "./types"

type PrismaUserWithRoles = Prisma.UserGetPayload<{
  include: { roles: { include: { role: true } } }
}>

export async function getAllUsers(): Promise<UserListItem[]> {
  const users: PrismaUserWithRoles[] = await db.user.findMany({
    orderBy: { createdAt: "desc" },
    include: {
      roles: {
        include: {
          role: true,
        },
      },
    },
  })

  return users.map((user) => ({
    id: user.id,
    name: user.name,
    email: user.email,
    isActive: user.isActive,
    createdAt: user.createdAt,
    updatedAt: user.updatedAt,
    roles: user.roles.filter((r) => r.role !== null).map((r) => r.role!.name),
  }))
}

export async function getUserById(id: string): Promise<UserWithRoles | null> {
  return db.user.findUnique({
    where: { id },
    include: {
      roles: {
        include: {
          role: true,
        },
      },
    },
  })
}

export async function getUserByEmail(email: string): Promise<UserWithRoles | null> {
  return db.user.findUnique({
    where: { email },
    include: {
      roles: {
        include: {
          role: true,
        },
      },
    },
  })
}

export async function createUser(data: UserInput): Promise<User> {
  const hashedPassword = await bcrypt.hash(data.password, 12)

  const user = await db.user.create({
    data: {
      name: data.name,
      email: data.email,
      password: hashedPassword,
      isActive: data.isActive,
      roles: {
        create: data.roleIds.map((roleId) => ({
          roleId,
        })),
      },
    },
  })

  return user
}

export async function updateUser(id: string, data: UserUpdateInput): Promise<User> {
  const updateData: Partial<Pick<User, "name" | "email" | "isActive" | "password">> = {
    name: data.name,
    email: data.email,
    isActive: data.isActive,
  }

  if (data.password) {
    updateData.password = await bcrypt.hash(data.password, 12)
  }

  const user = await db.user.update({
    where: { id },
    data: updateData,
  })

  if (data.roleIds && data.roleIds.length > 0) {
    await db.userRole.deleteMany({
      where: { userId: id },
    })

    await db.userRole.createMany({
      data: data.roleIds.map((roleId) => ({
        userId: id,
        roleId,
      })),
    })
  }

  return user
}

export async function deleteUser(id: string): Promise<User> {
  return db.user.delete({
    where: { id },
  })
}

export async function toggleUserStatus(id: string, isActive: boolean): Promise<User> {
  return db.user.update({
    where: { id },
    data: { isActive },
  })
}

