import { getFirestore, type Firestore } from 'firebase-admin/firestore';
import { onDocumentCreated } from 'firebase-functions/v2/firestore';
import { HttpsError, onCall } from 'firebase-functions/v2/https';
import { exigirAdmin, uidDe } from './acesso';

/**
 * Regras do bolão — **portado** de `regras.service.ts`.
 *
 * A leitura não está aqui: as regras ficam no próprio documento do bolão, que
 * todo participante já lê (`CC-6` é rota de participante, não de admin). Uma
 * callable para devolver o que o cliente pode ler sozinho seria uma invocação
 * cobrada por nada.
 */

export interface RegrasNovas {
  pointsExact: number;
  pointsWinnerDiff: number;
  pointsWinner: number;
  tiebreakOrder: string[];
}

export const alterarRegras = onCall(async (request) =>
  alterar(
    uidDe(request),
    (request.data?.poolId ?? '') as string,
    request.data?.rules as RegrasNovas,
  ),
);

export async function alterar(
  uid: string,
  poolId: string,
  regras: RegrasNovas,
  db: Firestore = getFirestore(),
): Promise<RegrasNovas> {
  const bolao = await db.doc(`boloes/${poolId}`).get();
  if (!bolao.exists) throw new HttpsError('not-found', 'bolão não encontrado');
  await exigirAdmin(db, poolId, uid);

  if (bolao.data()!.rulesLockedAt != null) {
    // A trava é o que torna justa a participação do admin no próprio bolão —
    // sem ela, o sigilo de `PP-7` não basta: bastaria esperar os palpites e
    // ajustar a tabela para o que o dele acerta.
    throw new HttpsError(
      'failed-precondition',
      'as regras travaram no primeiro palpite e não podem mais mudar. Para jogar com regra nova, encerre este bolão e abra outro',
      { motivo: 'TRAVADAS' },
    );
  }

  const valores = [
    regras?.pointsExact,
    regras?.pointsWinnerDiff,
    regras?.pointsWinner,
  ];
  if (valores.some((v) => !Number.isInteger(v) || (v as number) < 0)) {
    throw new HttpsError('invalid-argument', 'pontuação inválida');
  }
  const ordem = (regras?.tiebreakOrder ?? []).filter(
    (c) => c === 'EXACT_SCORES' || c === 'WINNERS',
  );

  await db.doc(`boloes/${poolId}`).update({
    pointsExact: regras.pointsExact,
    pointsWinnerDiff: regras.pointsWinnerDiff,
    pointsWinner: regras.pointsWinner,
    tiebreakOrder: ordem.length > 0 ? ordem : ['EXACT_SCORES', 'WINNERS'],
  });

  return { ...regras, tiebreakOrder: ordem };
}

/**
 * Trava as regras no **primeiro palpite** do bolão (`CC-3`).
 *
 * Precisa ser gatilho: o palpite é escrita direta do cliente (é o caminho quente
 * do produto, e Function no meio somaria cold start ao momento em que todo mundo
 * palpita junto). Quem grava o palpite não pode ser quem trava, então quem trava
 * é quem escuta.
 *
 * A janela entre o palpite e a trava é de milissegundos, e o pior caso dela é o
 * admin conseguir alterar a regra logo depois do primeiro palpite — o mesmo
 * risco que o NestJS tinha entre o `INSERT` e o `UPDATE`, e menor que o de
 * deixar o cliente travar (ele simplesmente não travaria).
 */
export const travarRegrasNoPrimeiroPalpite = onDocumentCreated(
  'boloes/{poolId}/palpites/{palpiteId}',
  async (evento) => {
    const db = getFirestore();
    const bolao = db.doc(`boloes/${evento.params.poolId}`);
    const atual = await bolao.get();
    if (!atual.exists || atual.data()!.rulesLockedAt != null) return;
    await bolao.update({ rulesLockedAt: new Date() });
  },
);
