/**
 * Formato dos documentos no Firestore.
 *
 * O Firestore não tem schema, então **este arquivo é o schema**: se um campo não
 * está aqui, ninguém deveria escrevê-lo. É também o contrato que as security
 * rules assumem — rule que consulta `role` e documento que grava `papel` é um
 * furo de autorização silencioso.
 *
 * **Nomes de campo em inglês, de propósito.** O app já mapeia o JSON da API atual
 * (`homeScore`, `deadlineOpen`, `matchday`), e `MG-7` troca o backend **atrás dos
 * mesmos repositórios**. Traduzir os campos aqui obrigaria a reescrever cada
 * mapeador do app por gosto de nomenclatura, sem ganho para ninguém. Os
 * comentários e os nomes de código nosso seguem em português.
 */

/** `usuarios/{uid}` — perfil. A credencial fica no Firebase Auth. */
export interface Usuario {
  displayName: string;
  email: string;
  createdAt: FirebaseFirestore.Timestamp;
}

/**
 * `usuarios/{uid}/boloes/{poolId}` — índice dos meus bolões, para a tela inicial.
 *
 * **Por que existe, em vez de uma consulta de grupo em `membros`:** em consulta de
 * grupo o `list` não vincula o `{uid}` do caminho, e a rule não consegue inspecionar
 * o filtro `where` — então não há como provar que a pessoa pediu só os vínculos
 * dela. Verificado no caso `MG-2-26`, que falhou com "Null value error".
 *
 * Com o índice aqui, "meus bolões" é uma consulta dentro do próprio caminho do
 * usuário: a rule decide sem `get()`, e o resumo vem no documento — nenhuma leitura
 * extra por bolão. O preço é manter esta cópia em dia, e quem faz isso é Function.
 */
export interface MeuBolao {
  poolId: string;
  name: string;
  matchdayFrom: number;
  matchdayTo: number;
  activeMembers: number;
  isAdmin: boolean;
  closed: boolean;
  archived: boolean;
  joinedAt: FirebaseFirestore.Timestamp;
}

export type Papel = 'ADMIN' | 'MEMBER';
export type CriterioDesempate = 'EXACT_SCORES' | 'WINNERS';

/** `boloes/{poolId}` */
export interface Bolao {
  name: string;
  competitionCode: string;
  matchdayFrom: number;
  matchdayTo: number;
  /** Quem criou é admin permanente: não sai, não é rebaixado, não é removido. */
  createdBy: string;
  createdAt: FirebaseFirestore.Timestamp;
  /** Tabela de pontos **deste** bolão. A apuração lê daqui, nunca de constante. */
  pointsExact: number;
  pointsWinnerDiff: number;
  pointsWinner: number;
  tiebreakOrder: CriterioDesempate[];
  /** Travadas no primeiro palpite (CC-3). Nulo enquanto ninguém palpitou. */
  rulesLockedAt: FirebaseFirestore.Timestamp | null;
  closedAt: FirebaseFirestore.Timestamp | null;
  closedEarly: boolean;
  archivedAt: FirebaseFirestore.Timestamp | null;
  /** Contagem de membros **ativos**, mantida pela Function de participação. */
  activeMembers: number;
}

/**
 * `boloes/{poolId}/membros/{uid}` — quem está no bolão.
 *
 * Guarda o `displayName` copiado do perfil: a lista de participantes precisa do
 * nome, e ler `usuarios/{uid}` de cada um seria leitura extra por participante —
 * além de furar a regra de ninguém ler o perfil alheio.
 */
export interface Membro {
  userId: string;
  displayName: string;
  role: Papel;
  joinedAt: FirebaseFirestore.Timestamp;
  /** Preenchido na saída **ou** na remoção. A saída é definitiva (CV-4). */
  leftAt: FirebaseFirestore.Timestamp | null;
}

/**
 * `boloes/{poolId}/classificacao/{uid}` — agregado materializado.
 *
 * Existe por **custo**, não por elegância: somar pontos na leitura seria uma
 * leitura por palpite por abertura de tela (20 pessoas × 38 rodadas × 10 jogos), o
 * que estoura o free tier antes do fim do campeonato. A Function de apuração
 * escreve aqui; o cliente só lê.
 */
export interface LinhaDaClassificacao {
  userId: string;
  displayName: string;
  points: number;
  exactScores: number;
  winners: number;
  /** Posição já calculada, com empate compartilhando a mesma. */
  position: number;
  sharedPosition: boolean;
  /** Critério que decidiu a posição contra o vizinho empatado (PR-5). */
  decidedBy: 'PONTOS' | CriterioDesempate | 'EMPATE_PERSISTE';
  updatedAt: FirebaseFirestore.Timestamp;
}

/** `jogos/{matchId}` — calendário. Escrita só por Function. */
export interface Jogo {
  externalId: string;
  competitionCode: string;
  matchday: number;
  homeTeam: string;
  awayTeam: string;
  kickoffAt: FirebaseFirestore.Timestamp;
  status: string;
  homeScore: number | null;
  awayScore: number | null;
  sourceUpdatedAt: FirebaseFirestore.Timestamp | null;
}

/**
 * `boloes/{poolId}/palpites/{matchId}_{uid}`
 *
 * O id é determinístico para a rule de escrita saber de quem é o palpite **sem
 * ler o documento**, e para reenvio da fila offline sobrescrever em vez de
 * duplicar.
 *
 * `matchFinished` é o estado do jogo **desnormalizado**: é o que permite à rule
 * decidir o sigilo de `PP-7` localmente. Security rule não filtra resultado de
 * query — sem esse campo, listar palpites de um jogo passaria inteiro ou falharia
 * inteiro. Quem vira o flag é a Function do fim do jogo (`MG-3`).
 */
export interface Palpite {
  userId: string;
  matchId: string;
  homeScore: number;
  awayScore: number;
  createdAt: FirebaseFirestore.Timestamp;
  updatedAt: FirebaseFirestore.Timestamp;
  matchFinished: boolean;
  /** Preenchidos pela apuração. Nulos até o jogo ser apurado. */
  points: number | null;
  band: string | null;
  scoredAt: FirebaseFirestore.Timestamp | null;
}

/** Id do palpite. Determinístico de propósito — ver `Palpite`. */
export function idDoPalpite(matchId: string, uid: string): string {
  return `${matchId}_${uid}`;
}
