import { NextResponse } from "next/server";
import webpush from "web-push";
import { createAdminClient } from "@/lib/supabase-server";

/**
 * Lembrete de compromisso que esta perto de comecar.
 *
 * Irmao de `push/enviar`, mas com outro ritmo e outro proposito. Aquele e o
 * resumo do dia, sai uma vez de manha e diz o que esta pendente. Este roda de
 * poucos em poucos minutos e avisa na antecedencia que a pessoa escolheu
 * naquele compromisso — quem esta no meio de um servico as 13h50 nao se lembra
 * da reuniao das 14h por ter lido alguma coisa as 7h.
 *
 * Marca o que enviou antes de mandar. A ordem importa: se marcasse depois e o
 * envio demorasse, a rodada seguinte pegaria o mesmo lembrete e a pessoa
 * receberia duas vezes. Melhor perder um aviso numa falha rara do que repetir
 * — repeticao e o que faz desligar a notificacao.
 */

export const dynamic = "force-dynamic";
export const maxDuration = 60;

type Lembrete = {
  company_id: string;
  event_id: string;
  ocorrencia: string;
  titulo: string;
  cliente: string | null;
  quando: string;
  minutos_ate: number;
  responsavel_id: string | null;
};

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;
}

/* "em 5 minutos", "em 1 hora", "amanha". O numero cru em minutos obrigaria a
   pessoa a fazer a conta justamente quando ela esta com pressa. */
function faltando(minutos: number) {
  if (minutos <= 0) return "agora";
  if (minutos < 60) return `em ${minutos} min`;
  if (minutos < 120) return "em 1 hora";
  if (minutos < 1440) return `em ${Math.round(minutos / 60)} horas`;
  const dias = Math.round(minutos / 1440);
  return dias === 1 ? "amanhã" : `em ${dias} dias`;
}

function horaDe(iso: string) {
  return new Date(iso).toLocaleTimeString("pt-BR", {
    hour: "2-digit",
    minute: "2-digit",
    timeZone: "America/Sao_Paulo",
  });
}

export async function POST(req: Request) {
  const segredo = process.env.AGENDA_PUSH_SECRET;
  if (!segredo) {
    return NextResponse.json({ error: "AGENDA_PUSH_SECRET nao configurado." }, { status: 500 });
  }
  if (req.headers.get("x-agenda-secret") !== segredo) {
    return NextResponse.json({ error: "Nao autorizado." }, { status: 401 });
  }
  if (!configurar()) {
    return NextResponse.json({ error: "Chaves VAPID nao configuradas." }, { status: 500 });
  }

  const admin = createAdminClient();

  const { data: devidos, error } = await admin.rpc("get_agenda_lembretes_devidos");
  if (error) {
    return NextResponse.json({ error: error.message }, { status: 500 });
  }

  const lembretes = (devidos as Lembrete[]) ?? [];
  if (!lembretes.length) {
    return NextResponse.json({ ok: true, lembretes: 0, enviados: 0 });
  }

  /* Uma consulta de assinaturas para todas as empresas envolvidas, em vez de
     uma por lembrete: numa manha cheia seriam dezenas de idas ao banco para
     buscar sempre a mesma coisa. */
  const empresas = [...new Set(lembretes.map((l) => l.company_id))];
  const { data: assinaturas } = await admin
    .from("push_subscriptions")
    .select("id,user_id,company_id,endpoint,p256dh,auth,failures")
    .in("company_id", empresas)
    .lt("failures", 5)
    .limit(2000);

  const porEmpresa = new Map<string, NonNullable<typeof assinaturas>>();
  const porPessoa = new Map<string, NonNullable<typeof assinaturas>>();
  for (const a of assinaturas ?? []) {
    const empresa = String(a.company_id);
    if (!porEmpresa.has(empresa)) porEmpresa.set(empresa, []);
    porEmpresa.get(empresa)!.push(a);

    const pessoa = String(a.user_id ?? "");
    if (!pessoa) continue;
    if (!porPessoa.has(pessoa)) porPessoa.set(pessoa, []);
    porPessoa.get(pessoa)!.push(a);
  }

  /* Toca no celular de quem vai ao compromisso, e nao no de todo mundo. Com
     tres pessoas na empresa, o aviso de "sua reuniao comeca em 15 min"
     chegando para as tres e o caminho curto para alguem desligar as
     notificacoes — e perder junto as que importam.

     Sem responsavel, ou com um responsavel que nunca registrou aparelho, cai
     para a empresa inteira: melhor um aviso a mais do que o compromisso passar
     sem ninguem ser avisado. */
  const destinatarios = (l: Lembrete) => {
    const doResponsavel = l.responsavel_id ? porPessoa.get(l.responsavel_id) : undefined;
    if (doResponsavel?.length) return doResponsavel;
    return porEmpresa.get(l.company_id) ?? [];
  };

  let enviados = 0;
  let removidos = 0;
  let semAparelho = 0;

  for (const l of lembretes) {
    const aparelhos = destinatarios(l);

    /* Sem aparelho registrado nao ha o que enviar — e tambem nao ha por que
       marcar como enviado: se a pessoa registrar o celular daqui a pouco,
       ainda da tempo de receber. */
    if (!aparelhos.length) {
      semAparelho += 1;
      continue;
    }

    const { error: jaMarcado } = await admin
      .from("agenda_reminders_sent")
      .insert({ event_id: l.event_id, ocorrencia: l.ocorrencia });

    /* Violacao de unicidade: outra rodada pegou este mesmo lembrete primeiro. */
    if (jaMarcado) continue;

    const titulo = `${l.titulo} ${faltando(l.minutos_ate)}`;
    const corpo = l.cliente
      ? `${horaDe(l.quando)} · ${l.cliente}`
      : `Começa às ${horaDe(l.quando)}`;

    for (const a of aparelhos) {
      try {
        await webpush.sendNotification(
          { endpoint: String(a.endpoint), keys: { p256dh: String(a.p256dh), auth: String(a.auth) } },
          JSON.stringify({
            titulo,
            corpo,
            rota: "/dashboard/agenda",
            /* Tag por ocorrencia: dois compromissos proximos aparecem como dois
               avisos, e nao um sobrescrevendo o outro. */
            tag: `lembrete-${l.event_id}-${l.ocorrencia}`,
          })
        );
        enviados += 1;
        await admin
          .from("push_subscriptions")
          .update({ last_sent_at: new Date().toISOString(), failures: 0 })
          .eq("id", a.id);
      } catch (erro) {
        const status = (erro as { statusCode?: number })?.statusCode;
        if (status === 404 || status === 410) {
          await admin.from("push_subscriptions").delete().eq("id", a.id);
          removidos += 1;
        } else {
          await admin
            .from("push_subscriptions")
            .update({ failures: Number(a.failures ?? 0) + 1 })
            .eq("id", a.id);
        }
      }
    }
  }

  return NextResponse.json({
    ok: true,
    lembretes: lembretes.length,
    enviados,
    removidos,
    semAparelho,
  });
}
