'use client'

import React, { useState, useEffect, useCallback, useRef } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import {
  Search, X, Users, Check, ChevronDown,
  Loader2, UserCheck, BookUser,
} from 'lucide-react'
import { contactApi, type BackendContact } from '../services/chatApiService'
import { formatPhone, getAvatarProxyUrl } from '../utils/inboxUtils'

// ─── Helpers ──────────────────────────────────────────────────────────────────

function resolveDisplayName(c: BackendContact): string {
  return (
    c.name ??
    c.verifiedName ??
    c.notify ??
    formatPhone(c.phone ?? c.jid.split('@')[0], c.jid)
  )
}

function getInitials(name: string): string {
  const parts = name.trim().split(/\s+/)
  if (parts.length >= 2) return (parts[0][0]! + parts[1]![0]).toUpperCase()
  return name.slice(0, 2).toUpperCase()
}

function avatarColor(jid: string): string {
  let h = 0
  for (let i = 0; i < jid.length; i++) h = jid.charCodeAt(i) + ((h << 5) - h)
  return `hsl(${Math.abs(h % 360)}, 58%, 42%)`
}

// ─── Types ────────────────────────────────────────────────────────────────────

interface Props {
  open: boolean
  instanceId: string | null
  onConfirm: (phones: string[]) => void
  onClose: () => void
}

/**
 * URL do avatar via proxy local (evita expiração do CDN do WhatsApp).
 *
 * Só retorna URL se o backend já confirmou que existe avatar (c.avatarUrl != null).
 * Se for null, evitamos disparar uma requisição que o proxy só pode responder
 * com 204 — economiza até 60 round-trips por página de contatos sem foto.
 */
function resolveAvatarUrl(c: BackendContact, instanceId: string | null): string | null {
  if (!instanceId) return null
  // @lid e @g.us não suportam profilePictureUrl — proxy retornaria 204
  if (c.jid.endsWith('@lid') || c.jid.endsWith('@g.us')) return null
  // Banco já sabe que não tem foto — pula o proxy direto pro fallback de iniciais
  if (!c.avatarUrl) return null
  return getAvatarProxyUrl({ instance_name: instanceId, remote_jid: c.jid })
}

const PAGE_SIZE = 60

// ─── Avatar de linha (sem hack de DOM no onError) ─────────────────────────────

interface RowAvatarProps {
  url: string | null
  name: string
  jid: string
}

/**
 * Avatar de uma linha da lista. Renderiza <img> quando há URL e ela carrega;
 * cai pro fallback de iniciais coloridas em qualquer outro caso.
 *
 * Substitui o `onError` antigo que mexia em `nextSibling` direto no DOM —
 * isso quebrava em re-renders do React e às vezes escondia a inicial em
 * vez do <img>. Aqui o estado de erro vive no React.
 */
function RowAvatar({ url, name, jid }: RowAvatarProps) {
  const [failed, setFailed] = useState(false)
  // Reseta o estado de falha quando a URL muda (ex: re-fetch da lista)
  useEffect(() => { setFailed(false) }, [url])

  if (url && !failed) {
    return (
      <img
        src={url}
        alt=""
        loading="lazy"
        decoding="async"
        className="w-9 h-9 rounded-full object-cover flex-shrink-0"
        onError={() => setFailed(true)}
      />
    )
  }
  return (
    <div
      className="w-9 h-9 rounded-full flex-shrink-0 flex items-center justify-center text-white text-xs font-bold"
      style={{ backgroundColor: avatarColor(jid) }}
    >
      {getInitials(name)}
    </div>
  )
}

// ─── Component ────────────────────────────────────────────────────────────────

export default function ContactPickerModal({ open, instanceId, onConfirm, onClose }: Props) {
  const [search, setSearch]         = useState('')
  const [contacts, setContacts]     = useState<BackendContact[]>([])
  const [loading, setLoading]       = useState(false)
  const [hasMore, setHasMore]       = useState(false)
  const [offset, setOffset]         = useState(0)
  const [total, setTotal]           = useState(0)
  const [selected, setSelected]     = useState<Set<string>>(new Set())
  const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null)
  const searchRef   = useRef<HTMLInputElement>(null)
  // Marca um run do effect de busca pra ser ignorado. Usado quando o effect
  // de open chama setSearch('') — esse setSearch dispara o effect de busca,
  // que agenda um refetch em 280ms; mas o open já fez fetchContacts imediato,
  // então o refetch é redundante e cria race com cliques em "carregar mais".
  const skipNextDebounceRef = useRef(false)
  // Contador de geração — qualquer fetch só aplica seu resultado se ainda for
  // o mais recente. Defende contra cenários onde uma busca/clear/load-more
  // dispara um fetch antes da resposta anterior chegar.
  const fetchGenRef = useRef(0)

  // ── Fetch ──────────────────────────────────────────────────────────────────

  const fetchContacts = useCallback(async (q: string, off: number, replace = false) => {
    if (!instanceId) return
    const gen = ++fetchGenRef.current
    setLoading(true)
    try {
      const res = await contactApi.list({
        instanceId,
        search: q.trim() || undefined,
        blocked: false,
        limit: PAGE_SIZE,
        offset: off,
      })
      // Resposta obsoleta — outro fetch já foi disparado depois deste. Ignora
      // pra não sobrescrever o estado correto com dados antigos.
      if (gen !== fetchGenRef.current) return
      // O `offset` é o SKIP da query SQL no backend, então tem que avançar
      // pelo tamanho CRU da página devolvida — não pelo filtrado. Senão, ao
      // descartar @g.us/@lid client-side, a próxima página começa "para trás"
      // do que já mostramos e o backend devolve linhas que já estão na tela.
      const raw = res.contacts ?? []
      const rows = raw.filter((c: BackendContact) =>
        !c.jid.endsWith('@g.us') && !c.jid.endsWith('@lid')
      )
      const total = res.total ?? raw.length
      const nextOffset = off + raw.length
      setTotal(total)
      setContacts(prev => {
        const merged = replace ? rows : [...prev, ...rows]
        // Dedupe por jid — defesa contra fetches concorrentes (debounce de
        // busca x effect de open) que podem reaplicar rows já presentes.
        const seen = new Set<string>()
        const unique: BackendContact[] = []
        for (const c of merged) {
          if (seen.has(c.jid)) continue
          seen.add(c.jid)
          unique.push(c)
        }
        return unique
      })
      setOffset(nextOffset)
      setHasMore(nextOffset < total)
    } catch {
      // silent
    } finally {
      // Só limpa o loading se este fetch ainda for o mais recente — senão
      // o spinner some enquanto o request "vencedor" continua em voo.
      if (gen === fetchGenRef.current) setLoading(false)
    }
  }, [instanceId])

  // ── Open/reset ─────────────────────────────────────────────────────────────

  useEffect(() => {
    if (!open) return
    // Se a busca tiver conteúdo de uma sessão anterior, o setSearch('') abaixo
    // vai disparar o effect debounced. Marca esse run pra ser ignorado — o
    // fetchContacts imediato logo abaixo já cobre o caso, e deixar o debounce
    // rodar cria race com cliques em "carregar mais" nos primeiros 280ms.
    if (search !== '') skipNextDebounceRef.current = true
    setSearch('')
    setSelected(new Set())
    setContacts([])
    setOffset(0)
    setHasMore(false)
    fetchContacts('', 0, true)
    setTimeout(() => searchRef.current?.focus(), 100)
  }, [open, instanceId])

  // ── Debounced search ───────────────────────────────────────────────────────

  useEffect(() => {
    if (!open) return
    if (skipNextDebounceRef.current) {
      skipNextDebounceRef.current = false
      return
    }
    if (debounceRef.current) clearTimeout(debounceRef.current)
    debounceRef.current = setTimeout(() => {
      // sem setOffset(0) — fetchContacts já atualiza o offset com base na
      // resposta, e setá-lo aqui no meio do voo capturava o valor errado no
      // onClick do botão "carregar mais" durante a janela de race.
      fetchContacts(search, 0, true)
    }, 280)
    return () => { if (debounceRef.current) clearTimeout(debounceRef.current) }
  // fetchContacts incluído nos deps para evitar stale closure quando instanceId muda
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [search, fetchContacts])

  // ── Keyboard close ─────────────────────────────────────────────────────────

  useEffect(() => {
    const handler = (e: KeyboardEvent) => {
      if (e.key === 'Escape') onClose()
    }
    if (open) document.addEventListener('keydown', handler)
    return () => document.removeEventListener('keydown', handler)
  }, [open, onClose])

  // ── Helpers ────────────────────────────────────────────────────────────────

  const getPhone = (c: BackendContact) => c.phone ?? c.jid.split('@')[0]

  const toggle = useCallback((c: BackendContact) => {
    const phone = getPhone(c)
    setSelected(prev => {
      const next = new Set(prev)
      next.has(phone) ? next.delete(phone) : next.add(phone)
      return next
    })
  }, [])

  const allVisible = contacts.every(c => selected.has(getPhone(c))) && contacts.length > 0

  const toggleAll = () => {
    if (allVisible) {
      setSelected(prev => {
        const next = new Set(prev)
        contacts.forEach(c => next.delete(getPhone(c)))
        return next
      })
    } else {
      setSelected(prev => {
        const next = new Set(prev)
        contacts.forEach(c => next.add(getPhone(c)))
        return next
      })
    }
  }

  const handleConfirm = () => {
    onConfirm(Array.from(selected))
    onClose()
  }

  // ─────────────────────────────────────────────────────────────────────────────

  return (
    <AnimatePresence>
      {open && (
        <>
          {/* Backdrop */}
          <motion.div
            key="backdrop"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            transition={{ duration: 0.18 }}
            className="fixed inset-0 z-50"
            style={{ backgroundColor: 'rgba(0,0,0,0.75)', backdropFilter: 'blur(6px)' }}
            onClick={onClose}
          />

          {/* Panel */}
          <motion.div
            key="panel"
            initial={{ opacity: 0, scale: 0.96, y: 24 }}
            animate={{ opacity: 1, scale: 1, y: 0 }}
            exit={{ opacity: 0, scale: 0.96, y: 24 }}
            transition={{ duration: 0.2, ease: 'easeOut' }}
            className="fixed inset-0 z-50 flex items-center justify-center p-4 pointer-events-none"
          >
            <div
              className="w-full max-w-xl bg-[#0d1017] border border-white/[0.08] rounded-2xl shadow-2xl flex flex-col pointer-events-auto overflow-hidden"
              style={{ maxHeight: '88vh' }}
              onClick={e => e.stopPropagation()}
            >

              {/* ── Header ──────────────────────────────────────────────────── */}
              <div className="flex items-center gap-3 px-5 pt-5 pb-4 border-b border-white/[0.06]">
                <div className="w-9 h-9 rounded-xl bg-brand-500/15 flex items-center justify-center flex-shrink-0">
                  <BookUser className="w-4 h-4 text-brand-400" />
                </div>
                <div className="flex-1 min-w-0">
                  <h2 className="text-white font-semibold text-sm">Selecionar da Agenda</h2>
                  <p className="text-slate-500 text-xs mt-0.5 truncate">
                    {total > 0 ? `${total} contatos sincronizados` : 'Contatos do WhatsApp'}
                  </p>
                </div>
                <button
                  onClick={onClose}
                  className="p-1.5 rounded-lg hover:bg-white/[0.06] text-slate-500 hover:text-white transition-colors"
                >
                  <X className="w-4 h-4" />
                </button>
              </div>

              {/* ── Search ──────────────────────────────────────────────────── */}
              <div className="px-5 py-3 border-b border-white/[0.04] space-y-2.5">
                <div className="relative">
                  <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-500 pointer-events-none" />
                  <input
                    ref={searchRef}
                    value={search}
                    onChange={e => setSearch(e.target.value)}
                    placeholder="Buscar por nome ou número…"
                    className="w-full bg-white/[0.05] border border-white/[0.08] rounded-xl pl-9 pr-10 py-2.5 text-sm text-white placeholder-slate-500 focus:outline-none focus:border-brand-500/40 focus:ring-1 focus:ring-brand-500/25 transition-all"
                  />
                  {search && (
                    <button
                      onClick={() => setSearch('')}
                      className="absolute right-3 top-1/2 -translate-y-1/2 p-0.5 rounded-md hover:bg-white/10 text-slate-500 hover:text-white transition-colors"
                    >
                      <X className="w-3.5 h-3.5" />
                    </button>
                  )}
                </div>

                {/* Toolbar */}
                <div className="flex items-center justify-between">
                  <button
                    onClick={toggleAll}
                    disabled={contacts.length === 0}
                    className="flex items-center gap-2 text-xs text-slate-400 hover:text-white transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
                  >
                    <div className={`w-4 h-4 rounded border-2 flex items-center justify-center transition-all flex-shrink-0 ${
                      allVisible
                        ? 'bg-brand-500 border-brand-500'
                        : 'border-white/25'
                    }`}>
                      {allVisible && <Check className="w-2.5 h-2.5 text-white" strokeWidth={3} />}
                    </div>
                    {allVisible ? 'Desmarcar página' : 'Selecionar página'}
                  </button>

                  <div className="flex items-center gap-3 text-xs text-slate-500">
                    {selected.size > 0 && (
                      <button
                        onClick={() => setSelected(new Set())}
                        className="text-red-400/70 hover:text-red-400 transition-colors"
                      >
                        Limpar seleção
                      </button>
                    )}
                    <span>{contacts.length} exibidos</span>
                  </div>
                </div>
              </div>

              {/* ── List ────────────────────────────────────────────────────── */}
              <div className="flex-1 overflow-y-auto min-h-0">
                {loading && contacts.length === 0 ? (
                  <div className="flex flex-col items-center justify-center py-20 gap-3">
                    <Loader2 className="w-6 h-6 text-brand-400 animate-spin" />
                    <p className="text-slate-500 text-xs">Carregando contatos…</p>
                  </div>

                ) : contacts.length === 0 ? (
                  <div className="flex flex-col items-center justify-center py-20 gap-3 text-center px-8">
                    <Users className="w-10 h-10 text-slate-700" />
                    <p className="text-slate-400 text-sm font-medium">Nenhum contato encontrado</p>
                    <p className="text-slate-600 text-xs">
                      {search
                        ? 'Tente outro nome ou número.'
                        : 'Os contatos são sincronizados automaticamente quando o WhatsApp está conectado.'}
                    </p>
                  </div>

                ) : (
                  <>
                    {contacts.map((c) => {
                      const phone     = getPhone(c)
                      const name      = resolveDisplayName(c)
                      const sel       = selected.has(phone)
                      const avatarUrl = resolveAvatarUrl(c, instanceId)

                      return (
                        <button
                          key={c.jid}
                          type="button"
                          onClick={() => toggle(c)}
                          className={`w-full flex items-center gap-3 px-5 py-2.5 text-left transition-colors border-b border-white/[0.03] last:border-0 ${
                            sel
                              ? 'bg-brand-500/[0.08] hover:bg-brand-500/[0.12]'
                              : 'hover:bg-white/[0.04]'
                          }`}
                        >
                          {/* Checkbox */}
                          <div className={`w-5 h-5 rounded-md border-2 flex items-center justify-center flex-shrink-0 transition-all ${
                            sel ? 'bg-brand-500 border-brand-500' : 'border-white/20'
                          }`}>
                            {sel && <Check className="w-3 h-3 text-white" strokeWidth={3} />}
                          </div>

                          {/* Avatar — proxy local quando o banco indica que existe foto */}
                          <RowAvatar url={avatarUrl} name={name} jid={c.jid} />

                          {/* Info */}
                          <div className="flex-1 min-w-0">
                            <p className={`text-sm font-medium truncate transition-colors ${sel ? 'text-brand-300' : 'text-white'}`}>
                              {name}
                            </p>
                            <p className="text-slate-500 text-xs truncate">
                              {formatPhone(phone, c.jid)}
                            </p>
                          </div>

                          {/* Selected indicator */}
                          {sel && (
                            <UserCheck className="w-4 h-4 text-brand-400 flex-shrink-0" />
                          )}
                        </button>
                      )
                    })}

                    {/* Load more */}
                    {hasMore && (
                      <div className="flex justify-center py-4">
                        <button
                          onClick={() => fetchContacts(search, offset)}
                          disabled={loading}
                          className="flex items-center gap-1.5 text-xs text-brand-400 hover:text-brand-300 transition-colors disabled:opacity-50"
                        >
                          {loading
                            ? <Loader2 className="w-3.5 h-3.5 animate-spin" />
                            : <ChevronDown className="w-3.5 h-3.5" />
                          }
                          Carregar mais contatos
                        </button>
                      </div>
                    )}
                  </>
                )}
              </div>

              {/* ── Footer ──────────────────────────────────────────────────── */}
              <div className="flex items-center justify-between gap-4 px-5 py-4 border-t border-white/[0.06]">
                {selected.size > 0 ? (
                  <p className="text-sm text-white">
                    <span className="font-bold text-brand-400">{selected.size}</span>
                    <span className="text-slate-400">
                      {' '}{selected.size === 1 ? 'contato selecionado' : 'contatos selecionados'}
                    </span>
                  </p>
                ) : (
                  <p className="text-xs text-slate-600">Toque em um contato para selecioná-lo</p>
                )}

                <div className="flex items-center gap-2">
                  <button
                    onClick={onClose}
                    className="px-4 py-2 rounded-xl text-sm text-slate-400 hover:text-white hover:bg-white/[0.05] transition-colors"
                  >
                    Cancelar
                  </button>
                  <button
                    onClick={handleConfirm}
                    disabled={selected.size === 0}
                    className="inline-flex items-center gap-1.5 px-5 py-2 rounded-xl text-sm font-semibold bg-brand-500 hover:bg-brand-600 active:scale-[0.98] text-white transition-all disabled:opacity-35 disabled:cursor-not-allowed"
                  >
                    <Users className="w-3.5 h-3.5" />
                    Adicionar{selected.size > 0 ? ` ${selected.size}` : ''}
                  </button>
                </div>
              </div>

            </div>
          </motion.div>
        </>
      )}
    </AnimatePresence>
  )
}
