/**
 * Serviço de Notificações
 * Orquestrador de notificações por e-mail
 * Sistema de Chamados Raven
 */

import { prisma } from "@/lib/prisma"
import { sendEmail, type NotificationType } from "../email"
import { generateAccessToken } from "./token-service"

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

interface TicketData {
  id: string
  tenantId: string
  reference: number
  subject: string
  status: string
  priority: string
  createdAt: Date
  resolvedAt?: Date | null
  requester: {
    id: string
    name: string
    email: string
    role: string
  }
  assignee?: {
    id: string
    name: string
    email: string
    role: string
  } | null
  company?: {
    id: string
    name: string
  } | null
}

interface CommentData {
  id: string
  body: string
  visibility: string
  createdAt: Date
  author: {
    id: string
    name: string
    email: string
    role: string
  }
}

// ============================================
// Helpers
// ============================================

const APP_URL = process.env.NEXT_PUBLIC_APP_URL ?? "https://tickets.esdrasrenan.com.br"

function getTicketViewUrl(ticketId: string, token?: string): string {
  if (token) {
    return `${APP_URL}/ticket-view/${token}`
  }
  return `${APP_URL}/portal/tickets/${ticketId}`
}

function getRateUrl(token: string): string {
  return `${APP_URL}/rate/${token}`
}

async function shouldSendNotification(
  userId: string,
  notificationType: NotificationType,
  tenantId: string
): Promise<boolean> {
  try {
    const prefs = await prisma.notificationPreferences.findUnique({
      where: { userId },
    })

    // Se não tem preferências, usa os defaults
    if (!prefs) return true

    // Se e-mail está desabilitado globalmente
    if (!prefs.emailEnabled) return false

    // Verifica preferências por tipo
    const typePrefs = prefs.typePreferences as Record<string, boolean>
    if (typePrefs && notificationType in typePrefs) {
      return typePrefs[notificationType] !== false
    }

    return true
  } catch {
    // Em caso de erro, envia a notificação
    return true
  }
}

// ============================================
// Notificações de Ciclo de Vida
// ============================================

/**
 * Notificação de abertura de chamado
 * Enviada para: Solicitante
 */
export async function notifyTicketCreated(ticket: TicketData): Promise<void> {
  const { requester } = ticket

  // Gera token de acesso para visualização
  const accessToken = await generateAccessToken({
    tenantId: ticket.tenantId,
    ticketId: ticket.id,
    userId: requester.id,
    scope: "view",
    expiresInDays: 7,
  })

  const viewUrl = getTicketViewUrl(ticket.id, accessToken)

  await sendEmail({
    to: {
      email: requester.email,
      name: requester.name,
      userId: requester.id,
      role: requester.role as "ADMIN" | "MANAGER" | "AGENT" | "COLLABORATOR",
    },
    subject: `[#${ticket.reference}] Chamado Aberto - ${ticket.subject}`,
    template: "ticket_created",
    data: {
      reference: ticket.reference,
      subject: ticket.subject,
      status: ticket.status,
      priority: ticket.priority,
      createdAt: ticket.createdAt.toISOString(),
      viewUrl,
    },
    notificationType: "ticket_created",
    tenantId: ticket.tenantId,
  })
}

/**
 * Notificação de atribuição de chamado
 * Enviada para: Solicitante e Agente atribuído
 */
export async function notifyTicketAssigned(ticket: TicketData): Promise<void> {
  if (!ticket.assignee) return

  const { requester, assignee } = ticket

  // Gera tokens de acesso
  const requesterToken = await generateAccessToken({
    tenantId: ticket.tenantId,
    ticketId: ticket.id,
    userId: requester.id,
    scope: "view",
    expiresInDays: 7,
  })

  const assigneeToken = await generateAccessToken({
    tenantId: ticket.tenantId,
    ticketId: ticket.id,
    userId: assignee.id,
    scope: "interact",
    expiresInDays: 7,
  })

  // Notifica o solicitante
  if (await shouldSendNotification(requester.id, "ticket_assigned", ticket.tenantId)) {
    await sendEmail({
      to: {
        email: requester.email,
        name: requester.name,
        userId: requester.id,
        role: requester.role as "ADMIN" | "MANAGER" | "AGENT" | "COLLABORATOR",
      },
      subject: `[#${ticket.reference}] Agente Atribuído - ${ticket.subject}`,
      template: "ticket_assigned",
      data: {
        reference: ticket.reference,
        subject: ticket.subject,
        status: ticket.status,
        priority: ticket.priority,
        requesterName: requester.name,
        assigneeName: assignee.name,
        isForRequester: true,
        viewUrl: getTicketViewUrl(ticket.id, requesterToken),
      },
      notificationType: "ticket_assigned",
      tenantId: ticket.tenantId,
    })
  }

  // Notifica o agente atribuído
  if (await shouldSendNotification(assignee.id, "ticket_assigned", ticket.tenantId)) {
    await sendEmail({
      to: {
        email: assignee.email,
        name: assignee.name,
        userId: assignee.id,
        role: assignee.role as "ADMIN" | "MANAGER" | "AGENT" | "COLLABORATOR",
      },
      subject: `[#${ticket.reference}] Novo Chamado Atribuído - ${ticket.subject}`,
      template: "ticket_assigned",
      data: {
        reference: ticket.reference,
        subject: ticket.subject,
        status: ticket.status,
        priority: ticket.priority,
        requesterName: requester.name,
        assigneeName: assignee.name,
        isForRequester: false,
        viewUrl: getTicketViewUrl(ticket.id, assigneeToken),
      },
      notificationType: "ticket_assigned",
      tenantId: ticket.tenantId,
    })
  }
}

/**
 * Notificação de resolução de chamado
 * Enviada para: Solicitante (com link de avaliação)
 */
export async function notifyTicketResolved(
  ticket: TicketData,
  resolutionSummary?: string
): Promise<void> {
  const { requester, assignee } = ticket

  // Gera token de acesso para avaliação
  const rateToken = await generateAccessToken({
    tenantId: ticket.tenantId,
    ticketId: ticket.id,
    userId: requester.id,
    scope: "rate",
    expiresInDays: 30, // 30 dias para avaliar
  })

  const viewToken = await generateAccessToken({
    tenantId: ticket.tenantId,
    ticketId: ticket.id,
    userId: requester.id,
    scope: "view",
    expiresInDays: 7,
  })

  await sendEmail({
    to: {
      email: requester.email,
      name: requester.name,
      userId: requester.id,
      role: requester.role as "ADMIN" | "MANAGER" | "AGENT" | "COLLABORATOR",
    },
    subject: `[#${ticket.reference}] Chamado Resolvido - ${ticket.subject}`,
    template: "ticket_resolved",
    data: {
      reference: ticket.reference,
      subject: ticket.subject,
      assigneeName: assignee?.name ?? "Equipe de Suporte",
      resolutionSummary,
      viewUrl: getTicketViewUrl(ticket.id, viewToken),
      rateUrl: getRateUrl(rateToken),
    },
    notificationType: "ticket_resolved",
    tenantId: ticket.tenantId,
  })
}

/**
 * Notificação de mudança de status
 * Enviada para: Solicitante
 */
export async function notifyTicketStatusChanged(
  ticket: TicketData,
  oldStatus: string,
  newStatus: string
): Promise<void> {
  const { requester } = ticket

  if (!(await shouldSendNotification(requester.id, "ticket_status_changed", ticket.tenantId))) {
    return
  }

  const accessToken = await generateAccessToken({
    tenantId: ticket.tenantId,
    ticketId: ticket.id,
    userId: requester.id,
    scope: "view",
    expiresInDays: 7,
  })

  await sendEmail({
    to: {
      email: requester.email,
      name: requester.name,
      userId: requester.id,
      role: requester.role as "ADMIN" | "MANAGER" | "AGENT" | "COLLABORATOR",
    },
    subject: `[#${ticket.reference}] Status Atualizado - ${ticket.subject}`,
    template: "ticket_status",
    data: {
      reference: ticket.reference,
      subject: ticket.subject,
      oldStatus,
      newStatus,
      viewUrl: getTicketViewUrl(ticket.id, accessToken),
    },
    notificationType: "ticket_status_changed",
    tenantId: ticket.tenantId,
  })
}

/**
 * Notificação de novo comentário público
 * Enviada para: Solicitante (quando agente comenta)
 */
export async function notifyPublicComment(
  ticket: TicketData,
  comment: CommentData
): Promise<void> {
  // Só notifica comentários públicos
  if (comment.visibility !== "PUBLIC") return

  // Não notifica se o autor é o próprio solicitante
  if (comment.author.id === ticket.requester.id) return

  const { requester } = ticket

  if (!(await shouldSendNotification(requester.id, "comment_public", ticket.tenantId))) {
    return
  }

  const accessToken = await generateAccessToken({
    tenantId: ticket.tenantId,
    ticketId: ticket.id,
    userId: requester.id,
    scope: "view",
    expiresInDays: 7,
  })

  await sendEmail({
    to: {
      email: requester.email,
      name: requester.name,
      userId: requester.id,
      role: requester.role as "ADMIN" | "MANAGER" | "AGENT" | "COLLABORATOR",
    },
    subject: `[#${ticket.reference}] Nova Atualização - ${ticket.subject}`,
    template: "ticket_comment",
    data: {
      reference: ticket.reference,
      subject: ticket.subject,
      authorName: comment.author.name,
      commentBody: comment.body,
      commentedAt: comment.createdAt.toISOString(),
      viewUrl: getTicketViewUrl(ticket.id, accessToken),
    },
    notificationType: "comment_public",
    tenantId: ticket.tenantId,
  })
}

/**
 * Notificação de resposta do solicitante
 * Enviada para: Agente atribuído
 */
export async function notifyRequesterResponse(
  ticket: TicketData,
  comment: CommentData
): Promise<void> {
  // Só notifica se tem agente atribuído
  if (!ticket.assignee) return

  // Só notifica se o autor é o solicitante
  if (comment.author.id !== ticket.requester.id) return

  const { assignee } = ticket

  if (!(await shouldSendNotification(assignee.id, "comment_response", ticket.tenantId))) {
    return
  }

  const accessToken = await generateAccessToken({
    tenantId: ticket.tenantId,
    ticketId: ticket.id,
    userId: assignee.id,
    scope: "interact",
    expiresInDays: 7,
  })

  await sendEmail({
    to: {
      email: assignee.email,
      name: assignee.name,
      userId: assignee.id,
      role: assignee.role as "ADMIN" | "MANAGER" | "AGENT" | "COLLABORATOR",
    },
    subject: `[#${ticket.reference}] Resposta do Solicitante - ${ticket.subject}`,
    template: "ticket_comment",
    data: {
      reference: ticket.reference,
      subject: ticket.subject,
      authorName: comment.author.name,
      commentBody: comment.body,
      commentedAt: comment.createdAt.toISOString(),
      viewUrl: getTicketViewUrl(ticket.id, accessToken),
    },
    notificationType: "comment_response",
    tenantId: ticket.tenantId,
  })
}

// ============================================
// Notificações de Autenticação
// ============================================

/**
 * Notificação de reset de senha
 */
export async function notifyPasswordReset(
  email: string,
  name: string,
  resetUrl: string
): Promise<void> {
  await sendEmail({
    to: { email, name },
    subject: "Redefinição de Senha - Sistema de Chamados Raven",
    template: "password_reset",
    data: { resetUrl },
    notificationType: "security_password_reset",
    skipPreferenceCheck: true,
  })
}

/**
 * Notificação de verificação de e-mail
 */
export async function notifyEmailVerification(
  email: string,
  name: string,
  verifyUrl: string
): Promise<void> {
  await sendEmail({
    to: { email, name },
    subject: "Confirme seu E-mail - Sistema de Chamados Raven",
    template: "email_verify",
    data: { verifyUrl },
    notificationType: "security_email_verify",
    skipPreferenceCheck: true,
  })
}

/**
 * Notificação de convite de usuário
 */
export async function notifyUserInvite(
  email: string,
  name: string | null,
  inviterName: string,
  roleName: string,
  companyName: string | null,
  inviteUrl: string
): Promise<void> {
  await sendEmail({
    to: { email, name: name ?? undefined },
    subject: "Você foi convidado! - Sistema de Chamados Raven",
    template: "invite",
    data: {
      inviterName,
      roleName,
      companyName,
      inviteUrl,
    },
    notificationType: "security_invite",
    skipPreferenceCheck: true,
  })
}

/**
 * Notificação de novo login
 */
export async function notifyNewLogin(
  userId: string,
  email: string,
  name: string,
  loginAt: Date,
  userAgent: string,
  ipAddress: string,
  tenantId: string
): Promise<void> {
  if (!(await shouldSendNotification(userId, "security_new_login", tenantId))) {
    return
  }

  await sendEmail({
    to: { email, name, userId },
    subject: "Novo Acesso Detectado - Sistema de Chamados Raven",
    template: "new_login",
    data: {
      loginAt: loginAt.toISOString(),
      userAgent,
      ipAddress,
    },
    notificationType: "security_new_login",
    tenantId,
  })
}

// ============================================
// Notificações de SLA
// ============================================

/**
 * Notificação de SLA em risco
 * Enviada para: Agente atribuído e supervisor
 */
export async function notifySlaAtRisk(
  ticket: TicketData,
  dueAt: Date,
  timeRemaining: string
): Promise<void> {
  const recipients: Array<{ email: string; name: string; userId: string; role: string }> = []

  // Adiciona o agente atribuído
  if (ticket.assignee) {
    if (await shouldSendNotification(ticket.assignee.id, "sla_at_risk", ticket.tenantId)) {
      recipients.push({
        email: ticket.assignee.email,
        name: ticket.assignee.name,
        userId: ticket.assignee.id,
        role: ticket.assignee.role,
      })
    }
  }

  if (recipients.length === 0) return

  for (const recipient of recipients) {
    const accessToken = await generateAccessToken({
      tenantId: ticket.tenantId,
      ticketId: ticket.id,
      userId: recipient.userId,
      scope: "interact",
      expiresInDays: 7,
    })

    await sendEmail({
      to: {
        email: recipient.email,
        name: recipient.name,
        userId: recipient.userId,
        role: recipient.role as "ADMIN" | "MANAGER" | "AGENT" | "COLLABORATOR",
      },
      subject: `[ATENÇÃO] SLA em Risco - #${ticket.reference} ${ticket.subject}`,
      template: "sla_warning",
      data: {
        reference: ticket.reference,
        subject: ticket.subject,
        status: ticket.status,
        priority: ticket.priority,
        requesterName: ticket.requester.name,
        assigneeName: ticket.assignee?.name,
        dueAt: dueAt.toISOString(),
        timeRemaining,
        viewUrl: getTicketViewUrl(ticket.id, accessToken),
      },
      notificationType: "sla_at_risk",
      tenantId: ticket.tenantId,
    })
  }
}

/**
 * Notificação de SLA violado
 * Enviada para: Agente atribuído e administradores
 */
export async function notifySlaBreached(
  ticket: TicketData,
  dueAt: Date,
  timeExceeded: string
): Promise<void> {
  const recipients: Array<{ email: string; name: string; userId: string; role: string }> = []

  // Adiciona o agente atribuído
  if (ticket.assignee) {
    if (await shouldSendNotification(ticket.assignee.id, "sla_breached", ticket.tenantId)) {
      recipients.push({
        email: ticket.assignee.email,
        name: ticket.assignee.name,
        userId: ticket.assignee.id,
        role: ticket.assignee.role,
      })
    }
  }

  if (recipients.length === 0) return

  for (const recipient of recipients) {
    const accessToken = await generateAccessToken({
      tenantId: ticket.tenantId,
      ticketId: ticket.id,
      userId: recipient.userId,
      scope: "interact",
      expiresInDays: 7,
    })

    await sendEmail({
      to: {
        email: recipient.email,
        name: recipient.name,
        userId: recipient.userId,
        role: recipient.role as "ADMIN" | "MANAGER" | "AGENT" | "COLLABORATOR",
      },
      subject: `[URGENTE] SLA Violado - #${ticket.reference} ${ticket.subject}`,
      template: "sla_breached",
      data: {
        reference: ticket.reference,
        subject: ticket.subject,
        status: ticket.status,
        priority: ticket.priority,
        requesterName: ticket.requester.name,
        assigneeName: ticket.assignee?.name,
        dueAt: dueAt.toISOString(),
        timeExceeded,
        viewUrl: getTicketViewUrl(ticket.id, accessToken),
      },
      notificationType: "sla_breached",
      tenantId: ticket.tenantId,
    })
  }
}
