import { getFirestore, type Firestore } from 'firebase-admin/firestore';

/**
 * Avisos endereçados ao participante — `usuarios/{uid}/avisos/{id}`.
 *
 * O caso difícil é o de `PR-7`: quando a sincronização corrige a rodada inteira,
 * **um aviso por jogo transforma o domingo à noite em spam** — e quem desliga a
 * notificação por spam deixa de ver a que importa.
 *
 * No NestJS a consolidação era fácil: a sincronização passava um coletor em
 * memória e gravava tudo junto no fim. **Em Functions esse coletor não existe:**
 * cada jogo corrigido é uma invocação separada, possivelmente em outra máquina,
 * e nenhuma delas sabe que as outras estão acontecendo.
 *
 * A consolidação passa a ser por **fusão**: se já existe um aviso de correção
 * não lido e recente para o mesmo bolão, o jogo novo entra nele em vez de virar
 * outro. O resultado que o participante vê é o mesmo — uma mensagem dizendo
 * tudo que mudou —, e o mecanismo deixa de depender de as correções acontecerem
 * no mesmo processo.
 */

/** Janela em que dois avisos de correção viram um só. */
export const JANELA_DE_FUSAO_MS = 10 * 60 * 1000;

export interface JogoCorrigido {
  jogo: string;
  placarDe: string;
  placarPara: string;
}

export interface DadosDoAviso {
  poolId: string;
  manual: boolean;
  jogo: JogoCorrigido;
  posicaoDe: number | null;
  posicaoPara: number | null;
}

export async function avisarCorrecao(
  db: Firestore,
  uid: string,
  dados: DadosDoAviso,
): Promise<void> {
  const colecao = db.collection(`usuarios/${uid}/avisos`);
  const recentes = await colecao
    .where('poolId', '==', dados.poolId)
    .where('readAt', '==', null)
    .where('createdAt', '>', new Date(Date.now() - JANELA_DE_FUSAO_MS))
    .get();

  const anterior = recentes.docs.find((d) =>
    (d.data().kind as string).startsWith('PLACAR_CORRIGIDO'),
  );

  const agora = new Date();
  if (!anterior) {
    await colecao.add({
      kind: dados.manual ? 'PLACAR_CORRIGIDO_MANUAL' : 'PLACAR_CORRIGIDO',
      poolId: dados.poolId,
      jogos: [dados.jogo],
      // A posição de origem é guardada para a **mudança líquida**: somar os
      // degraus intermediários contaria posições que ninguém chegou a ver.
      posicaoDe: dados.posicaoDe,
      posicaoPara: dados.posicaoPara,
      ...textoDoAviso([dados.jogo], dados.manual, dados.posicaoDe, dados.posicaoPara),
      createdAt: agora,
      readAt: null,
    });
    return;
  }

  const atual = anterior.data();
  const jogos = [...(atual.jogos as JogoCorrigido[]), dados.jogo];
  const manual = (atual.kind as string).endsWith('MANUAL') || dados.manual;
  // De onde a pessoa estava **antes da primeira** correção para onde ficou
  // **depois da última**.
  const posicaoDe = (atual.posicaoDe as number | null) ?? dados.posicaoDe;
  const posicaoPara = dados.posicaoPara ?? (atual.posicaoPara as number | null);

  await anterior.ref.update({
    kind: manual ? 'PLACAR_CORRIGIDO_MANUAL' : 'PLACAR_CORRIGIDO',
    jogos,
    posicaoDe,
    posicaoPara,
    ...textoDoAviso(jogos, manual, posicaoDe, posicaoPara),
    createdAt: agora,
  });
}

/** Texto do aviso, para um jogo ou para vários — **portado** de `PR-7`. */
export function textoDoAviso(
  jogos: JogoCorrigido[],
  manual: boolean,
  posicaoDe: number | null,
  posicaoPara: number | null,
): { title: string; body: string } {
  const detalhe =
    posicaoDe !== null && posicaoPara !== null && posicaoDe !== posicaoPara
      ? ` Sua posição no ranking passou de ${posicaoDe}º para ${posicaoPara}º.`
      : '';
  const porQuem = manual ? ' (corrigido pelo administrador)' : '';

  if (jogos.length === 1) {
    const j = jogos[0]!;
    return {
      title: 'Sua pontuação mudou',
      body:
        `${j.jogo}: o placar mudou de ${j.placarDe} para ${j.placarPara}` +
        `${porQuem}.${detalhe}`,
    };
  }

  const lista = jogos
    .map((j) => `${j.jogo} (${j.placarDe} → ${j.placarPara})`)
    .join('; ');
  return {
    title: `Sua pontuação mudou em ${jogos.length} jogos`,
    body: `${jogos.length} jogos tiveram o placar corrigido: ${lista}${porQuem}.`
      + detalhe,
  };
}

/** Atalho para quem já tem uma instância à mão. */
export const avisos = (uid: string, db: Firestore = getFirestore()) =>
  db.collection(`usuarios/${uid}/avisos`);
