import { Inject, Injectable, Logger } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { PUSH } from '../push/push.port';
import type { PushSender } from '../push/push.port';

export interface ResultadoLembretes {
  jogosNaJanela: number;
  lembretesEnviados: number;
  pushEntregues: number;
}

@Injectable()
export class LembretesService {
  private readonly log = new Logger(LembretesService.name);

  /** Quantos minutos antes do apito o lembrete dispara. */
  private get minutosAntes(): number {
    return Number(process.env.LEMBRETE_MINUTOS_ANTES ?? 120);
  }

  constructor(
    private readonly prisma: PrismaService,
    @Inject(PUSH) private readonly push: PushSender,
  ) {}

  /**
   * Lembra **apenas** quem ainda não palpitou nos jogos que estão fechando.
   *
   * O PRD é explícito: se o lembrete de prazo vier junto com aviso de ranking e
   * comentário, o casual desliga tudo e volta a perder rodada. Por isso este job
   * só olha prazo, e nada mais.
   */
  async dispararPendentes(): Promise<ResultadoLembretes> {
    const agora = new Date();
    const limite = new Date(agora.getTime() + this.minutosAntes * 60 * 1000);

    // Jogos de bolões ativos cujo prazo fecha dentro da janela.
    const vinculos = await this.prisma.poolMatch.findMany({
      where: {
        pool: { closedAt: null },
        match: { kickoffAt: { gt: agora, lte: limite } },
      },
      select: {
        poolId: true,
        matchId: true,
        match: { select: { homeTeam: true, awayTeam: true, kickoffAt: true } },
      },
    });

    let enviados = 0;
    let entregues = 0;

    for (const v of vinculos) {
      // Participantes ativos que ainda não palpitaram neste jogo e ainda não
      // foram lembrados dele. Quem saiu do bolão não entra — é o que atende
      // CV-4 BE-4 e CC-9 BE-5.
      const pendentes = await this.prisma.poolMember.findMany({
        where: {
          poolId: v.poolId,
          leftAt: null,
          user: {
            predictions: { none: { poolId: v.poolId, matchId: v.matchId } },
            reminders: { none: { poolId: v.poolId, matchId: v.matchId } },
          },
        },
        select: { userId: true },
      });

      for (const { userId } of pendentes) {
        const titulo = 'Seu palpite está fechando';
        const corpo =
          `${v.match.homeTeam} x ${v.match.awayTeam} começa em breve e você ainda ` +
          `não palpitou. Depois do apito não dá mais.`;

        // Grava aviso e marca o lembrete na mesma transação: se o push falhar, o
        // participante ainda vê o aviso no app — e não é lembrado duas vezes.
        await this.prisma.$transaction([
          this.prisma.notification.create({
            data: {
              userId,
              poolId: v.poolId,
              kind: 'PRAZO_FECHANDO',
              title: titulo,
              body: corpo,
            },
          }),
          this.prisma.predictionReminder.create({
            data: { userId, matchId: v.matchId, poolId: v.poolId },
          }),
        ]);

        const r = await this.push.enviar(userId, titulo, corpo);
        if (r.entregue) entregues++;
        enviados++;
      }
    }

    if (enviados > 0) {
      this.log.log(
        `${enviados} lembrete(s) de prazo gerado(s), ${entregues} entregue(s) por push`,
      );
    }
    return {
      jogosNaJanela: vinculos.length,
      lembretesEnviados: enviados,
      pushEntregues: entregues,
    };
  }
}
