import { prisma } from '@/lib/db'
import { AUDIT_ACTIONS, writeAudit } from '@/lib/audit'
import { apiRoute } from '@/lib/api/handler'
import { ApiError, ERROR_CODES } from '@/lib/api/errors'
import { userUpdateSchema, type UserUpdateInput } from '@/lib/api/schemas'

export const dynamic = 'force-dynamic'

/**
 * Update a user's status, role or language.
 *
 * Two guards worth stating: an administrator cannot deactivate their own account (which
 * would be an easy way to lock the last admin out), and changing a role revokes that
 * user's live sessions so the new permissions take effect immediately rather than at
 * their next sign-in.
 */
export const PATCH = apiRoute<{ id: string }, UserUpdateInput>(
  { permission: 'users.manage', bodySchema: userUpdateSchema },
  async ({ params, body, user, request }) => {
    const target = await prisma.user.findUnique({
      where: { id: params.id },
      include: { roles: { include: { role: true } } },
    })
    if (!target) throw new ApiError(ERROR_CODES.notFound)

    if (body.isActive === false && target.id === user.id) {
      throw new ApiError(ERROR_CODES.conflict, {
        message: 'You cannot deactivate your own account.',
      })
    }

    const before = {
      isActive: target.isActive,
      roles: target.roles.map((link) => link.role.key),
      locale: target.locale,
    }

    if (body.roleKey) {
      const role = await prisma.role.findUnique({ where: { key: body.roleKey } })
      if (!role) throw new ApiError(ERROR_CODES.notFound)

      await prisma.$transaction([
        prisma.userRole.deleteMany({ where: { userId: target.id } }),
        prisma.userRole.create({
          data: { userId: target.id, roleId: role.id, assignedBy: user.id },
        }),
        // Permissions are re-read from the database on every request, but revoking the
        // sessions makes the change unambiguous and forces a fresh sign-in.
        prisma.session.updateMany({
          where: { userId: target.id, revokedAt: null },
          data: { revokedAt: new Date() },
        }),
      ])
    }

    const updated = await prisma.user.update({
      where: { id: target.id },
      data: {
        isActive: body.isActive ?? target.isActive,
        locale: body.locale ?? target.locale,
      },
      include: { roles: { include: { role: true } } },
    })

    await writeAudit({
      action: body.roleKey ? AUDIT_ACTIONS.roleChanged : AUDIT_ACTIONS.userChanged,
      entity: 'user',
      entityId: target.id,
      user,
      request,
      before,
      after: {
        isActive: updated.isActive,
        roles: updated.roles.map((link) => link.role.key),
        locale: updated.locale,
      },
      detail: target.email,
    })

    return {
      id: updated.id,
      isActive: updated.isActive,
      locale: updated.locale,
      roles: updated.roles.map((link) => link.role.key),
    }
  },
)
