"use client"

import { useEffect, useMemo, useRef, useState } from "react"
import { toast } from "sonner"
import { usePaginatedQuery, useQuery } from "convex/react"
import { api } from "@/convex/_generated/api"
import type { Id } from "@/convex/_generated/dataModel"
import { DEFAULT_TENANT_ID } from "@/lib/constants"
import { mapTicketsFromServerList } from "@/lib/mappers/ticket"
import type { Ticket, TicketQueueSummary } from "@/lib/schemas/ticket"
import { TicketsFilters, TicketFiltersState, defaultTicketFilters } from "@/components/tickets/tickets-filters"
import { TicketsTable } from "@/components/tickets/tickets-table"
import { TicketsBoard } from "@/components/tickets/tickets-board"
import { useAuth } from "@/lib/auth-client"
import { useDefaultQueues } from "@/hooks/use-default-queues"
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
import { LayoutGrid, List, ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from "lucide-react"
import { isVisitTicket } from "@/lib/ticket-matchers"
import { useTicketCategories } from "@/hooks/use-ticket-categories"
import { Button } from "@/components/ui/button"
import { Spinner } from "@/components/ui/spinner"

/** Número de tickets por página */
const TICKETS_PER_PAGE = 25

type TicketsViewProps = {
  initialFilters?: Partial<TicketFiltersState>
}

export function TicketsView({ initialFilters }: TicketsViewProps = {}) {
  const mergedInitialFilters = useMemo(
    () => ({
      ...defaultTicketFilters,
      ...(initialFilters ?? {}),
    }),
    [initialFilters]
  )
  const [filters, setFilters] = useState<TicketFiltersState>(mergedInitialFilters)
  const [viewMode, setViewMode] = useState<"table" | "board">("table")

  useEffect(() => {
    setFilters(mergedInitialFilters)
  }, [mergedInitialFilters])

  const { session, convexUserId, isStaff, role } = useAuth()
  const userId = session?.user?.id ?? null
  const tenantId = session?.user?.tenantId ?? DEFAULT_TENANT_ID
  const viewModeStorageKey = useMemo(() => {
    const userKey = userId ?? (convexUserId ? String(convexUserId) : "anonymous")
    return `tickets:view-mode:${tenantId}:${userKey}`
  }, [tenantId, userId, convexUserId])

  useEffect(() => {
    try {
      const stored = localStorage.getItem(viewModeStorageKey)
      if (stored === "table" || stored === "board") {
        setViewMode(stored)
      }
    } catch {
      // ignore
    }
  }, [viewModeStorageKey])

  useEffect(() => {
    try {
      localStorage.setItem(viewModeStorageKey, viewMode)
    } catch {
      // ignore
    }
  }, [viewMode, viewModeStorageKey])

  useDefaultQueues(tenantId)
  const { categories: ticketCategories } = useTicketCategories(tenantId)

  const queuesEnabled = Boolean(isStaff && convexUserId)
  const queuesResult = useQuery(
    api.queues.summary,
    queuesEnabled ? { tenantId, viewerId: convexUserId as Id<"users"> } : "skip"
  )
  const queues: TicketQueueSummary[] = Array.isArray(queuesResult) ? queuesResult : []
  const agents = useQuery(
    api.users.listAgents,
    isStaff && convexUserId ? { tenantId } : "skip"
  ) as { _id: string; name: string }[] | undefined

  // Argumentos para a query paginada de tickets
  const ticketsArgs = useMemo(() => {
    if (!convexUserId) return "skip" as const
    return {
      tenantId,
      viewerId: convexUserId as Id<"users">,
      status: filters.status ?? undefined,
      priority: filters.priority ?? undefined,
      channel: filters.channel ?? undefined,
      queueId: undefined, // Filtro por nome da fila aplicado no cliente
      assigneeId: filters.assigneeId ? (filters.assigneeId as unknown as Id<"users">) : undefined,
      search: filters.search || undefined,
    }
  }, [convexUserId, tenantId, filters.status, filters.priority, filters.channel, filters.assigneeId, filters.search])

  // Query paginada para evitar carregar todos os tickets de uma vez
  const {
    results: ticketsRaw,
    status: paginationStatus,
    loadMore,
  } = usePaginatedQuery(
    api.tickets.listPaginated,
    ticketsArgs,
    { initialNumItems: TICKETS_PER_PAGE }
  )

  // Indicadores de estado da paginação
  const isLoadingFirstPage = paginationStatus === "LoadingFirstPage"
  const isLoadingMore = paginationStatus === "LoadingMore"
  const canLoadMore = paginationStatus === "CanLoadMore"

  const tickets = useMemo(
    () => mapTicketsFromServerList(Array.isArray(ticketsRaw) ? (ticketsRaw as unknown[]) : []),
    [ticketsRaw]
  )
  const [companies, setCompanies] = useState<string[]>([])
  useEffect(() => {
    let aborted = false
    async function loadCompanies() {
      try {
        const r = await fetch("/api/admin/companies", { credentials: "include" })
        const json = (await r.json().catch(() => ({}))) as { companies?: Array<{ name: string }> }
        const names = Array.isArray(json.companies) ? json.companies.map((c) => c.name).filter(Boolean) : []
        if (!aborted) setCompanies(names.sort((a, b) => a.localeCompare(b, "pt-BR")))
      } catch {
        if (!aborted) setCompanies([])
      }
    }
    void loadCompanies()
    return () => {
      aborted = true
    }
  }, [])

  useEffect(() => {
    if (!convexUserId) return
    try {
      const key = `tickets:filters:${tenantId}:${String(convexUserId)}`
      const saved = localStorage.getItem(key)
      if (saved) {
        const parsed = JSON.parse(saved) as Partial<TicketFiltersState>
        setFilters((prev) => ({ ...prev, ...parsed }))
      }
    } catch {
      // ignore
    }
  }, [tenantId, convexUserId])

  const handleSaveDefault = () => {
    if (!convexUserId) return
    try {
      const key = `tickets:filters:${tenantId}:${String(convexUserId)}`
      localStorage.setItem(key, JSON.stringify(filters))
      toast.success("Filtro salvo como padrão")
    } catch {
      // ignore
    }
  }

  const handleClearDefault = () => {
    if (!convexUserId) return
    try {
      const key = `tickets:filters:${tenantId}:${String(convexUserId)}`
      localStorage.removeItem(key)
      toast.success("Padrão de filtro limpo")
    } catch {
      // ignore
    }
  }

  const filteredTickets = useMemo(() => {
    const completedStatuses = new Set<Ticket["status"]>(["RESOLVED"])
    let working = tickets

    if (!filters.status) {
      if (filters.view === "active") {
        working = working.filter((t) => !completedStatuses.has(t.status))
      } else if (filters.view === "completed") {
        working = working.filter((t) => completedStatuses.has(t.status))
      }
    }

    if (filters.queue) {
      working = working.filter((t) => t.queue === filters.queue)
    }
    if (filters.company) {
      working = working.filter((t) => (((t as unknown as { company?: { name?: string } })?.company?.name) ?? null) === filters.company)
    }
    if (filters.focusVisits) {
      working = working.filter((t) => isVisitTicket(t))
    }
    if (filters.categoryId) {
      working = working.filter((t) => (t.category?.id ?? null) === filters.categoryId)
    }
    const fromDate = parseDateInput(filters.dateFrom)
    if (fromDate) {
      working = working.filter((t) => getTicketDate(t) >= fromDate.getTime())
    }
    const toDate = parseDateInput(filters.dateTo, { endOfDay: true })
    if (toDate) {
      working = working.filter((t) => getTicketDate(t) <= toDate.getTime())
    }

    const sorted = [...working].sort((a, b) => {
      const diff = getTicketDate(a) - getTicketDate(b)
      return filters.sort === "oldest" ? diff : -diff
    })

    return sorted
  }, [
    tickets,
    filters.queue,
    filters.status,
    filters.view,
    filters.company,
    filters.focusVisits,
    filters.categoryId,
    filters.dateFrom,
    filters.dateTo,
    filters.sort,
  ])

  const previousIdsRef = useRef<string[]>([])
  const [enteringIds, setEnteringIds] = useState<Set<string>>(new Set())

  // Estado de paginação no cliente para a visão atual
  const [currentPage, setCurrentPage] = useState(1)
  const itemsPerPage = TICKETS_PER_PAGE
  const totalFilteredTickets = filteredTickets.length
  const totalPages = Math.max(1, Math.ceil(totalFilteredTickets / itemsPerPage))

  // Tickets da página atual (paginação no cliente sobre os resultados carregados)
  const paginatedTickets = useMemo(() => {
    const startIndex = (currentPage - 1) * itemsPerPage
    const endIndex = startIndex + itemsPerPage
    return filteredTickets.slice(startIndex, endIndex)
  }, [filteredTickets, currentPage, itemsPerPage])

  // Reseta para a primeira página quando os filtros mudam
  useEffect(() => {
    setCurrentPage(1)
  }, [filters])

  useEffect(() => {
    if (isLoadingFirstPage) {
      previousIdsRef.current = []
      setEnteringIds(new Set())
      return
    }
    const ids = filteredTickets.map((ticket) => ticket.id)
    const previous = previousIdsRef.current
    previousIdsRef.current = ids
    if (!previous.length) return
    const newIds = ids.filter((id) => !previous.includes(id))
    if (newIds.length === 0) return
    const highlight = new Set(newIds)
    setEnteringIds(highlight)
    const timeout = window.setTimeout(() => setEnteringIds(new Set()), 600)
    return () => window.clearTimeout(timeout)
  }, [filteredTickets, isLoadingFirstPage])

  return (
    <div className="flex flex-col gap-6 px-4 lg:px-6">
      <TicketsFilters
        onChange={setFilters}
        queues={queues.map((q) => q.name)}
        companies={companies}
        assignees={(agents ?? []).map((a) => ({ id: a._id, name: a.name }))}
        categories={ticketCategories.map((category) => ({ id: category.id, name: category.name }))}
        initialState={mergedInitialFilters}
        viewerRole={role}
      />
      <div className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
        <ToggleGroup
          type="single"
          value={viewMode}
          onValueChange={(next) => {
            if (!next) return
            setViewMode(next as "table" | "board")
          }}
          variant="outline"
          className="inline-flex rounded-md border border-border/60 bg-muted/30"
        >
          <ToggleGroupItem value="table" aria-label="Listagem em tabela" className="min-w-[96px] justify-center gap-2">
            <List className="size-4" />
            <span>Tabela</span>
          </ToggleGroupItem>
          <ToggleGroupItem value="board" aria-label="Visão em quadro" className="min-w-[96px] justify-center gap-2">
            <LayoutGrid className="size-4" />
            <span>Quadro</span>
          </ToggleGroupItem>
        </ToggleGroup>
        <div className="flex items-center justify-end gap-2">
          <button
            type="button"
            onClick={handleSaveDefault}
            className="rounded-lg border border-slate-300 bg-white px-3 py-1.5 text-xs font-medium text-neutral-800 hover:bg-slate-50"
          >
            Salvar filtro como padrão
          </button>
          <button
            type="button"
            onClick={handleClearDefault}
            className="rounded-lg border border-slate-300 bg-white px-3 py-1.5 text-xs font-medium text-neutral-600 hover:bg-slate-50"
          >
            Limpar padrão
          </button>
        </div>
      </div>
      {isLoadingFirstPage ? (
        <div className="rounded-2xl border border-slate-200 bg-white p-4 shadow-sm">
          <div className="flex items-center justify-center gap-2 py-12">
            <Spinner className="size-5 text-neutral-500" />
            <span className="text-sm text-neutral-600">Carregando tickets...</span>
          </div>
        </div>
      ) : viewMode === "board" ? (
        <TicketsBoard tickets={paginatedTickets} enteringIds={enteringIds} />
      ) : (
        <TicketsTable tickets={paginatedTickets} enteringIds={enteringIds} />
      )}

      {/* Controles de paginação numerada */}
      {!isLoadingFirstPage && filteredTickets.length > 0 && (
        <div className="flex flex-col items-center gap-4 rounded-2xl border border-slate-200 bg-white px-4 py-3 shadow-sm sm:flex-row sm:justify-between">
          <div className="text-sm text-neutral-600">
            Mostrando {((currentPage - 1) * itemsPerPage) + 1} a {Math.min(currentPage * itemsPerPage, totalFilteredTickets)} de {totalFilteredTickets} tickets
            {canLoadMore && (
              <span className="ml-1 text-neutral-400">(mais disponíveis)</span>
            )}
          </div>

          <div className="flex items-center gap-1">
            {/* Primeira página */}
            <Button
              variant="outline"
              size="sm"
              onClick={() => setCurrentPage(1)}
              disabled={currentPage === 1}
              className="h-8 w-8 p-0"
              title="Primeira página"
            >
              <ChevronsLeft className="h-4 w-4" />
            </Button>

            {/* Página anterior */}
            <Button
              variant="outline"
              size="sm"
              onClick={() => setCurrentPage((p) => Math.max(1, p - 1))}
              disabled={currentPage === 1}
              className="h-8 w-8 p-0"
              title="Página anterior"
            >
              <ChevronLeft className="h-4 w-4" />
            </Button>

            {/* Números das páginas */}
            <div className="flex items-center gap-1 px-2">
              {generatePageNumbers(currentPage, totalPages).map((pageNum, idx) =>
                pageNum === "..." ? (
                  <span key={`ellipsis-${idx}`} className="px-2 text-neutral-400">
                    ...
                  </span>
                ) : (
                  <Button
                    key={pageNum}
                    variant={currentPage === pageNum ? "default" : "outline"}
                    size="sm"
                    onClick={() => setCurrentPage(pageNum as number)}
                    className="h-8 min-w-[32px] px-2"
                  >
                    {pageNum}
                  </Button>
                )
              )}
            </div>

            {/* Próxima página */}
            <Button
              variant="outline"
              size="sm"
              onClick={() => setCurrentPage((p) => Math.min(totalPages, p + 1))}
              disabled={currentPage === totalPages}
              className="h-8 w-8 p-0"
              title="Próxima página"
            >
              <ChevronRight className="h-4 w-4" />
            </Button>

            {/* Última página */}
            <Button
              variant="outline"
              size="sm"
              onClick={() => setCurrentPage(totalPages)}
              disabled={currentPage === totalPages}
              className="h-8 w-8 p-0"
              title="Última página"
            >
              <ChevronsRight className="h-4 w-4" />
            </Button>
          </div>

          {/* Botão para carregar mais do servidor */}
          {canLoadMore && currentPage === totalPages && (
            <Button
              variant="outline"
              size="sm"
              onClick={() => loadMore(TICKETS_PER_PAGE)}
              disabled={isLoadingMore}
              className="flex items-center gap-2"
            >
              {isLoadingMore ? (
                <>
                  <Spinner className="size-4" />
                  Carregando...
                </>
              ) : (
                "Carregar mais tickets"
              )}
            </Button>
          )}
        </div>
      )}

      {/* Mensagem quando não há tickets */}
      {!isLoadingFirstPage && filteredTickets.length === 0 && (
        <div className="rounded-2xl border border-dashed border-slate-200 bg-white p-8 text-center shadow-sm">
          <p className="text-sm text-neutral-500">
            Nenhum ticket encontrado com os filtros selecionados.
          </p>
        </div>
      )}
    </div>
  )
}

/**
 * Gera array de números de página para exibição.
 * Mostra primeira, última e páginas próximas à atual com reticências.
 */
function generatePageNumbers(currentPage: number, totalPages: number): (number | "...")[] {
  if (totalPages <= 7) {
    return Array.from({ length: totalPages }, (_, i) => i + 1)
  }

  const pages: (number | "...")[] = []

  // Sempre mostra a primeira página
  pages.push(1)

  if (currentPage > 3) {
    pages.push("...")
  }

  // Páginas próximas à atual
  const start = Math.max(2, currentPage - 1)
  const end = Math.min(totalPages - 1, currentPage + 1)

  for (let i = start; i <= end; i++) {
    if (!pages.includes(i)) {
      pages.push(i)
    }
  }

  if (currentPage < totalPages - 2) {
    pages.push("...")
  }

  // Sempre mostra a última página
  if (!pages.includes(totalPages)) {
    pages.push(totalPages)
  }

  return pages
}

function parseDateInput(value: string | null, options?: { endOfDay?: boolean }) {
  if (!value) return null
  const [year, month, day] = value.split("-").map((part) => Number(part))
  if (!year || !month || !day) return null
  const date = new Date(
    year,
    month - 1,
    day,
    options?.endOfDay ? 23 : 0,
    options?.endOfDay ? 59 : 0,
    options?.endOfDay ? 59 : 0,
    options?.endOfDay ? 999 : 0
  )
  if (Number.isNaN(date.getTime())) return null
  return date
}

function getTicketDate(ticket: Ticket) {
  const createdAt = ticket.createdAt instanceof Date ? ticket.createdAt : new Date(ticket.createdAt)
  return createdAt.getTime()
}
