import { eq } from 'drizzle-orm';

import { db } from '@/lib/db';
import { roles, userPortRoles } from '@/lib/db/schema';
import type { RolePermissions } from '@/lib/db/schema/users';
import { createAuditLog, type AuditMeta } from '@/lib/audit';
import { ConflictError, NotFoundError, ValidationError } from '@/lib/errors';
import { emitToRoom } from '@/lib/socket/server';
import type { CreateRoleInput, UpdateRoleInput } from '@/lib/validators/roles';

export async function listRoles() {
  return db.select().from(roles).orderBy(roles.name);
}

export async function getRole(id: string) {
  const role = await db.query.roles.findFirst({
    where: eq(roles.id, id),
  });
  if (!role) throw new NotFoundError('Role');
  return role;
}

export async function createRole(data: CreateRoleInput, meta: AuditMeta) {
  // Check name uniqueness
  const existing = await db.query.roles.findFirst({
    where: eq(roles.name, data.name),
  });
  if (existing) {
    throw new ConflictError(`A role named "${data.name}" already exists`);
  }

  const [role] = await db
    .insert(roles)
    .values({
      name: data.name,
      description: data.description ?? null,
      permissions: data.permissions as RolePermissions,
    })
    .returning();

  void createAuditLog({
    userId: meta.userId,
    portId: meta.portId,
    action: 'create',
    entityType: 'role',
    entityId: role!.id,
    newValue: { name: role!.name },
    ipAddress: meta.ipAddress,
    userAgent: meta.userAgent,
  });

  emitToRoom(`port:${meta.portId}`, 'system:alert', {
    alertType: 'role:created',
    message: `Role "${role!.name}" created`,
    severity: 'info',
  });

  return role!;
}

export async function updateRole(id: string, data: UpdateRoleInput, meta: AuditMeta) {
  const role = await db.query.roles.findFirst({
    where: eq(roles.id, id),
  });
  if (!role) throw new NotFoundError('Role');

  // Check name uniqueness if changing name
  if (data.name && data.name !== role.name) {
    const conflict = await db.query.roles.findFirst({
      where: eq(roles.name, data.name),
    });
    if (conflict) {
      throw new ConflictError(`A role named "${data.name}" already exists`);
    }
  }

  const updates: Record<string, unknown> = { updatedAt: new Date() };
  if (data.name !== undefined) updates.name = data.name;
  if (data.description !== undefined) updates.description = data.description;
  if (data.permissions !== undefined) updates.permissions = data.permissions as RolePermissions;

  const [updated] = await db.update(roles).set(updates).where(eq(roles.id, id)).returning();

  void createAuditLog({
    userId: meta.userId,
    portId: meta.portId,
    action: 'update',
    entityType: 'role',
    entityId: id,
    oldValue: { name: role.name, permissions: role.permissions },
    newValue: { name: updated!.name, permissions: updated!.permissions },
    ipAddress: meta.ipAddress,
    userAgent: meta.userAgent,
  });

  emitToRoom(`port:${meta.portId}`, 'system:alert', {
    alertType: 'role:updated',
    message: `Role "${updated!.name}" updated`,
    severity: 'info',
  });

  return updated!;
}

export async function deleteRole(id: string, meta: AuditMeta) {
  const role = await db.query.roles.findFirst({
    where: eq(roles.id, id),
  });
  if (!role) throw new NotFoundError('Role');

  if (role.isSystem) {
    throw new ValidationError('System roles cannot be deleted');
  }

  // Check if any users are assigned this role
  const assignments = await db.query.userPortRoles.findFirst({
    where: eq(userPortRoles.roleId, id),
  });
  if (assignments) {
    throw new ConflictError('Cannot delete a role that is assigned to users. Reassign them first.');
  }

  await db.delete(roles).where(eq(roles.id, id));

  void createAuditLog({
    userId: meta.userId,
    portId: meta.portId,
    action: 'delete',
    entityType: 'role',
    entityId: id,
    oldValue: { name: role.name },
    ipAddress: meta.ipAddress,
    userAgent: meta.userAgent,
  });

  emitToRoom(`port:${meta.portId}`, 'system:alert', {
    alertType: 'role:deleted',
    message: `Role "${role.name}" deleted`,
    severity: 'info',
  });
}
