import { getFirestore, type Firestore } from 'firebase-admin/firestore';
import { logger } from 'firebase-functions';
import { onSchedule } from 'firebase-functions/v2/scheduler';

/**
 * Lembrete de prazo — **portado** de `lembretes.service.ts` (`PP-8`).
 *
 * Lembra **apenas** quem ainda não palpitou nos jogos que estão fechando. O PRD é
 * explícito sobre o porquê: se o lembrete de prazo vier junto com aviso de ranking
 * e comentário, o participante casual desliga tudo e volta a perder rodada. Este
 * job olha prazo, e nada mais.
 *
 * **O push não está aqui.** O aviso é gravado no Firestore, que é o canal que
 * sempre existe: `PP-8-08` pede exatamente isso — sem push, o lembrete continua
 * legível no app. Entregar por FCM é `MG-6 BE-4`, depende de app registrado no
 * projeto real, e **não muda quem é lembrado**. Separar os dois é o que permite
 * ter a regra verificada antes de ter a credencial.
 */

/** Quantos minutos antes do apito o lembrete dispara. */
export const MINUTOS_ANTES = Number(process.env.LEMBRETE_MINUTOS_ANTES ?? 120);

export interface ResultadoLembretes {
  jogosNaJanela: number;
  lembretesGerados: number;
}

export const lembrarPrazoFechando = onSchedule(
  {
    // A cada 30 minutos: a janela é de 2 h, então nenhum jogo entra e sai dela
    // sem ser visto — e o job não precisa acertar o minuto exato de nada.
    schedule: 'every 30 minutes',
    timeZone: 'America/Sao_Paulo',
    region: 'southamerica-east1',
  },
  async () => {
    await lembrar();
  },
);

export async function lembrar(
  db: Firestore = getFirestore(),
  agora: Date = new Date(),
): Promise<ResultadoLembretes> {
  const limite = new Date(agora.getTime() + MINUTOS_ANTES * 60 * 1000);

  // Jogos cujo prazo fecha dentro da janela. A consulta é global porque o
  // calendário é global; o filtro por bolão vem depois, ao cruzar com as rodadas
  // de cada um.
  const jogos = await db
    .collection('jogos')
    .where('kickoffAt', '>', agora)
    .where('kickoffAt', '<=', limite)
    .get();

  if (jogos.empty) return { jogosNaJanela: 0, lembretesGerados: 0 };

  let gerados = 0;
  let naJanela = 0;

  for (const jogo of jogos.docs) {
    const j = jogo.data();

    // Bolões que contêm este jogo: mesmo campeonato, e a rodada dentro do
    // intervalo. Bolão encerrado ou arquivado não lembra ninguém — o prazo dele
    // não existe mais (`PP-8-06`).
    const boloes = await db
      .collection('boloes')
      .where('competitionCode', '==', j.competitionCode)
      .where('closedAt', '==', null)
      .get();

    for (const bolao of boloes.docs) {
      const b = bolao.data();
      if (j.matchday < b.matchdayFrom || j.matchday > b.matchdayTo) continue;
      if (b.archivedAt != null) continue;
      naJanela++;

      const [membros, palpites, lembrados] = await Promise.all([
        db
          .collection(`boloes/${bolao.id}/membros`)
          .where('leftAt', '==', null)
          .get(),
        db
          .collection(`boloes/${bolao.id}/palpites`)
          .where('matchId', '==', jogo.id)
          .select('userId')
          .get(),
        // Marca de "já lembrei desse jogo": sem ela, o job avisaria a mesma
        // pessoa a cada meia hora até o apito, que é o caminho mais curto para
        // ela desligar a notificação (`PP-8-04`).
        db
          .collection(`boloes/${bolao.id}/lembretes`)
          .where('matchId', '==', jogo.id)
          .select('userId')
          .get(),
      ]);

      const jaPalpitou = new Set(palpites.docs.map((d) => d.data().userId));
      const jaLembrado = new Set(lembrados.docs.map((d) => d.data().userId));

      for (const membro of membros.docs) {
        const uid = membro.id;
        if (jaPalpitou.has(uid) || jaLembrado.has(uid)) continue;

        const title = 'Seu palpite está fechando';
        const body =
          `${j.homeTeam} x ${j.awayTeam} começa em breve e você ainda não ` +
          'palpitou. Depois do apito não dá mais.';

        const lote = db.batch();
        lote.set(db.collection(`usuarios/${uid}/avisos`).doc(), {
          kind: 'PRAZO_FECHANDO',
          poolId: bolao.id,
          matchId: jogo.id,
          title,
          body,
          createdAt: new Date(),
          readAt: null,
        });
        // Aviso e marca na **mesma** escrita: se só a marca gravasse, a pessoa
        // nunca seria lembrada; se só o aviso, seria lembrada para sempre.
        lote.set(db.doc(`boloes/${bolao.id}/lembretes/${jogo.id}_${uid}`), {
          userId: uid,
          matchId: jogo.id,
          createdAt: new Date(),
        });
        await lote.commit();
        gerados++;
      }
    }
  }

  if (gerados > 0) {
    logger.info(`${gerados} lembrete(s) de prazo gerado(s) em ${naJanela} jogo(s)`);
  }
  return { jogosNaJanela: naJanela, lembretesGerados: gerados };
}
