import type { SupabaseClient } from "@supabase/supabase-js";
import webpush from "web-push";

/*
  Aviso de mensagem no celular do cliente.

  A loja escreve no pedido e o cliente pode estar em qualquer lugar, com o
  portal fechado. Este e o unico caminho que alcanca ele ali.

  Sem chave VAPID, sem aparelho registrado ou com o envio falhando, nada quebra:
  a mensagem ja esta salva e aparece assim que ele abrir o portal.
*/

function configurar() {
  const publica = process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY || process.env.VAPID_PUBLIC_KEY;
  const privada = process.env.VAPID_PRIVATE_KEY;
  const contato = process.env.VAPID_SUBJECT || "mailto:contato@ktalogu.com.br";
  if (!publica || !privada) return false;
  webpush.setVapidDetails(contato, publica, privada);
  return true;
}

export async function avisarClientePorPush(
  admin: SupabaseClient,
  params: {
    clientId?: string | null;
    tenantId?: string | null;
    titulo: string;
    corpo: string;
    rota: string;
    tag?: string;
  }
) {
  if (!params.clientId || !configurar()) return { enviados: 0 };

  const icone = await iconeDaLoja(admin, params.tenantId);

  const { data: aparelhos } = await admin
    .from("client_push_subscriptions")
    .select("id,endpoint,p256dh,auth,failures")
    .eq("client_id", params.clientId)
    .lt("failures", 5)
    .limit(20);

  let enviados = 0;
  for (const aparelho of aparelhos ?? []) {
    try {
      await webpush.sendNotification(
        {
          endpoint: String(aparelho.endpoint),
          keys: { p256dh: String(aparelho.p256dh), auth: String(aparelho.auth) },
        },
        JSON.stringify({
          titulo: params.titulo,
          corpo: params.corpo,
          rota: params.rota,
          tag: params.tag || "ktalogu-conversa",
          icone,
        })
      );
      enviados += 1;
      await admin
        .from("client_push_subscriptions")
        .update({ last_sent_at: new Date().toISOString(), failures: 0 })
        .eq("id", aparelho.id);
    } catch (erro) {
      const status = (erro as { statusCode?: number })?.statusCode;
      /* Aparelho que sumiu (trocou de celular, limpou o navegador) sai da
         lista: insistir nele so gera fila de erro. */
      if (status === 404 || status === 410) {
        await admin.from("client_push_subscriptions").delete().eq("id", aparelho.id);
      } else {
        await admin
          .from("client_push_subscriptions")
          .update({ failures: Number(aparelho.failures ?? 0) + 1 })
          .eq("id", aparelho.id);
      }
    }
  }

  return { enviados };
}

/* O endereco do portal muda com a loja: sem o slug o aviso levaria a lugar
   nenhum. */
export async function enderecoDoPortal(admin: SupabaseClient, tenantId: string, orderId: string) {
  const { data } = await admin.from("profiles").select("slug").eq("id", tenantId).maybeSingle();
  const slug = String((data as { slug?: string } | null)?.slug || "").trim();
  if (!slug) return null;
  /* A ancora leva direto ao bloco da conversa: o pedido tem tela longa e o
     aviso prometeu a mensagem, nao o cabecalho. */
  return `/${slug}/cliente/pedido/${orderId}#conversa-do-pedido`;
}

/*
  Aviso no aparelho do visitante da loja.

  Ele nao tem conta: a assinatura esta presa a conversa. Quando a loja responde,
  e este o unico caminho que alcanca quem ja fechou a aba da loja.
*/
export async function avisarVisitantePorPush(
  admin: SupabaseClient,
  params: { threadId: string; tenantId?: string | null; titulo: string; corpo: string; rota: string; tag?: string }
) {
  if (!configurar()) return { enviados: 0 };

  const icone = await iconeDaLoja(admin, params.tenantId);

  const { data: aparelhos } = await admin
    .from("visitor_push_subscriptions")
    .select("id,endpoint,p256dh,auth,failures")
    .eq("thread_id", params.threadId)
    .lt("failures", 5)
    .limit(10);

  let enviados = 0;
  for (const aparelho of aparelhos ?? []) {
    try {
      await webpush.sendNotification(
        {
          endpoint: String(aparelho.endpoint),
          keys: { p256dh: String(aparelho.p256dh), auth: String(aparelho.auth) },
        },
        JSON.stringify({
          titulo: params.titulo,
          corpo: params.corpo,
          rota: params.rota,
          tag: params.tag || "ktalogu-loja",
          icone,
        })
      );
      enviados += 1;
      await admin
        .from("visitor_push_subscriptions")
        .update({ last_sent_at: new Date().toISOString(), failures: 0 })
        .eq("id", aparelho.id);
    } catch (erro) {
      const status = (erro as { statusCode?: number })?.statusCode;
      if (status === 404 || status === 410) {
        await admin.from("visitor_push_subscriptions").delete().eq("id", aparelho.id);
      } else {
        await admin
          .from("visitor_push_subscriptions")
          .update({ failures: Number(aparelho.failures ?? 0) + 1 })
          .eq("id", aparelho.id);
      }
    }
  }

  return { enviados };
}

/* Endereco da loja com o chat ja aberto: o aviso prometeu a resposta, e nao a
   vitrine. */
export async function enderecoDaLoja(admin: SupabaseClient, tenantId: string) {
  const { data } = await admin.from("profiles").select("slug").eq("id", tenantId).maybeSingle();
  const slug = String((data as { slug?: string } | null)?.slug || "").trim();
  return slug ? `/${slug}?chat=1` : null;
}

/*
  O icone do aviso e o da loja, nao o do Ktalogu.

  Quem recebe no celular ve primeiro a figurinha. Se todas as lojas mandarem o
  mesmo simbolo, o dono de duas lojas nao sabe qual delas vendeu — e o cliente
  nao reconhece de quem e o recado. Sem favicon cadastrado, cai no simbolo do
  sistema, que e melhor do que quadrado vazio.
*/
const iconesPorLoja = new Map<string, { valor: string | null; em: number }>();
const VALIDADE_DO_ICONE_MS = 5 * 60 * 1000;

export async function iconeDaLoja(admin: SupabaseClient, tenantId?: string | null) {
  if (!tenantId) return null;

  const guardado = iconesPorLoja.get(tenantId);
  if (guardado && Date.now() - guardado.em < VALIDADE_DO_ICONE_MS) return guardado.valor;

  const { data } = await admin
    .from("profiles")
    .select("catalog_favicon_url,catalog_logo_url,logo_url")
    .eq("id", tenantId)
    .maybeSingle();

  const perfil = data as
    | { catalog_favicon_url?: string | null; catalog_logo_url?: string | null; logo_url?: string | null }
    | null;

  const escolhido =
    [perfil?.catalog_favicon_url, perfil?.catalog_logo_url, perfil?.logo_url]
      .map((valor) => String(valor || "").trim())
      .find((valor) => valor.startsWith("http")) || null;

  iconesPorLoja.set(tenantId, { valor: escolhido, em: Date.now() });
  return escolhido;
}

/*
  Venda aprovada.

  E o aviso que a loja mais quer receber, e o que ela precisa saber cabe em duas
  linhas: como foi paga e quanto entrou. O resto e detalhe que espera a pessoa
  abrir o sistema.
*/
export async function avisarVendaAprovada(
  admin: SupabaseClient,
  params: {
    tenantId?: string | null;
    valor: number;
    metodo?: string | null;
    numero?: number | string | null;
    cliente?: string | null;
    orderId?: string | null;
    origem?: "sistema" | "loja";
  }
) {
  if (!params.tenantId || !configurar()) return { enviados: 0 };

  const dinheiro = new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(
    Number(params.valor || 0)
  );
  const comoFoiPaga = rotuloDoMeio(params.metodo);
  const titulo = comoFoiPaga ? `Venda aprovada no ${comoFoiPaga}` : "Venda aprovada";
  const detalhe = [
    params.numero ? `Pedido #${params.numero}` : null,
    params.cliente || null,
    params.origem === "loja" ? "Loja online" : null,
  ]
    .filter(Boolean)
    .join(" · ");

  const icone = await iconeDaLoja(admin, params.tenantId);

  const { data: aparelhos } = await admin
    .from("push_subscriptions")
    .select("id,endpoint,p256dh,auth,failures")
    .eq("company_id", params.tenantId)
    .lt("failures", 5)
    .limit(50);

  let enviados = 0;
  for (const aparelho of aparelhos ?? []) {
    try {
      await webpush.sendNotification(
        {
          endpoint: String(aparelho.endpoint),
          keys: { p256dh: String(aparelho.p256dh), auth: String(aparelho.auth) },
        },
        JSON.stringify({
          titulo,
          corpo: detalhe ? `${dinheiro}
${detalhe}` : dinheiro,
          rota: params.orderId ? `/dashboard/orders/${params.orderId}` : "/dashboard",
          tag: `venda-${params.orderId || Date.now()}`,
          icone,
        })
      );
      enviados += 1;
      await admin
        .from("push_subscriptions")
        .update({ last_sent_at: new Date().toISOString(), failures: 0 })
        .eq("id", aparelho.id);
    } catch (erro) {
      const status = (erro as { statusCode?: number })?.statusCode;
      if (status === 404 || status === 410) {
        await admin.from("push_subscriptions").delete().eq("id", aparelho.id);
      } else {
        await admin
          .from("push_subscriptions")
          .update({ failures: Number(aparelho.failures ?? 0) + 1 })
          .eq("id", aparelho.id);
      }
    }
  }

  return { enviados };
}

/* Pix, cartao, dinheiro: o nome que a pessoa usa no balcao. */
export function rotuloDoMeio(metodo?: string | null) {
  const chave = String(metodo || "").toLowerCase();
  if (!chave) return "";
  if (chave.includes("pix") || chave === "bank_transfer") return "Pix";
  if (chave.includes("credit") || chave === "cartao_credito" || chave === "credit_card") return "cartão de crédito";
  if (chave.includes("debit") || chave === "cartao_debito" || chave === "debit_card") return "cartão de débito";
  if (chave.includes("card") || chave.includes("cartao")) return "cartão";
  if (chave.includes("boleto") || chave.includes("ticket")) return "boleto";
  if (chave.includes("cash") || chave.includes("dinheiro")) return "dinheiro";
  if (chave.includes("transfer")) return "transferência";
  return "";
}
