/**
 * Serviço de Tokens de Acesso
 * Gera e valida tokens para acesso direto aos chamados
 * Sistema de Chamados Raven
 */

import { prisma } from "@/lib/prisma"
import { randomBytes } from "crypto"

// ============================================
// Tipos
// ============================================

export interface GenerateTokenOptions {
  tenantId: string
  ticketId: string
  userId: string
  machineId?: string
  scope: "view" | "interact" | "rate"
  expiresInDays?: number
}

export interface ValidatedToken {
  id: string
  tenantId: string
  ticketId: string
  userId: string
  machineId: string | null
  scope: string
  expiresAt: Date
  usedAt: Date | null
  createdAt: Date
}

// ============================================
// Geração de Token
// ============================================

/**
 * Gera um token seguro de acesso ao chamado
 */
export async function generateAccessToken(options: GenerateTokenOptions): Promise<string> {
  const {
    tenantId,
    ticketId,
    userId,
    machineId,
    scope,
    expiresInDays = 7,
  } = options

  // Gera um token aleatório de 32 bytes (64 caracteres hex)
  const token = randomBytes(32).toString("hex")

  // Calcula a data de expiração
  const expiresAt = new Date()
  expiresAt.setDate(expiresAt.getDate() + expiresInDays)

  // Salva o token no banco
  await prisma.ticketAccessToken.create({
    data: {
      tenantId,
      token,
      ticketId,
      userId,
      machineId,
      scope,
      expiresAt,
    },
  })

  return token
}

/**
 * Valida um token de acesso
 */
export async function validateAccessToken(token: string): Promise<ValidatedToken | null> {
  const tokenRecord = await prisma.ticketAccessToken.findUnique({
    where: { token },
  })

  if (!tokenRecord) {
    return null
  }

  // Verifica se o token expirou
  if (tokenRecord.expiresAt < new Date()) {
    return null
  }

  return {
    id: tokenRecord.id,
    tenantId: tokenRecord.tenantId,
    ticketId: tokenRecord.ticketId,
    userId: tokenRecord.userId,
    machineId: tokenRecord.machineId,
    scope: tokenRecord.scope,
    expiresAt: tokenRecord.expiresAt,
    usedAt: tokenRecord.usedAt,
    createdAt: tokenRecord.createdAt,
  }
}

/**
 * Marca um token como usado
 */
export async function markTokenAsUsed(token: string): Promise<void> {
  await prisma.ticketAccessToken.update({
    where: { token },
    data: { usedAt: new Date() },
  })
}

/**
 * Invalida todos os tokens de um chamado
 */
export async function invalidateTicketTokens(ticketId: string): Promise<void> {
  await prisma.ticketAccessToken.updateMany({
    where: { ticketId },
    data: { expiresAt: new Date() },
  })
}

/**
 * Invalida todos os tokens de um usuário
 */
export async function invalidateUserTokens(userId: string): Promise<void> {
  await prisma.ticketAccessToken.updateMany({
    where: { userId },
    data: { expiresAt: new Date() },
  })
}

/**
 * Limpa tokens expirados (pode ser chamado periodicamente)
 */
export async function cleanupExpiredTokens(): Promise<number> {
  const result = await prisma.ticketAccessToken.deleteMany({
    where: {
      expiresAt: {
        lt: new Date(),
      },
    },
  })

  return result.count
}

/**
 * Verifica se um token tem o escopo necessário
 */
export function hasScope(tokenScope: string, requiredScope: "view" | "interact" | "rate"): boolean {
  const scopeHierarchy: Record<string, number> = {
    view: 1,
    interact: 2,
    rate: 3,
  }

  const tokenLevel = scopeHierarchy[tokenScope] ?? 0
  const requiredLevel = scopeHierarchy[requiredScope] ?? 0

  // rate é especial, só pode avaliar
  if (requiredScope === "rate") {
    return tokenScope === "rate"
  }

  // interact permite view
  // view só permite view
  return tokenLevel >= requiredLevel
}
