/**
 * Admin-plane RBAC query helpers — admin-dashboard.
 *
 * CRUD for `admin_roles`. System roles (is_system_role=true) are read-only —
 * delete/update attempts on them are rejected with AdminRoleSystemError.
 *
 * Also exports resolveAdminPermissions: loads the permissions array for a
 * given role id (used by auth middleware to populate AdminSessionPayload).
 */
import { count, eq, isNotNull } from 'drizzle-orm'
import type { Db } from '../client'
import { adminRoles } from '../schema/admin-rbac'
import { adminUsers } from '../schema/admin'

// ── Errors ────────────────────────────────────────────────────────────────────

export class AdminRoleSystemError extends Error {
  constructor() {
    super('System admin roles cannot be modified or deleted')
    this.name = 'AdminRoleSystemError'
  }
}

export class AdminRoleInUseError extends Error {
  constructor() {
    super('This admin role is still assigned to one or more admin users and cannot be deleted')
    this.name = 'AdminRoleInUseError'
  }
}

export class AdminRoleNotFoundError extends Error {
  constructor() {
    super('Admin role not found')
    this.name = 'AdminRoleNotFoundError'
  }
}

// ── Types ─────────────────────────────────────────────────────────────────────

export interface AdminRoleListRow {
  id: string
  name: string
  permissions: string[]
  isSystemRole: boolean
  createdAt: Date
  userCount: number
}

export interface CreateAdminRoleInput {
  name: string
  permissions: string[]
}

export interface UpdateAdminRoleInput {
  permissions: string[]
}

// ── Queries ───────────────────────────────────────────────────────────────────

/**
 * List all admin roles with assigned user counts.
 */
export async function listAdminRoles(db: Db): Promise<AdminRoleListRow[]> {
  const rows = await db
    .select({
      id: adminRoles.id,
      name: adminRoles.name,
      permissions: adminRoles.permissions,
      isSystemRole: adminRoles.isSystemRole,
      createdAt: adminRoles.createdAt,
    })
    .from(adminRoles)
    .orderBy(adminRoles.name)

  // Count admin users per role
  const userCountRows = await db
    .select({
      roleId: adminUsers.roleId,
      cnt: count(adminUsers.id),
    })
    .from(adminUsers)
    .where(isNotNull(adminUsers.roleId))
    .groupBy(adminUsers.roleId)

  const countMap = new Map<string, number>()
  for (const row of userCountRows) {
    if (row.roleId) countMap.set(row.roleId, Number(row.cnt))
  }

  return rows.map((row) => ({
    ...row,
    userCount: countMap.get(row.id) ?? 0,
  }))
}

/**
 * Get a single admin role by id.
 */
export async function getAdminRoleById(db: Db, id: string): Promise<AdminRoleListRow | null> {
  const [row] = await db
    .select({
      id: adminRoles.id,
      name: adminRoles.name,
      permissions: adminRoles.permissions,
      isSystemRole: adminRoles.isSystemRole,
      createdAt: adminRoles.createdAt,
    })
    .from(adminRoles)
    .where(eq(adminRoles.id, id))
    .limit(1)

  if (!row) return null

  const [countRow] = await db
    .select({ cnt: count(adminUsers.id) })
    .from(adminUsers)
    .where(eq(adminUsers.roleId, id))

  return { ...row, userCount: Number(countRow?.cnt ?? 0) }
}

/**
 * Create a custom admin role.
 */
export async function createAdminRole(
  db: Db,
  input: CreateAdminRoleInput,
): Promise<{ id: string }> {
  const [row] = await db
    .insert(adminRoles)
    .values({
      name: input.name,
      permissions: input.permissions,
      isSystemRole: false,
    })
    .returning({ id: adminRoles.id })

  return { id: row!.id }
}

/**
 * Update the permissions of an admin role.
 * Throws AdminRoleSystemError if the role is a system role.
 */
export async function updateAdminRole(
  db: Db,
  id: string,
  input: UpdateAdminRoleInput,
): Promise<void> {
  const existing = await getAdminRoleById(db, id)
  if (!existing) throw new AdminRoleNotFoundError()
  if (existing.isSystemRole) throw new AdminRoleSystemError()

  await db
    .update(adminRoles)
    .set({ permissions: input.permissions })
    .where(eq(adminRoles.id, id))
}

/**
 * Delete a custom admin role.
 * Throws AdminRoleSystemError for system roles.
 * Throws AdminRoleInUseError if admin users still reference this role.
 */
export async function deleteAdminRole(db: Db, id: string): Promise<void> {
  const existing = await getAdminRoleById(db, id)
  if (!existing) throw new AdminRoleNotFoundError()
  if (existing.isSystemRole) throw new AdminRoleSystemError()
  if (existing.userCount > 0) throw new AdminRoleInUseError()

  await db.delete(adminRoles).where(eq(adminRoles.id, id))
}

// ── Permission resolution ─────────────────────────────────────────────────────

/**
 * Resolve the permissions array for an admin role id.
 * Returns empty array if role not found.
 * Used by auth layer to populate AdminSessionPayload.permissions.
 */
export async function resolveAdminPermissions(
  db: Db,
  roleId: string,
): Promise<string[]> {
  const [row] = await db
    .select({ permissions: adminRoles.permissions })
    .from(adminRoles)
    .where(eq(adminRoles.id, roleId))
    .limit(1)

  return row?.permissions ?? []
}
