import { NextResponse } from "next/server";
import { acessoDoChat } from "@/lib/chats/acesso";
import { avisarConversa, canalDaConversa } from "@/lib/chats/aviso";
import { createAdminClient } from "@/lib/supabase-server";

/*
  Quem esta na loja agora.

  E o movimento da vitrine que a loja online nunca teve. Cada linha e uma pessoa
  com uma aba aberta: ha quanto tempo esta, olhando qual produto (com a foto,
  para a lista parecer a vitrine e nao um relatorio), o que ja separou no
  carrinho — e se ja falou com a loja alguma vez.

  Junto vem a leitura mais util: o quanto aquela visita esta perto de virar
  venda, e por que. Um numero de probabilidade seria chute; o motivo em palavras
  ("carrinho montado e 3 voltas no mesmo produto") a pessoa do balcao consegue
  usar na frase seguinte.

  Presenca e coisa do agora: quem parou de dar sinal ha mais de um minuto e meio
  ja fechou a aba, e sai da lista sozinho.
*/

const JANELA_DE_PRESENCA_MS = 90 * 1000;
const JANELA_DA_ROTA_MS = 24 * 60 * 60 * 1000;

function texto(valor: unknown) {
  return String(valor ?? "").trim();
}

type Sinal = { peso: number; frase: string };

/*
  Termometro do fechamento.

  Sao os sinais que qualquer vendedor de loja de rua le sem pensar: ha quanto
  tempo a pessoa esta ali, se voltou no mesmo produto, se ja esta com a peca na
  mao. Aqui eles viram peso, e o maior deles explica o selo.
*/
function termometro(dados: {
  minutos: number;
  paginas: number;
  repeticoes: number;
  produto: string | null;
  carrinhoItens: number;
  jaFalou: boolean;
  ehCliente: boolean;
}) {
  const sinais: Sinal[] = [];

  if (dados.carrinhoItens > 0) {
    sinais.push({
      peso: 3,
      frase: `${dados.carrinhoItens} ${dados.carrinhoItens === 1 ? "item" : "itens"} no carrinho`,
    });
  }
  if (dados.repeticoes >= 2) {
    sinais.push({
      peso: 2,
      frase: dados.produto ? `${dados.repeticoes} voltas em ${dados.produto}` : `${dados.repeticoes} voltas no mesmo produto`,
    });
  }
  if (dados.minutos >= 5) sinais.push({ peso: 2, frase: `${dados.minutos} min na loja` });
  else if (dados.minutos >= 2) sinais.push({ peso: 1, frase: `${dados.minutos} min na loja` });
  if (dados.paginas >= 6) sinais.push({ peso: 1, frase: `${dados.paginas} páginas vistas` });
  if (dados.jaFalou) sinais.push({ peso: 1, frase: "já conversou com a loja" });
  if (dados.ehCliente) sinais.push({ peso: 1, frase: "já é cliente" });

  const soma = sinais.reduce((total, sinal) => total + sinal.peso, 0);
  const temperatura = soma >= 4 ? "quente" : soma >= 2 ? "morno" : "frio";

  const motivo = sinais
    .sort((a, b) => b.peso - a.peso)
    .slice(0, 3)
    .map((sinal) => sinal.frase)
    .join(" · ");

  return { temperatura, motivo: motivo || "acabou de chegar" };
}

export async function GET() {
  const acesso = await acessoDoChat();
  if ("erro" in acesso) return acesso.erro;
  if (!acesso.podeVerLoja) return NextResponse.json({ ok: true, visitantes: [] });

  const admin = createAdminClient();
  const desde = new Date(Date.now() - JANELA_DE_PRESENCA_MS).toISOString();

  const { data, error } = await admin
    .from("store_visitor_pings")
    .select(
      "visitor_key,page_path,page_title,product_id,product_name,product_price,product_image,visitor_name,thread_id,client_id,referrer,cart_items,cart_total,first_seen_at,last_seen_at,pings"
    )
    .in("user_id", acesso.tenantIds)
    .gte("last_seen_at", desde)
    .order("last_seen_at", { ascending: false })
    .limit(60);

  if (error) return NextResponse.json({ error: error.message }, { status: 400 });

  const linhas = (data || []) as {
    visitor_key: string;
    page_path: string | null;
    page_title: string | null;
    product_id: string | null;
    product_name: string | null;
    product_price: number | null;
    product_image: string | null;
    visitor_name: string | null;
    thread_id: string | null;
    client_id: string | null;
    referrer: string | null;
    cart_items: number | null;
    cart_total: number | null;
    first_seen_at: string;
    last_seen_at: string;
    pings: number | null;
  }[];

  const chaves = linhas.map((linha) => linha.visitor_key);

  /* Nome de quem ja se apresentou em alguma conversa: melhor "Marcos olhando
     canecas" do que "visitante 3f2a". */
  const nomes = new Map<string, { nome: string | null; threadId: string }>();
  if (chaves.length) {
    const { data: conversas } = await admin
      .from("chat_threads")
      .select("id,visitor_key,visitor_name")
      .eq("kind", "visitor")
      .in("user_id", acesso.tenantIds)
      .in("visitor_key", chaves);

    ((conversas || []) as { id: string; visitor_key: string; visitor_name: string | null }[]).forEach((conversa) => {
      nomes.set(conversa.visitor_key, { nome: conversa.visitor_name, threadId: conversa.id });
    });
  }

  /* Quantas vezes cada pessoa voltou ao mesmo produto no ultimo dia: quem volta
     tres vezes na mesma caneca esta decidindo, nao passeando. */
  const repeticoes = new Map<string, number>();
  if (chaves.length) {
    const { data: passos } = await admin
      .from("store_visitor_events")
      .select("visitor_key,product_id")
      .eq("kind", "produto")
      .in("user_id", acesso.tenantIds)
      .in("visitor_key", chaves)
      .gte("created_at", new Date(Date.now() - JANELA_DA_ROTA_MS).toISOString())
      .limit(600);

    const contagem = new Map<string, number>();
    ((passos || []) as { visitor_key: string; product_id: string | null }[]).forEach((passo) => {
      if (!passo.product_id) return;
      const marca = `${passo.visitor_key}:${passo.product_id}`;
      contagem.set(marca, (contagem.get(marca) || 0) + 1);
    });

    linhas.forEach((linha) => {
      if (!linha.product_id) return;
      repeticoes.set(linha.visitor_key, contagem.get(`${linha.visitor_key}:${linha.product_id}`) || 0);
    });
  }

  /* A foto vem do proprio cadastro do produto: e a mesma que a loja publicou, e
     nao o que sobrou na tela do visitante. */
  const fotos = new Map<string, string>();
  const produtos = [...new Set(linhas.map((linha) => linha.product_id).filter(Boolean))] as string[];
  if (produtos.length) {
    const { data: cadastro } = await admin.from("products").select("id,image_url").in("id", produtos);
    ((cadastro || []) as { id: string; image_url: string | null }[]).forEach((produto) => {
      if (produto.image_url) fotos.set(produto.id, produto.image_url);
    });
  }

  const visitantes = linhas.map((linha) => {
    const conhecido = nomes.get(linha.visitor_key);
    const minutos = Math.max(1, Math.round((Date.now() - new Date(linha.first_seen_at).getTime()) / 60000));
    const carrinhoItens = Number(linha.cart_items || 0);
    const voltas = repeticoes.get(linha.visitor_key) || 0;

    const leitura = termometro({
      minutos,
      paginas: Number(linha.pings || 1),
      repeticoes: voltas,
      produto: linha.product_name,
      carrinhoItens,
      jaFalou: Boolean(conhecido?.threadId || linha.thread_id),
      ehCliente: Boolean(linha.client_id),
    });

    return {
      chave: linha.visitor_key,
      nome: texto(conhecido?.nome) || texto(linha.visitor_name) || null,
      pagina: linha.page_title || linha.page_path || "Loja",
      caminho: linha.page_path,
      produtoId: linha.product_id,
      produto: linha.product_name,
      preco: linha.product_price,
      imagem: (linha.product_id && fotos.get(linha.product_id)) || linha.product_image || null,
      threadId: conhecido?.threadId || linha.thread_id || null,
      clienteId: linha.client_id,
      chegouPor: linha.referrer,
      carrinhoItens,
      carrinhoTotal: Number(linha.cart_total || 0),
      voltasNoProduto: voltas,
      minutosNaLoja: minutos,
      paginasVistas: Number(linha.pings || 1),
      temperatura: leitura.temperatura,
      motivo: leitura.motivo,
      desde: linha.first_seen_at,
      visto: linha.last_seen_at,
    };
  });

  /*
    O placar do dia.

    Tres numeros, e so: quantos entraram, quantos falaram, quantos compraram. E
    a conta que diz se o atendimento da loja online esta valendo a pena.
  */
  const inicioDoDia = new Date();
  inicioDoDia.setHours(0, 0, 0, 0);
  const abertura = inicioDoDia.toISOString();

  const [entraram, falaram, compraram] = await Promise.all([
    admin
      .from("store_visitor_pings")
      .select("visitor_key", { count: "exact", head: true })
      .in("user_id", acesso.tenantIds)
      .gte("first_seen_at", abertura),
    admin
      .from("chat_threads")
      .select("id", { count: "exact", head: true })
      .eq("kind", "visitor")
      .in("user_id", acesso.tenantIds)
      .gte("created_at", abertura),
    admin
      .from("store_visitor_events")
      .select("id", { count: "exact", head: true })
      .eq("kind", "compra")
      .in("user_id", acesso.tenantIds)
      .gte("created_at", abertura),
  ]);

  return NextResponse.json({
    ok: true,
    visitantes,
    dia: {
      entraram: entraram.count || 0,
      falaram: falaram.count || 0,
      compraram: compraram.count || 0,
    },
  });
}

/*
  Apagar tudo o que se sabe de um visitante.

  A lei dá a ele esse direito, e o pedido chega em português, pelo próprio chat:
  "apaga meus dados". Sem um botão, alguém teria que sair caçando linha por
  linha em três tabelas — e é assim que sobra rastro.

  Some a navegação, some a presença e some a conversa inteira. Não é
  arquivamento: é apagar mesmo.
*/
export async function DELETE(request: Request) {
  const acesso = await acessoDoChat();
  if ("erro" in acesso) return acesso.erro;
  if (!acesso.podeConfigurar) {
    return NextResponse.json({ error: "Só quem configura o atendimento pode apagar dados." }, { status: 403 });
  }

  const endereco = new URL(request.url);
  const chave = texto(endereco.searchParams.get("chave"));
  if (!chave) return NextResponse.json({ error: "Visitante invalido." }, { status: 400 });

  const admin = createAdminClient();

  const { data: conversas } = await admin
    .from("chat_threads")
    .select("id")
    .eq("kind", "visitor")
    .eq("visitor_key", chave)
    .in("user_id", acesso.tenantIds);

  const ids = ((conversas || []) as { id: string }[]).map((conversa) => conversa.id);

  if (ids.length) {
    /* As mensagens saem por cascata da conversa, mas os anexos ficariam órfãos
       no armazenamento — por isso a conversa é apagada por último, depois de
       limpar o que aponta para ela. */
    await admin.from("chat_messages").delete().in("thread_id", ids);
    await admin.from("chat_threads").delete().in("id", ids);
  }

  await admin.from("store_visitor_events").delete().eq("visitor_key", chave).in("user_id", acesso.tenantIds);
  await admin.from("store_visitor_pings").delete().eq("visitor_key", chave).in("user_id", acesso.tenantIds);

  return NextResponse.json({ ok: true, conversasApagadas: ids.length });
}

/*
  Puxar conversa com quem esta olhando.

  A loja escolhe a pessoa na lista e manda a primeira mensagem. Do lado dela o
  balao acende sozinho, com o aviso de mensagem nova — e a conversa passa a
  viver na aba Loja Online, como qualquer outra.
*/
export async function POST(request: Request) {
  const acesso = await acessoDoChat();
  if ("erro" in acesso) return acesso.erro;
  if (!acesso.podeResponderLoja) return NextResponse.json({ error: "Sem permissao." }, { status: 403 });

  const corpo = (await request.json().catch(() => null)) as { chave?: unknown; mensagem?: unknown } | null;
  const chave = texto(corpo?.chave);
  const mensagem = texto(corpo?.mensagem).slice(0, 2000);
  if (!chave) return NextResponse.json({ error: "Visitante invalido." }, { status: 400 });
  if (!mensagem) return NextResponse.json({ error: "Escreva a mensagem." }, { status: 400 });

  const admin = createAdminClient();
  const { data: presenca } = await admin
    .from("store_visitor_pings")
    .select("user_id,visitor_name,product_name")
    .eq("visitor_key", chave)
    .in("user_id", acesso.tenantIds)
    .maybeSingle();

  const lojaId = (presenca as { user_id?: string } | null)?.user_id;
  if (!lojaId) return NextResponse.json({ error: "Visitante nao esta mais na loja." }, { status: 404 });

  const agora = new Date().toISOString();
  const nome = texto((presenca as { visitor_name?: string } | null)?.visitor_name) || "Visitante";

  const { data: existente } = await admin
    .from("chat_threads")
    .select("id")
    .eq("kind", "visitor")
    .eq("user_id", lojaId)
    .eq("visitor_key", chave)
    .maybeSingle();

  let threadId = (existente as { id?: string } | null)?.id || null;

  if (!threadId) {
    const { data: criada, error } = await admin
      .from("chat_threads")
      .insert({
        user_id: lojaId,
        kind: "visitor",
        visitor_key: chave,
        visitor_name: nome,
        title: nome,
        stage: "atendendo",
        first_seen_at: agora,
        last_seen_at: agora,
      })
      .select("id")
      .single();
    if (error || !criada) return NextResponse.json({ error: error?.message || "Falha ao abrir." }, { status: 400 });
    threadId = (criada as { id: string }).id;
  }

  await admin.from("chat_messages").insert({
    thread_id: threadId,
    user_id: lojaId,
    author_type: "store",
    author_id: acesso.user.id,
    author_name: acesso.nome,
    body: mensagem,
    read_by_store_at: agora,
  });

  await admin
    .from("chat_threads")
    .update({
      last_message_at: agora,
      last_message_preview: mensagem.slice(0, 120),
      last_message_author: "store",
      updated_at: agora,
      closed_at: null,
    })
    .eq("id", threadId);

  await admin.from("store_visitor_pings").update({ thread_id: threadId }).eq("visitor_key", chave).eq("user_id", lojaId);

  /* A abordagem tambem e um passo da rota: e o momento em que a loja entrou na
     historia daquela visita. */
  await admin.from("store_visitor_events").insert({
    user_id: lojaId,
    visitor_key: chave,
    kind: "mensagem",
    detail: "A loja puxou conversa",
    title: mensagem.slice(0, 120),
    created_at: agora,
  });

  await avisarConversa([canalDaConversa(threadId), `visitante-${chave}`], {
    threadId,
    autor: "store",
    autorNome: acesso.nome,
    resumo: mensagem.slice(0, 120),
  });

  return NextResponse.json({ ok: true, threadId });
}
