import { NextResponse } from "next/server"
// Use the standalone build to avoid AFM filesystem lookups
// and ensure compatibility in serverless/traced environments.
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore – no ambient types for this path; declared in types/
import PDFDocument from "pdfkit/js/pdfkit.standalone.js"
import { format } from "date-fns"
import fs from "fs"
import path from "path"
import { ptBR } from "date-fns/locale"
import { ConvexHttpClient } from "convex/browser"

import { api } from "@/convex/_generated/api"
import type { Id } from "@/convex/_generated/dataModel"
import { env } from "@/lib/env"
import { assertAuthenticatedSession } from "@/lib/auth-server"
import { mapTicketWithDetailsFromServer } from "@/lib/mappers/ticket"
import { DEFAULT_TENANT_ID } from "@/lib/constants"
import { TICKET_TIMELINE_LABELS } from "@/lib/ticket-timeline-labels"

// Force Node.js runtime for pdfkit compatibility
export const runtime = "nodejs"

const statusLabel: Record<string, string> = {
  PENDING: "Pendente",
  AWAITING_ATTENDANCE: "Aguardando atendimento",
  PAUSED: "Pausado",
  RESOLVED: "Resolvido",
}

const statusColors: Record<string, string> = {
  PENDING: "#64748B", // slate-500
  AWAITING_ATTENDANCE: "#0EA5E9", // sky-500
  PAUSED: "#F59E0B", // amber-500
  RESOLVED: "#10B981", // emerald-500
}

const priorityLabel: Record<string, string> = {
  LOW: "Baixa",
  MEDIUM: "Média",
  HIGH: "Alta",
  URGENT: "Urgente",
  CRITICAL: "Crítica",
}

const channelLabel: Record<string, string> = {
  EMAIL: "E-mail",
  PHONE: "Telefone",
  CHAT: "Chat",
  PORTAL: "Portal",
  WEB: "Portal",
  API: "API",
  SOCIAL: "Redes sociais",
  OTHER: "Outro",
}


function formatDateTime(date: Date | null | undefined) {
  if (!date) return "—"
  return format(date, "dd/MM/yyyy HH:mm", { locale: ptBR })
}

function htmlToPlainText(html?: string | null) {
  if (!html) return ""
  const withBreaks = html
    .replace(/<\s*br\s*\/?>/gi, "\n")
    .replace(/<\/p>/gi, "\n\n")
  const stripped = withBreaks.replace(/<[^>]+>/g, "")
  return decodeHtmlEntities(stripped).replace(/\u00A0/g, " ").trim()
}

function decodeHtmlEntities(input: string) {
  return input
    .replace(/&amp;/g, "&")
    .replace(/&lt;/g, "<")
    .replace(/&gt;/g, ">")
    .replace(/&quot;/g, '"')
    .replace(/&#39;/g, "'")
    .replace(/&nbsp;/g, " ")
}

function stringifyPayload(payload: unknown): string | null {
  if (!payload) return null
  if (typeof payload === "object") {
    if (Array.isArray(payload)) {
      if (payload.length === 0) return null
    } else if (payload) {
      if (Object.keys(payload as Record<string, unknown>).length === 0) return null
    }
  }
  if (typeof payload === "string" && payload.trim() === "") return null
  try {
    return JSON.stringify(payload, null, 2)
  } catch {
    return String(payload)
  }
}

function formatDurationMs(ms: number | null | undefined) {
  if (!ms || ms <= 0) return null
  const totalSeconds = Math.floor(ms / 1000)
  const hours = Math.floor(totalSeconds / 3600)
  const minutes = Math.floor((totalSeconds % 3600) / 60)
  const seconds = totalSeconds % 60
  if (hours > 0) return `${hours}h ${String(minutes).padStart(2, "0")}m`
  if (minutes > 0) return `${minutes}m ${String(seconds).padStart(2, "0")}s`
  return `${seconds}s`
}

function buildTimelineMessage(type: string, payload: Record<string, unknown> | null | undefined): string | null {
  const p = payload ?? {}
  const to = (p.toLabel as string | undefined) ?? (p.to as string | undefined)
  const assignee = (p.assigneeName as string | undefined) ?? (p.assigneeId as string | undefined)
  const queue = (p.queueName as string | undefined) ?? (p.queueId as string | undefined)
  const requester = p.requesterName as string | undefined
  const author = (p.authorName as string | undefined) ?? (p.authorId as string | undefined)
  const actor = (p.actorName as string | undefined) ?? (p.actorId as string | undefined)
  const attachmentName = p.attachmentName as string | undefined
  const subjectTo = p.to as string | undefined
  const pauseReason = (p.pauseReasonLabel as string | undefined) ?? (p.pauseReason as string | undefined)
  const pauseNote = p.pauseNote as string | undefined
  const sessionDuration = formatDurationMs((p.sessionDurationMs as number | undefined) ?? null)
  const categoryName = p.categoryName as string | undefined
  const subcategoryName = p.subcategoryName as string | undefined

  switch (type) {
    case "STATUS_CHANGED":
      return to ? `Status alterado para ${to}` : "Status alterado"
    case "ASSIGNEE_CHANGED":
      return assignee ? `Responsável alterado para ${assignee}` : "Responsável alterado"
    case "QUEUE_CHANGED":
      return queue ? `Fila alterada para ${queue}` : "Fila alterada"
    case "PRIORITY_CHANGED":
      return to ? `Prioridade alterada para ${to}` : "Prioridade alterada"
    case "CREATED":
      return requester ? `Criado por ${requester}` : "Criado"
    case "COMMENT_ADDED":
      return author ? `Comentário adicionado por ${author}` : "Comentário adicionado"
    case "COMMENT_EDITED": {
      const who = actor ?? author
      return who ? `Comentário editado por ${who}` : "Comentário editado"
    }
    case "SUBJECT_CHANGED":
      return subjectTo ? `Assunto alterado para "${subjectTo}"` : "Assunto alterado"
    case "SUMMARY_CHANGED":
      return "Resumo atualizado"
    case "ATTACHMENT_REMOVED":
      return attachmentName ? `Anexo removido: ${attachmentName}` : "Anexo removido"
    case "WORK_PAUSED": {
      const parts: string[] = []
      if (pauseReason) parts.push(`Motivo: ${pauseReason}`)
      if (sessionDuration) parts.push(`Tempo registrado: ${sessionDuration}`)
      if (pauseNote) parts.push(`Observação: ${pauseNote}`)
      return parts.length > 0 ? parts.join(" • ") : "Atendimento pausado"
    }
    case "WORK_STARTED":
      return "Atendimento iniciado"
    case "CATEGORY_CHANGED": {
      if (categoryName || subcategoryName) {
        return `Categoria alterada para ${categoryName ?? ""}${subcategoryName ? ` • ${subcategoryName}` : ""}`.trim()
      }
      return "Categoria removida"
    }
    case "MANAGER_NOTIFIED":
      return "Gestor notificado"
    case "VISIT_SCHEDULED":
      return "Visita agendada"
    case "CSAT_RECEIVED":
      return "CSAT recebido"
    case "CSAT_RATED":
      return "CSAT avaliado"
    default:
      return null
  }
}

export async function GET(_request: Request, context: { params: Promise<{ id: string }> }) {
  const { id: ticketId } = await context.params
  const session = await assertAuthenticatedSession()
  if (!session) {
    return NextResponse.json({ error: "Não autorizado" }, { status: 401 })
  }

  const convexUrl = env.NEXT_PUBLIC_CONVEX_URL
  if (!convexUrl) {
    return NextResponse.json({ error: "Convex não configurado" }, { status: 500 })
  }

  const client = new ConvexHttpClient(convexUrl)
  const tenantId = session.user.tenantId ?? DEFAULT_TENANT_ID
  let viewerId: string | null = null
  try {
    const ensuredUser = await client.mutation(api.users.ensureUser, {
      tenantId,
      name: session.user.name ?? session.user.email,
      email: session.user.email,
      avatarUrl: session.user.avatarUrl ?? undefined,
      role: session.user.role.toUpperCase(),
    })
    viewerId = ensuredUser?._id ?? null
  } catch (error) {
    console.error("Failed to synchronize user with Convex for PDF export", error)
    return NextResponse.json({ error: "Falha ao sincronizar usuário com Convex" }, { status: 500 })
  }

  if (!viewerId) {
    return NextResponse.json({ error: "Usuário não encontrado no Convex" }, { status: 403 })
  }

  let ticketRaw: unknown
  try {
    ticketRaw = await client.query(api.tickets.getById, {
      tenantId,
      id: ticketId as unknown as Id<"tickets">,
      viewerId: viewerId as unknown as Id<"users">,
    })
  } catch (error) {
    console.error("Failed to load ticket from Convex for PDF export", error, {
      tenantId,
      ticketId,
      viewerId,
    })
    return NextResponse.json({ error: "Falha ao carregar ticket no Convex" }, { status: 500 })
  }

  if (!ticketRaw) {
    return NextResponse.json({ error: "Ticket não encontrado" }, { status: 404 })
  }

  const ticket = mapTicketWithDetailsFromServer(ticketRaw)
  const doc = new PDFDocument({ size: "A4", margin: 56 })
  const chunks: Buffer[] = []

  doc.on("data", (chunk: unknown) => {
    const buf = typeof chunk === "string" ? Buffer.from(chunk) : (chunk as Buffer)
    chunks.push(buf)
  })

  const pdfBufferPromise = new Promise<Buffer>((resolve, reject) => {
    doc.on("end", () => resolve(Buffer.concat(chunks)))
    doc.on("error", reject)
  })

  // Register custom fonts (Inter) if available
  try {
    const pubRegular = path.join(process.cwd(), "public", "fonts", "Inter-Regular.ttf")
    const pubBold = path.join(process.cwd(), "public", "fonts", "Inter-Bold.ttf")
    const fontRegular = fs.existsSync(pubRegular)
      ? pubRegular
      : path.join(process.cwd(), "Inter,Manrope", "Inter", "static", "Inter_24pt-Regular.ttf")
    const fontBold = fs.existsSync(pubBold)
      ? pubBold
      : path.join(process.cwd(), "Inter,Manrope", "Inter", "static", "Inter_24pt-Bold.ttf")
    const D = doc as unknown as {
      registerFont?: (name: string, src: string) => void
      _fontFamilies?: Record<string, unknown>
      roundedRect?: (x: number, y: number, w: number, h: number, r: number) => void
    }
    if (fs.existsSync(fontRegular)) {
      D.registerFont?.("Inter", fontRegular)
    }
    if (fs.existsSync(fontBold)) {
      D.registerFont?.("Inter-Bold", fontBold)
    }
  } catch {}

  const D = doc as unknown as { _fontFamilies?: Record<string, unknown>; roundedRect?: (x:number,y:number,w:number,h:number,r:number)=>void }
  const hasInter = Boolean(D._fontFamilies && (D._fontFamilies as Record<string, unknown>)["Inter-Bold"]) 

  // Header with logo and brand bar
  try {
    const logoPath = path.join(process.cwd(), "public", "rever-8.png")
    if (fs.existsSync(logoPath)) {
      doc.image(logoPath, doc.page.margins.left, doc.y, { width: 120 })
    }
  } catch {}
  doc.moveDown(0.5)
  doc
    .fillColor("#00e8ff")
    .rect(doc.page.margins.left, doc.y, doc.page.width - doc.page.margins.left - doc.page.margins.right, 3)
    .fill()
  doc.moveDown(0.5)

  // Título
  doc.fillColor("#0F172A").font(hasInter ? "Inter-Bold" : "Helvetica-Bold").fontSize(18).text(`Ticket #${ticket.reference} — ${ticket.subject}`)
  doc.moveDown(0.25)
  // Linha abaixo do título
  doc
    .strokeColor("#E2E8F0")
    .moveTo(doc.page.margins.left, doc.y)
    .lineTo(doc.page.width - doc.page.margins.right, doc.y)
    .stroke()

  // Badge de status
  doc.moveDown(0.5)
  const statusText = statusLabel[ticket.status] ?? ticket.status
  const badgeColor = statusColors[ticket.status] ?? "#475569"
  const badgeFontSize = 10
  const badgePaddingX = 6
  const badgePaddingY = 3
  const badgeX = doc.page.margins.left
  const badgeY = doc.y
  doc.save()
  doc.font(hasInter ? "Inter-Bold" : "Helvetica-Bold").fontSize(badgeFontSize)
  const badgeTextWidth = doc.widthOfString(statusText)
  const badgeHeight = badgeFontSize + badgePaddingY * 2
  const badgeWidth = badgeTextWidth + badgePaddingX * 2
  D.roundedRect?.(badgeX, badgeY, badgeWidth, badgeHeight, 4) ?? doc.rect(badgeX, badgeY, badgeWidth, badgeHeight)
  doc.fill(badgeColor)
  doc.fillColor("#FFFFFF").text(statusText, badgeX + badgePaddingX, badgeY + badgePaddingY)
  doc.restore()
  doc.y = badgeY + badgeHeight + 8

  // Metadados em duas colunas
  const leftX = doc.page.margins.left
  const colGap = 24
  const colWidth = (doc.page.width - doc.page.margins.left - doc.page.margins.right - colGap) / 2
  const rightX = leftX + colWidth + colGap
  const startY = doc.y
  const drawMeta = (x: number, lines: string[]) => {
    doc.save()
    doc.x = x
    doc.fillColor("#0F172A").font(hasInter ? "Inter" : "Helvetica").fontSize(11)
    for (const line of lines) {
      doc.text(line, { width: colWidth, lineGap: 2 })
    }
    const currY = doc.y
    doc.restore()
    return currY
  }
  const leftLines = [
    `Status: ${statusText}`,
    `Prioridade: ${priorityLabel[ticket.priority] ?? ticket.priority}`,
    `Canal: ${channelLabel[ticket.channel] ?? ticket.channel ?? "—"}`,
    `Fila: ${ticket.queue ?? "—"}`,
  ]
  const rightLines = [
    `Solicitante: ${ticket.requester.name} (${ticket.requester.email})`,
    `Responsável: ${ticket.assignee ? `${ticket.assignee.name} (${ticket.assignee.email})` : "Não atribuído"}`,
    `Criado em: ${formatDateTime(ticket.createdAt)}`,
    `Atualizado em: ${formatDateTime(ticket.updatedAt)}`,
  ]
  const leftY = drawMeta(leftX, leftLines)
  const rightY = drawMeta(rightX, rightLines)
  doc.y = Math.max(leftY, rightY)
  doc.moveDown(0.5)

  doc.moveDown(0.75)
  doc
    .font(hasInter ? "Inter-Bold" : "Helvetica-Bold")
    .fontSize(12)
    .text("Solicitante")
  doc
    .strokeColor("#E2E8F0")
    .moveTo(doc.page.margins.left, doc.y)
    .lineTo(doc.page.width - doc.page.margins.right, doc.y)
    .stroke()
  doc.moveDown(0.3)
  doc
    .font(hasInter ? "Inter" : "Helvetica")
    .fontSize(11)
    .text(`${ticket.requester.name} (${ticket.requester.email})`)

  doc.moveDown(0.5)
  doc.font(hasInter ? "Inter-Bold" : "Helvetica-Bold").fontSize(12).text("Responsável")
  doc
    .strokeColor("#E2E8F0")
    .moveTo(doc.page.margins.left, doc.y)
    .lineTo(doc.page.width - doc.page.margins.right, doc.y)
    .stroke()
  doc.moveDown(0.3)
  doc
    .font(hasInter ? "Inter" : "Helvetica")
    .fontSize(11)
    .text(ticket.assignee ? `${ticket.assignee.name} (${ticket.assignee.email})` : "Não atribuído")

  doc.moveDown(0.75)
  doc.font("Helvetica-Bold").fontSize(12).text("Datas")
  doc
    .strokeColor("#E2E8F0")
    .moveTo(doc.page.margins.left, doc.y)
    .lineTo(doc.page.width - doc.page.margins.right, doc.y)
    .stroke()
  doc.moveDown(0.3)
  doc
    .font("Helvetica")
    .fontSize(11)
    .text(`Criado em: ${formatDateTime(ticket.createdAt)}`)
    .moveDown(0.15)
    .text(`Atualizado em: ${formatDateTime(ticket.updatedAt)}`)
    .moveDown(0.15)
    .text(`Resolvido em: ${formatDateTime(ticket.resolvedAt ?? null)}`)

  if (ticket.summary) {
    doc.moveDown(0.75)
    doc.font(hasInter ? "Inter-Bold" : "Helvetica-Bold").fontSize(12).text("Resumo")
    doc
      .strokeColor("#E2E8F0")
      .moveTo(doc.page.margins.left, doc.y)
      .lineTo(doc.page.width - doc.page.margins.right, doc.y)
      .stroke()
    doc
      .font(hasInter ? "Inter" : "Helvetica")
      .fontSize(11)
      .text(ticket.summary, { align: "justify", lineGap: 2 })
  }

  if (ticket.description) {
    doc.moveDown(0.75)
    doc.font(hasInter ? "Inter-Bold" : "Helvetica-Bold").fontSize(12).text("Descrição")
    doc
      .strokeColor("#E2E8F0")
      .moveTo(doc.page.margins.left, doc.y)
      .lineTo(doc.page.width - doc.page.margins.right, doc.y)
      .stroke()
    doc
      .font(hasInter ? "Inter" : "Helvetica")
      .fontSize(11)
      .text(htmlToPlainText(ticket.description), { align: "justify", lineGap: 2 })
  }

  if (ticket.comments.length > 0) {
    doc.addPage()
    doc.font(hasInter ? "Inter-Bold" : "Helvetica-Bold").fontSize(14).text("Comentários")
    doc.moveDown(0.6)
    const commentsSorted = [...ticket.comments].sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime())
    commentsSorted.forEach((comment, index) => {
      const visibility =
        comment.visibility === "PUBLIC" ? "Público" : "Interno"
      doc
        .font(hasInter ? "Inter-Bold" : "Helvetica-Bold")
        .fontSize(11)
        .text(`${comment.author.name} • ${visibility} • ${formatDateTime(comment.createdAt)}`)
      doc.moveDown(0.15)
      const body = htmlToPlainText(comment.body)
      if (body) {
        doc
          .font(hasInter ? "Inter" : "Helvetica")
          .fontSize(11)
          .text(body, { align: "justify", lineGap: 2, indent: 6 })
      }
      if (comment.attachments.length > 0) {
        doc.moveDown(0.25)
        doc.font(hasInter ? "Inter" : "Helvetica").fontSize(10).text("Anexos:")
        comment.attachments.forEach((attachment) => {
          doc
            .font(hasInter ? "Inter" : "Helvetica")
            .fontSize(10)
            .text(`• ${attachment.name ?? attachment.id}`, { indent: 16, lineGap: 1 })
        })
      }
      if (index < commentsSorted.length - 1) {
        doc.moveDown(1)
        doc
          .strokeColor("#E2E8F0")
          .moveTo(doc.x, doc.y)
          .lineTo(doc.page.width - doc.page.margins.right, doc.y)
          .stroke()
        doc.moveDown(0.9)
      }
    })
  }

  if (ticket.timeline.length > 0) {
    doc.addPage()
    doc.font(hasInter ? "Inter-Bold" : "Helvetica-Bold").fontSize(14).text("Linha do tempo")
    doc.moveDown(0.6)
    const timelineSorted = [...ticket.timeline].sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime())
    timelineSorted.forEach((event) => {
      const label = TICKET_TIMELINE_LABELS[event.type] ?? event.type
      doc
        .font(hasInter ? "Inter-Bold" : "Helvetica-Bold")
        .fontSize(11)
        .text(`${label} • ${formatDateTime(event.createdAt)}`)
      doc.moveDown(0.15)

      const friendly = buildTimelineMessage(event.type, event.payload)
      if (friendly) {
        doc
          .font(hasInter ? "Inter" : "Helvetica")
          .fontSize(10)
          .text(friendly, { indent: 16, lineGap: 1 })
      } else {
        const payloadText = stringifyPayload(event.payload)
        if (payloadText) {
          doc
            .font(hasInter ? "Inter" : "Helvetica")
            .fontSize(10)
            .text(payloadText, { indent: 16, lineGap: 1 })
        }
      }
      doc.moveDown(0.7)
    })
  }

  doc.end()
  const pdfBuffer = await pdfBufferPromise
  const pdfBytes = new Uint8Array(pdfBuffer)

  return new NextResponse(pdfBytes, {
    headers: {
      "Content-Type": "application/pdf",
      "Content-Disposition": `attachment; filename="ticket-${ticket.reference}.pdf"`,
      "Cache-Control": "no-store",
    },
  })
}
