"use client"

import { useCallback, useEffect, useMemo, useRef, useState, type KeyboardEvent } from "react"
import { CheckCircle2, Eraser } from "lucide-react"
import { useMutation, useQuery } from "convex/react"
import { api } from "@/convex/_generated/api"
import type { Id } from "@/convex/_generated/dataModel"
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"
import { Button } from "@/components/ui/button"
import { Spinner } from "@/components/ui/spinner"
import { RichTextEditor, sanitizeEditorHtml, stripLeadingEmptyParagraphs } from "@/components/ui/rich-text-editor"
import { toast } from "sonner"
import { Checkbox } from "@/components/ui/checkbox"
import { Label } from "@/components/ui/label"
import { Input } from "@/components/ui/input"
import { Textarea } from "@/components/ui/textarea"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import type { TicketStatus } from "@/lib/schemas/ticket"
import { cn } from "@/lib/utils"

type ClosingTemplate = { id: string; title: string; body: string }

type TicketLinkSuggestion = {
  id: string
  reference: number
  subject: string
  status: string
  priority: string
}

const DEFAULT_PHONE_NUMBER = "(11) 4173-5368"
const DEFAULT_COMPANY_NAME = "Rever Tecnologia"

const sanitizeTemplate = (html: string) => stripLeadingEmptyParagraphs(sanitizeEditorHtml(html.trim()))
const htmlToPlainText = (value: string) =>
  value
    .replace(/<[^>]+>/g, " ")
    .replace(/&nbsp;/gi, " ")
    .replace(/\s+/g, " ")
    .trim()

export type AdjustWorkSummaryResult = {
  ticketId: Id<"tickets">
  totalWorkedMs: number
  internalWorkedMs: number
  externalWorkedMs: number
  serverNow?: number
  perAgentTotals?: Array<{
    agentId: string
    agentName: string | null
    agentEmail: string | null
    avatarUrl: string | null
    totalWorkedMs: number
    internalWorkedMs: number
    externalWorkedMs: number
  }>
}

const DEFAULT_CLOSING_TEMPLATES: ClosingTemplate[] = [
  {
    id: "default-standard",
    title: "Encerramento padrão",
    body: sanitizeTemplate(`
      <p>Olá {{cliente}},</p>
      <p>A equipe da ${DEFAULT_COMPANY_NAME} agradece o contato. Este ticket está sendo encerrado.</p>
      <p>Se surgirem novas questões, você pode reabrir o ticket em até 14 dias ou nos contatar pelo número <strong>${DEFAULT_PHONE_NUMBER}</strong>. Obrigado.</p>
      <p>{{agente}} · ${DEFAULT_COMPANY_NAME}</p>
    `),
  },
  {
    id: "default-no-contact",
    title: "Tentativa de contato sem sucesso",
    body: sanitizeTemplate(`
      <p>Prezado(a) {{cliente}},</p>
      <p>Realizamos uma tentativa de contato, mas não obtivemos sucesso.</p>
      <p>Por favor, retorne assim que possível para seguirmos com as verificações necessárias.</p>
      <p>Este ticket será encerrado após 3 tentativas realizadas sem sucesso.</p>
      <p>Telefone para contato: <strong>${DEFAULT_PHONE_NUMBER}</strong>.</p>
      <p>{{agente}} · ${DEFAULT_COMPANY_NAME}</p>
    `),
  },
  {
    id: "default-closed-after-attempts",
    title: "Encerramento após 3 tentativas",
    body: sanitizeTemplate(`
      <p>Prezado(a) {{cliente}},</p>
      <p>Esse ticket está sendo encerrado pois realizamos 3 tentativas sem retorno.</p>
      <p>Você pode reabrir este ticket em até 14 dias ou entrar em contato pelo telefone <strong>${DEFAULT_PHONE_NUMBER}</strong> quando preferir.</p>
      <p>{{agente}} · ${DEFAULT_COMPANY_NAME}</p>
    `),
  },
]

const WIZARD_STEPS = [
  { key: "message", title: "Mensagem", description: "Personalize o texto enviado ao cliente." },
  { key: "time", title: "Tempo", description: "Revise e ajuste o esforço registrado." },
  { key: "confirm", title: "Confirmações", description: "Vincule tickets e defina reabertura." },
] as const

type WizardStepKey = (typeof WIZARD_STEPS)[number]["key"]

const DRAFT_STORAGE_PREFIX = "close-ticket-draft:"

type CloseTicketDraft = {
  selectedTemplateId: string | null
  message: string
  shouldAdjustTime: boolean
  internalHours: string
  internalMinutes: string
  externalHours: string
  externalMinutes: string
  adjustReason: string
  linkedReference: string
  reopenWindowDays: string
  step: number
}

function applyTemplatePlaceholders(html: string, customerName?: string | null, agentName?: string | null) {
  const normalizedCustomer = customerName?.trim()
  const customerFallback = normalizedCustomer && normalizedCustomer.length > 0 ? normalizedCustomer : "cliente"
  const normalizedAgent = agentName?.trim()
  const agentFallback = normalizedAgent && normalizedAgent.length > 0 ? normalizedAgent : "Equipe Rever"
  return html
    .replace(/{{\s*(cliente|customer|customername|nome|nomecliente)\s*}}/gi, customerFallback)
    .replace(/{{\s*(agente|agent|atendente|responsavel|usu[aá]rio|usuario)\s*}}/gi, agentFallback)
    .replace(/{{\s*(empresa|company|companhia)\s*}}/gi, DEFAULT_COMPANY_NAME)
}

const splitDuration = (ms: number) => {
  const safeMs = Number.isFinite(ms) && ms > 0 ? ms : 0
  const totalMinutes = Math.round(safeMs / 60000)
  const hours = Math.floor(totalMinutes / 60)
  const minutes = totalMinutes % 60
  return { hours, minutes }
}

const formatDurationLabel = (ms: number) => {
  const { hours, minutes } = splitDuration(ms)
  if (hours > 0 && minutes > 0) return `${hours}h ${minutes}min`
  if (hours > 0) return `${hours}h`
  return `${minutes}min`
}

const STATUS_LABELS: Record<TicketStatus, string> = {
  PENDING: "Pendente",
  AWAITING_ATTENDANCE: "Em atendimento",
  PAUSED: "Pausado",
  RESOLVED: "Resolvido",
}

function formatTicketStatusLabel(status: string) {
  const normalized = (status ?? "").toUpperCase()
  if (normalized in STATUS_LABELS) {
    return STATUS_LABELS[normalized as TicketStatus]
  }
  return status
}

export function CloseTicketDialog({
  open,
  onOpenChange,
  ticketId,
  tenantId,
  actorId,
  ticketReference,
  requesterName,
  agentName,
  onSuccess,
  workSummary,
  onWorkSummaryAdjusted,
  canAdjustTime = false,
}: {
  open: boolean
  onOpenChange: (open: boolean) => void
  ticketId: string
  tenantId: string
  actorId: Id<"users"> | null
  ticketReference?: number | null
  requesterName?: string | null
  agentName?: string | null
  onSuccess: () => void
  workSummary?: {
    totalWorkedMs: number
    internalWorkedMs: number
    externalWorkedMs: number
  } | null
  onWorkSummaryAdjusted?: (result: AdjustWorkSummaryResult) => void
  canAdjustTime?: boolean
}) {
  const resolveTicketMutation = useMutation(api.tickets.resolveTicket)
  const addComment = useMutation(api.tickets.addComment)
  const adjustWorkSummary = useMutation(api.tickets.adjustWorkSummary)

  const closingTemplateArgs =
    actorId && open ? { tenantId, viewerId: actorId, kind: "closing" as const } : undefined
  const closingTemplatesRemote = useQuery(
    api.commentTemplates.list,
    closingTemplateArgs ?? "skip"
  )
  const closingTemplates = Array.isArray(closingTemplatesRemote)
    ? (closingTemplatesRemote as { id: string; title: string; body: string }[])
    : undefined

  const templatesLoading = Boolean(actorId && open && !Array.isArray(closingTemplatesRemote))
  const templates = useMemo<ClosingTemplate[]>(() => {
    if (closingTemplates && closingTemplates.length > 0) {
      return closingTemplates.map((t) => ({ id: t.id, title: t.title, body: t.body }))
    }
    return DEFAULT_CLOSING_TEMPLATES
  }, [closingTemplates])

  const [selectedTemplateId, setSelectedTemplateId] = useState<string | null>(null)
  const [message, setMessage] = useState<string>("")
  const [messageWarning, setMessageWarning] = useState(false)
  const messagePlainText = useMemo(() => htmlToPlainText(message ?? ""), [message])
  const hasMessage = messagePlainText.length > 0
  const [isSubmitting, setIsSubmitting] = useState(false)
  const [shouldAdjustTime, setShouldAdjustTime] = useState<boolean>(false)
  const [internalHours, setInternalHours] = useState<string>("0")
  const [internalMinutes, setInternalMinutes] = useState<string>("0")
  const [externalHours, setExternalHours] = useState<string>("0")
  const [externalMinutes, setExternalMinutes] = useState<string>("0")
  const [adjustReason, setAdjustReason] = useState<string>("")
  const enableAdjustment = Boolean(canAdjustTime && workSummary)
  const [linkedReference, setLinkedReference] = useState<string>("")
  const [linkSuggestions, setLinkSuggestions] = useState<TicketLinkSuggestion[]>([])
  const [linkedTicketSelection, setLinkedTicketSelection] = useState<TicketLinkSuggestion | null>(null)
  const [showLinkSuggestions, setShowLinkSuggestions] = useState(false)
  const [isSearchingLinks, setIsSearchingLinks] = useState(false)
  const linkSuggestionsAbortRef = useRef<AbortController | null>(null)
  const linkedReferenceInputRef = useRef<HTMLInputElement | null>(null)
  const suggestionHideTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
  const [reopenWindowDays, setReopenWindowDays] = useState<string>("14")
  const [currentStep, setCurrentStep] = useState(0)
  const [draftLoaded, setDraftLoaded] = useState(false)
  const [hasStoredDraft, setHasStoredDraft] = useState(false)

  const draftStorageKey = useMemo(() => `${DRAFT_STORAGE_PREFIX}${ticketId}`, [ticketId])

  useEffect(() => {
    if (messageWarning && hasMessage) {
      setMessageWarning(false)
    }
  }, [hasMessage, messageWarning])

  const digitsOnlyReference = linkedReference.replace(/[^0-9]/g, "").trim()

  const normalizedReference = useMemo(() => {
    if (!digitsOnlyReference) return null
    const parsed = Number(digitsOnlyReference)
    if (!Number.isFinite(parsed) || parsed <= 0) return null
    if (ticketReference && parsed === ticketReference) return null
    return parsed
  }, [digitsOnlyReference, ticketReference])

  const linkedTicket = useQuery(
    api.tickets.findByReference,
    actorId && normalizedReference ? { tenantId, viewerId: actorId, reference: normalizedReference } : "skip"
  ) as { id: Id<"tickets">; reference: number; subject: string; status: string } | null | undefined
  const isLinkLoading = Boolean(actorId && normalizedReference && linkedTicket === undefined)
  const hasSufficientDigits = digitsOnlyReference.length >= 3
  const linkedTicketCandidate = linkedTicketSelection ?? (linkedTicket ?? null)
  const linkNotFound = Boolean(
    hasSufficientDigits && !isLinkLoading && !linkedTicketSelection && linkedTicket === null && linkSuggestions.length === 0
  )

  const hydrateTemplateBody = useCallback((templateHtml: string) => {
    const withPlaceholders = applyTemplatePlaceholders(templateHtml, requesterName, agentName)
    return stripLeadingEmptyParagraphs(sanitizeEditorHtml(withPlaceholders))
  }, [requesterName, agentName])

  const resetFormState = useCallback(() => {
    setSelectedTemplateId(null)
    setMessage("")
    setIsSubmitting(false)
    setShouldAdjustTime(false)
    setAdjustReason("")
    setInternalHours("0")
    setInternalMinutes("0")
    setExternalHours("0")
    setExternalMinutes("0")
    setLinkedReference("")
    setLinkedTicketSelection(null)
    setLinkSuggestions([])
    setShowLinkSuggestions(false)
    setReopenWindowDays("14")
    setCurrentStep(0)
  }, [])

  const loadDraft = useCallback(() => {
    if (typeof window === "undefined") {
      setHasStoredDraft(false)
      return
    }
    const stored = window.localStorage.getItem(draftStorageKey)
    if (!stored) {
      setHasStoredDraft(false)
      setCurrentStep(0)
      resetFormState()
      return
    }
    try {
      const parsed = JSON.parse(stored) as CloseTicketDraft
      setSelectedTemplateId(parsed.selectedTemplateId ?? null)
      setMessage(parsed.message ?? "")
      setShouldAdjustTime(Boolean(parsed.shouldAdjustTime))
      setInternalHours(parsed.internalHours ?? "0")
      setInternalMinutes(parsed.internalMinutes ?? "0")
      setExternalHours(parsed.externalHours ?? "0")
      setExternalMinutes(parsed.externalMinutes ?? "0")
      setAdjustReason(parsed.adjustReason ?? "")
      setLinkedReference(parsed.linkedReference ?? "")
      setLinkedTicketSelection(null)
      setLinkSuggestions([])
      setShowLinkSuggestions(false)
      setReopenWindowDays(parsed.reopenWindowDays ?? "14")
      setCurrentStep(Math.min(parsed.step ?? 0, WIZARD_STEPS.length - 1))
      setHasStoredDraft(true)
    } catch (error) {
      console.error("Erro ao carregar rascunho de encerramento", error)
      window.localStorage.removeItem(draftStorageKey)
      setHasStoredDraft(false)
      resetFormState()
    }
  }, [draftStorageKey, resetFormState])

  const handleSaveDraft = useCallback(() => {
    if (typeof window === "undefined") return
    const payload: CloseTicketDraft = {
      selectedTemplateId,
      message,
      shouldAdjustTime,
      internalHours,
      internalMinutes,
      externalHours,
      externalMinutes,
      adjustReason,
      linkedReference,
      reopenWindowDays,
      step: currentStep,
    }
    window.localStorage.setItem(draftStorageKey, JSON.stringify(payload))
    setHasStoredDraft(true)
    toast.success("Rascunho salvo.")
  }, [
    adjustReason,
    currentStep,
    draftStorageKey,
    externalHours,
    externalMinutes,
    internalHours,
    internalMinutes,
    linkedReference,
    message,
    reopenWindowDays,
    selectedTemplateId,
    shouldAdjustTime,
  ])

  const handleDiscardDraft = useCallback(() => {
    if (typeof window !== "undefined") {
      window.localStorage.removeItem(draftStorageKey)
    }
    setHasStoredDraft(false)
    resetFormState()
    toast.success("Rascunho descartado.")
  }, [draftStorageKey, resetFormState])

  useEffect(() => {
    if (open && !draftLoaded) {
      loadDraft()
      setDraftLoaded(true)
    }
    if (!open && draftLoaded) {
      setDraftLoaded(false)
      resetFormState()
    }
  }, [open, draftLoaded, loadDraft, resetFormState])

  useEffect(() => {
    return () => {
      linkSuggestionsAbortRef.current?.abort()
      if (suggestionHideTimeoutRef.current) {
        clearTimeout(suggestionHideTimeoutRef.current)
      }
    }
  }, [])

  useEffect(() => {
    if (!open || !enableAdjustment || !shouldAdjustTime) return
    const internal = splitDuration(workSummary?.internalWorkedMs ?? 0)
    const external = splitDuration(workSummary?.externalWorkedMs ?? 0)
    setInternalHours(internal.hours.toString())
    setInternalMinutes(internal.minutes.toString())
    setExternalHours(external.hours.toString())
    setExternalMinutes(external.minutes.toString())
  }, [
    open,
    enableAdjustment,
    shouldAdjustTime,
    workSummary?.internalWorkedMs,
    workSummary?.externalWorkedMs,
  ])

  useEffect(() => {
    if (!shouldAdjustTime) {
      setAdjustReason("")
    }
  }, [shouldAdjustTime])

  useEffect(() => {
    if (!open) return
    const rawQuery = linkedReference.trim()
    if (rawQuery.length < 2) {
      linkSuggestionsAbortRef.current?.abort()
      setIsSearchingLinks(false)
      setLinkSuggestions([])
      setShowLinkSuggestions(false)
      return
    }

    const digitsForSelection = String(linkedTicketSelection?.reference ?? "")
    if (linkedTicketSelection && digitsForSelection === digitsOnlyReference) {
      setLinkSuggestions([])
      setIsSearchingLinks(false)
      return
    }

    linkSuggestionsAbortRef.current?.abort()
    const controller = new AbortController()
    linkSuggestionsAbortRef.current = controller
    setIsSearchingLinks(true)
    if (linkedReferenceInputRef.current && document.activeElement === linkedReferenceInputRef.current) {
      setShowLinkSuggestions(true)
    }

    fetch(`/api/tickets/mentions?q=${encodeURIComponent(rawQuery)}`, {
      signal: controller.signal,
    })
      .then(async (response) => {
        if (!response.ok) {
          return { items: [] as TicketLinkSuggestion[] }
        }
        const json = (await response.json()) as { items?: Array<{ id: string; reference: number; subject?: string | null; status?: string | null; priority?: string | null }> }
        const items = Array.isArray(json.items) ? json.items : []
        return {
          items: items
            .filter((item) => String(item.id) !== ticketId && Number(item.reference) !== ticketReference)
            .map((item) => ({
              id: String(item.id),
              reference: Number(item.reference),
              subject: item.subject ?? "",
              status: item.status ?? "PENDING",
              priority: item.priority ?? "MEDIUM",
            })),
        }
      })
      .then(({ items }) => {
        if (controller.signal.aborted) {
          return
        }
        setLinkSuggestions(items)
        if (linkedReferenceInputRef.current && document.activeElement === linkedReferenceInputRef.current) {
          setShowLinkSuggestions(true)
        }
      })
      .catch((error) => {
        if ((error as Error).name !== "AbortError") {
          console.error("Falha ao buscar tickets para vincular", error)
        }
      })
      .finally(() => {
        if (!controller.signal.aborted) {
          setIsSearchingLinks(false)
        }
      })

    return () => {
      controller.abort()
    }
  }, [open, linkedReference, digitsOnlyReference, linkedTicketSelection, ticketId, ticketReference])

  const handleTemplateSelect = (template: ClosingTemplate) => {
    setSelectedTemplateId(template.id)
    setMessage(hydrateTemplateBody(template.body))
  }

  const handleLinkedReferenceChange = (value: string) => {
    setLinkedReference(value)
    const digits = value.replace(/[^0-9]/g, "").trim()
    if (value.trim().length === 0) {
      setLinkedTicketSelection(null)
      setLinkSuggestions([])
      setShowLinkSuggestions(false)
      return
    }
    if (linkedTicketSelection && String(linkedTicketSelection.reference) !== digits) {
      setLinkedTicketSelection(null)
    }
    if (value.trim().length >= 2) {
      setShowLinkSuggestions(true)
    } else {
      setShowLinkSuggestions(false)
    }
  }

  const handleLinkedReferenceFocus = () => {
    if (suggestionHideTimeoutRef.current) {
      clearTimeout(suggestionHideTimeoutRef.current)
      suggestionHideTimeoutRef.current = null
    }
    if (linkSuggestions.length > 0) {
      setShowLinkSuggestions(true)
    }
  }

  const handleLinkedReferenceBlur = () => {
    suggestionHideTimeoutRef.current = setTimeout(() => {
      setShowLinkSuggestions(false)
    }, 150)
  }

  const handleSelectLinkSuggestion = (suggestion: TicketLinkSuggestion) => {
    if (suggestionHideTimeoutRef.current) {
      clearTimeout(suggestionHideTimeoutRef.current)
      suggestionHideTimeoutRef.current = null
    }
    setLinkedTicketSelection(suggestion)
    setLinkedReference(`#${suggestion.reference}`)
    setShowLinkSuggestions(false)
    setLinkSuggestions([])
  }

  const handleLinkedReferenceKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
    if (event.key === "Enter") {
      if (linkSuggestions.length > 0) {
        event.preventDefault()
        handleSelectLinkSuggestion(linkSuggestions[0])
      }
    }
  }

  const hasFormChanges =
    Boolean(message.trim().length) ||
    Boolean(selectedTemplateId) ||
    shouldAdjustTime ||
    adjustReason.trim().length > 0 ||
    linkedReference.trim().length > 0 ||
    reopenWindowDays !== "14"

  const canSaveDraft = hasFormChanges && !isSubmitting

  const goToStep = (index: number) => {
    if (index < 0 || index >= WIZARD_STEPS.length) return
    setCurrentStep(index)
  }

  const goToNextStep = useCallback(() => {
    if (currentStep === 0 && !hasMessage) {
      setMessageWarning(true)
      toast.error("Escreva uma mensagem de encerramento antes de continuar.")
      return
    }
    setCurrentStep((prev) => Math.min(prev + 1, WIZARD_STEPS.length - 1))
  }, [currentStep, hasMessage])
  const goToPreviousStep = () => setCurrentStep((prev) => Math.max(prev - 1, 0))
  const isLastStep = currentStep === WIZARD_STEPS.length - 1

  const handleSubmit = async () => {
    if (!actorId) {
      toast.error("É necessário estar autenticado para encerrar o ticket.")
      return
    }

    const applyAdjustment = enableAdjustment && shouldAdjustTime
    let targetInternalMs = 0
    let targetExternalMs = 0
    let trimmedReason = ""

    if (applyAdjustment) {
      const parsePart = (value: string, label: string) => {
        const trimmed = value.trim()
        if (trimmed.length === 0) return 0
        if (!/^\d+$/u.test(trimmed)) {
          toast.error(`Informe um número válido para ${label}.`)
          return null
        }
        return Number.parseInt(trimmed, 10)
      }

      const internalHoursValue = parsePart(internalHours, "horas internas")
      if (internalHoursValue === null) return
      const internalMinutesValue = parsePart(internalMinutes, "minutos internos")
      if (internalMinutesValue === null) return
      if (internalMinutesValue >= 60) {
        toast.error("Os minutos internos devem estar entre 0 e 59.")
        return
      }

      const externalHoursValue = parsePart(externalHours, "horas externas")
      if (externalHoursValue === null) return
      const externalMinutesValue = parsePart(externalMinutes, "minutos externos")
      if (externalMinutesValue === null) return
      if (externalMinutesValue >= 60) {
        toast.error("Os minutos externos devem estar entre 0 e 59.")
        return
      }

      targetInternalMs = (internalHoursValue * 60 + internalMinutesValue) * 60000
      targetExternalMs = (externalHoursValue * 60 + externalMinutesValue) * 60000
      trimmedReason = adjustReason.trim()
      if (trimmedReason.length < 5) {
        toast.error("Descreva o motivo do ajuste (mínimo de 5 caracteres).")
        return
      }
    }

    const withPlaceholders = applyTemplatePlaceholders(message, requesterName, agentName)
    const sanitizedMessage = stripLeadingEmptyParagraphs(sanitizeEditorHtml(withPlaceholders))
    const hasContent = sanitizedMessage.replace(/<[^>]*>/g, "").trim().length > 0
    if (!hasContent) {
      toast.error("Inclua uma mensagem de encerramento antes de finalizar o ticket.", { id: "close-ticket" })
      return
    }

    toast.dismiss("close-ticket")
    setIsSubmitting(true)
    toast.loading(applyAdjustment ? "Ajustando tempo e encerrando ticket..." : "Encerrando ticket...", { id: "close-ticket" })
    try {
      if (linkedReference.trim().length > 0) {
        if (isLinkLoading) {
          toast.error("Aguarde carregar o ticket vinculado antes de encerrar.", { id: "close-ticket" })
          return
        }
        if (linkNotFound || !linkedTicketCandidate) {
          toast.error("Não encontramos o ticket informado para vincular. Verifique o número e tente novamente.", {
            id: "close-ticket",
          })
          return
        }
      }

      if (applyAdjustment) {
        const result = (await adjustWorkSummary({
          ticketId: ticketId as unknown as Id<"tickets">,
          actorId,
          internalWorkedMs: targetInternalMs,
          externalWorkedMs: targetExternalMs,
          reason: trimmedReason,
        })) as AdjustWorkSummaryResult
        onWorkSummaryAdjusted?.(result)
      }

      const reopenDaysNumber = Number(reopenWindowDays)
      await resolveTicketMutation({
        ticketId: ticketId as unknown as Id<"tickets">,
        actorId,
        resolvedWithTicketId: linkedTicketCandidate ? (linkedTicketCandidate.id as Id<"tickets">) : undefined,
        reopenWindowDays: Number.isFinite(reopenDaysNumber) ? reopenDaysNumber : undefined,
      })
      await addComment({
        ticketId: ticketId as unknown as Id<"tickets">,
        authorId: actorId,
        visibility: "PUBLIC",
        body: sanitizedMessage,
        attachments: [],
      })
      toast.success("Ticket encerrado com sucesso!", { id: "close-ticket" })
      if (typeof window !== "undefined") {
        window.localStorage.removeItem(draftStorageKey)
      }
      setHasStoredDraft(false)
      onOpenChange(false)
      onSuccess()
    } catch (error) {
      console.error(error)
      const fallback = applyAdjustment
        ? "Não foi possível ajustar o tempo ou encerrar o ticket."
        : "Não foi possível encerrar o ticket."
      const message = error instanceof Error && error.message.trim().length > 0 ? error.message : fallback
      toast.error(message, { id: "close-ticket" })
    } finally {
      setIsSubmitting(false)
    }
  }

  const renderStepContent = () => {
    const stepKey = WIZARD_STEPS[currentStep]?.key ?? "message"

    if (stepKey === "time") {
      if (!enableAdjustment) {
        return (
          <div className="rounded-2xl border border-dashed border-slate-200 bg-slate-50 px-4 py-6 text-sm text-neutral-600">
            Os ajustes de tempo não estão disponíveis para o seu perfil. Apenas administradores e agentes podem alterar o tempo registrado
            antes de encerrar um ticket.
          </div>
        )
      }
      return (
        <div className="rounded-2xl border border-slate-200 bg-white px-4 py-5 shadow-sm">
          <div className="flex flex-wrap items-center justify-between gap-3">
            <div>
              <p className="text-sm font-semibold text-neutral-900">Ajustar tempo antes de encerrar</p>
              <p className="text-xs text-neutral-500">
                Atualize o tempo registrado e informe um motivo para o ajuste. O comentário fica visível apenas para a equipe.
              </p>
            </div>
            <div className="flex items-center gap-2">
              <Checkbox
                id="toggle-time-adjustment"
                checked={shouldAdjustTime}
                onCheckedChange={(checked) => setShouldAdjustTime(Boolean(checked))}
                disabled={isSubmitting}
              />
              <Label htmlFor="toggle-time-adjustment" className="text-sm font-medium text-neutral-800">
                Incluir ajuste
              </Label>
            </div>
          </div>
          {shouldAdjustTime ? (
            <div className="mt-4 space-y-4">
              <div className="grid gap-4 sm:grid-cols-2">
                <div className="space-y-2">
                  <p className="text-xs font-semibold uppercase tracking-wide text-neutral-500">Tempo interno</p>
                  <div className="grid grid-cols-2 gap-2">
                    <div className="space-y-1">
                      <Label htmlFor="adjust-internal-hours" className="text-xs text-neutral-600">
                        Horas
                      </Label>
                      <Input
                        id="adjust-internal-hours"
                        type="number"
                        min={0}
                        step={1}
                        inputMode="numeric"
                        value={internalHours}
                        onChange={(event) => setInternalHours(event.target.value)}
                        disabled={isSubmitting}
                      />
                    </div>
                    <div className="space-y-1">
                      <Label htmlFor="adjust-internal-minutes" className="text-xs text-neutral-600">
                        Minutos
                      </Label>
                      <Input
                        id="adjust-internal-minutes"
                        type="number"
                        min={0}
                        max={59}
                        step={1}
                        inputMode="numeric"
                        value={internalMinutes}
                        onChange={(event) => setInternalMinutes(event.target.value)}
                        disabled={isSubmitting}
                      />
                    </div>
                  </div>
                  <p className="text-xs text-neutral-500">Atual: {formatDurationLabel(workSummary?.internalWorkedMs ?? 0)}</p>
                </div>
                <div className="space-y-2">
                  <p className="text-xs font-semibold uppercase tracking-wide text-neutral-500">Tempo externo</p>
                  <div className="grid grid-cols-2 gap-2">
                    <div className="space-y-1">
                      <Label htmlFor="adjust-external-hours" className="text-xs text-neutral-600">
                        Horas
                      </Label>
                      <Input
                        id="adjust-external-hours"
                        type="number"
                        min={0}
                        step={1}
                        inputMode="numeric"
                        value={externalHours}
                        onChange={(event) => setExternalHours(event.target.value)}
                        disabled={isSubmitting}
                      />
                    </div>
                    <div className="space-y-1">
                      <Label htmlFor="adjust-external-minutes" className="text-xs text-neutral-600">
                        Minutos
                      </Label>
                      <Input
                        id="adjust-external-minutes"
                        type="number"
                        min={0}
                        max={59}
                        step={1}
                        inputMode="numeric"
                        value={externalMinutes}
                        onChange={(event) => setExternalMinutes(event.target.value)}
                        disabled={isSubmitting}
                      />
                    </div>
                  </div>
                  <p className="text-xs text-neutral-500">Atual: {formatDurationLabel(workSummary?.externalWorkedMs ?? 0)}</p>
                </div>
              </div>
              <div className="space-y-1.5">
                <Label htmlFor="adjust-reason" className="text-xs text-neutral-600">
                  Motivo do ajuste
                </Label>
                <Textarea
                  id="adjust-reason"
                  value={adjustReason}
                  onChange={(event) => setAdjustReason(event.target.value)}
                  placeholder="Descreva por que o tempo precisa ser ajustado..."
                  rows={3}
                  disabled={isSubmitting}
                />
                <p className="text-xs text-neutral-500">
                  Registre o motivo para fins de auditoria interna. Informe valores em minutos quando menor que 1 hora.
                </p>
              </div>
            </div>
          ) : null}
        </div>
      )
    }

    if (stepKey === "confirm") {
      return (
        <div className="space-y-4">
          <div className="rounded-2xl border border-slate-200 bg-white px-4 py-5 shadow-sm">
            <div className="grid gap-4 lg:grid-cols-[minmax(0,1fr)_220px]">
              <div className="space-y-2">
                <Label htmlFor="linked-reference" className="text-sm font-medium text-neutral-800">
                  Ticket relacionado (opcional)
                </Label>
                <div className="relative">
                  <Input
                    id="linked-reference"
                    ref={linkedReferenceInputRef}
                    value={linkedReference}
                    onChange={(event) => handleLinkedReferenceChange(event.target.value)}
                    onFocus={handleLinkedReferenceFocus}
                    onBlur={handleLinkedReferenceBlur}
                    onKeyDown={handleLinkedReferenceKeyDown}
                    placeholder="Buscar por número ou assunto do ticket"
                    disabled={isSubmitting}
                  />
                  {showLinkSuggestions ? (
                    <div className="absolute left-0 right-0 z-50 mt-1 max-h-60 overflow-y-auto rounded-lg border border-slate-200 bg-white shadow-lg">
                      {isSearchingLinks ? (
                        <div className="flex items-center gap-2 px-3 py-2 text-sm text-neutral-500">
                          <Spinner className="size-3" /> Buscando tickets relacionados...
                        </div>
                      ) : linkSuggestions.length === 0 ? (
                        <div className="px-3 py-2 text-sm text-neutral-500">Nenhum ticket encontrado.</div>
                      ) : (
                        linkSuggestions.map((suggestion) => {
                          const statusLabel = formatTicketStatusLabel(suggestion.status)
                          return (
                            <button
                              key={suggestion.id}
                              type="button"
                              onMouseDown={(event) => event.preventDefault()}
                              onClick={() => handleSelectLinkSuggestion(suggestion)}
                              className="flex w-full flex-col gap-1 px-3 py-2 text-left text-sm transition hover:bg-slate-100 focus:bg-slate-100"
                            >
                              <span className="font-semibold text-neutral-900">#{suggestion.reference}</span>
                              {suggestion.subject ? (
                                <span className="text-xs text-neutral-600">{suggestion.subject}</span>
                              ) : null}
                              <span className="text-xs text-neutral-500">Status: {statusLabel}</span>
                            </button>
                          )
                        })
                      )}
                    </div>
                  ) : null}
                </div>
                {linkedReference.trim().length === 0 ? (
                  <p className="text-xs text-neutral-500">Informe o número de outro ticket quando o atendimento estiver relacionado.</p>
                ) : isLinkLoading ? (
                  <p className="flex items-center gap-2 text-xs text-neutral-500">
                    <Spinner className="size-3" /> Procurando ticket #{normalizedReference}...
                  </p>
                ) : linkNotFound ? (
                  <p className="text-xs text-red-500">
                    Ticket não encontrado ou sem acesso permitido. Verifique o número informado.
                  </p>
                ) : linkedTicketCandidate ? (
                  <p className="text-xs text-emerald-600">
                    Será registrado vínculo com o ticket #{linkedTicketCandidate.reference} —{" "}
                    {linkedTicketCandidate.subject ?? "Sem assunto"}
                  </p>
                ) : null}
              </div>
              <div className="space-y-2">
                <Label htmlFor="reopen-window" className="text-sm font-medium text-neutral-800">
                  Reabertura permitida
                </Label>
                <Select value={reopenWindowDays} onValueChange={setReopenWindowDays} disabled={isSubmitting}>
                  <SelectTrigger id="reopen-window">
                    <SelectValue placeholder="Escolha o prazo" />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectItem value="7">7 dias</SelectItem>
                    <SelectItem value="14">14 dias</SelectItem>
                  </SelectContent>
                </Select>
                <p className="text-xs text-neutral-500">Após esse período o ticket não poderá ser reaberto automaticamente.</p>
              </div>
            </div>
          </div>
        </div>
      )
    }

    return (
      <div className="space-y-4">
        <div className="space-y-2">
          <p className="text-sm font-medium text-neutral-800">Modelos rápidos</p>
          {templatesLoading ? (
            <div className="flex items-center gap-2 text-sm text-neutral-600">
              <Spinner className="size-4" /> Carregando templates...
            </div>
          ) : (
            <div className="flex flex-wrap gap-2">
              {templates.map((template) => (
                <Button
                  key={template.id}
                  type="button"
                  variant={selectedTemplateId === template.id ? "default" : "outline"}
                  size="sm"
                  onClick={() => handleTemplateSelect(template)}
                  disabled={isSubmitting}
                >
                  {template.title}
                </Button>
              ))}
            </div>
          )}
          <div className="flex flex-wrap items-center justify-between gap-2 text-xs text-neutral-500">
            <span>
              Use <code className="rounded bg-slate-100 px-1 py-0.5">{"{{cliente}}"}</code> dentro do template para inserir automaticamente o nome do solicitante.
            </span>
            <Button
              type="button"
              variant="outline"
              size="icon"
              aria-label="Limpar mensagem"
              onClick={() => {
                setMessage("")
                setSelectedTemplateId(null)
              }}
              disabled={isSubmitting}
            >
              <Eraser className="size-4" />
              <span className="sr-only">Limpar mensagem</span>
            </Button>
          </div>
        </div>
        <div
          className={cn(
            "space-y-2 rounded-2xl border px-4 py-3 transition",
            messageWarning ? "border-amber-500 bg-amber-50/70 shadow-[0_0_0_1px_rgba(251,191,36,0.4)]" : "border-transparent bg-transparent"
          )}
        >
          <p className="text-sm font-medium text-neutral-800">Mensagem de encerramento</p>
          <RichTextEditor
            value={message}
            onChange={setMessage}
            minHeight={220}
            placeholder="Descreva o encerramento para o cliente..."
          />
          <p
            className={cn(
              "text-xs",
              messageWarning ? "font-semibold text-amber-700" : "text-neutral-500"
            )}
          >
            Este texto é enviado ao cliente e é obrigatório para encerrar o ticket.
            {messageWarning ? " Digite uma mensagem para prosseguir." : ""}
          </p>
        </div>
      </div>
    )
  }
  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
        <DialogHeader>
          <DialogTitle>Encerrar ticket</DialogTitle>
          <DialogDescription>
            Complete as etapas abaixo para encerrar o ticket e registrar o comunicado final.
          </DialogDescription>
        </DialogHeader>
        <div className="space-y-6">
          <div className="rounded-2xl border border-slate-200 bg-slate-50 p-4">
            <div className="flex flex-col gap-4 sm:flex-row sm:flex-wrap">
              {WIZARD_STEPS.map((step, index) => {
                const isActive = index === currentStep
                const isCompleted = index < currentStep
                const canNavigate = index <= currentStep
                return (
                  <div
                    key={step.key}
                    className="flex min-w-[200px] flex-1 items-center gap-3"
                  >
                    <button
                      type="button"
                      onClick={() => (canNavigate && !isSubmitting ? goToStep(index) : undefined)}
                      disabled={!canNavigate || isSubmitting}
                      className={cn(
                        "flex size-9 items-center justify-center rounded-full border text-sm font-semibold transition",
                        isCompleted
                          ? "border-emerald-500 bg-emerald-500 text-white"
                          : isActive
                            ? "border-slate-900 bg-slate-900 text-white"
                            : "border-slate-200 bg-white text-neutral-500"
                      )}
                    >
                      {isCompleted ? <CheckCircle2 className="size-4" /> : index + 1}
                    </button>
                    <div>
                      <p className={cn("text-sm font-semibold", isActive ? "text-neutral-900" : "text-neutral-600")}>
                        {step.title}
                      </p>
                      <p className="text-xs text-neutral-500">{step.description}</p>
                    </div>
                  </div>
                )
              })}
            </div>
          </div>
          {renderStepContent()}
        </div>
        <DialogFooter className="mt-4 w-full border-t border-slate-100 pt-4">
          {hasStoredDraft ? (
            <div className="w-full text-xs text-neutral-500">Rascunho salvo localmente.</div>
          ) : null}
          <div className="flex w-full flex-wrap items-center justify-between gap-2">
            <div className="flex flex-wrap items-center gap-2">
              <Button
                type="button"
                variant="ghost"
                onClick={() => onOpenChange(false)}
                disabled={isSubmitting}
              >
                Cancelar
              </Button>
              <Button
                type="button"
                variant="outline"
                onClick={handleSaveDraft}
                disabled={!canSaveDraft}
              >
                Salvar rascunho
              </Button>
              {hasStoredDraft ? (
                <Button type="button" variant="ghost" onClick={handleDiscardDraft} disabled={isSubmitting}>
                  Descartar rascunho
                </Button>
              ) : null}
            </div>
            <div className="flex flex-wrap items-center gap-2">
              {currentStep > 0 ? (
                <Button type="button" variant="outline" onClick={goToPreviousStep} disabled={isSubmitting}>
                  Voltar
                </Button>
              ) : null}
              {!isLastStep ? (
                <Button type="button" onClick={goToNextStep} disabled={isSubmitting}>
                  Próximo passo
                </Button>
              ) : (
                <Button type="button" onClick={handleSubmit} disabled={isSubmitting || !actorId}>
                  {isSubmitting ? <Spinner className="size-4 text-white" /> : "Encerrar ticket"}
                </Button>
              )}
            </div>
          </div>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}
