import { Injectable, Logger } from '@nestjs/common';
import {
  CompeticaoExterna,
  FonteDeDados,
  FonteIndisponivelError,
  JogoExterno,
} from './fonte-dados.port';

/** Os 20 clubes da Série A, para o fixture parecer o campeonato real. */
const CLUBES = [
  'Flamengo',
  'Palmeiras',
  'Corinthians',
  'São Paulo',
  'Grêmio',
  'Internacional',
  'Atlético-MG',
  'Cruzeiro',
  'Fluminense',
  'Botafogo',
  'Vasco',
  'Santos',
  'Bahia',
  'Fortaleza',
  'Athletico-PR',
  'Bragantino',
  'Cuiabá',
  'Juventude',
  'Vitória',
  'Criciúma',
];

const TOTAL_RODADAS = 38;
const JOGOS_POR_RODADA = CLUBES.length / 2;

/**
 * Fonte de dados falsa.
 *
 * Não é mock de teste: é o adaptador que sustenta o desenvolvimento enquanto o
 * token da `football-data.org` não existe.
 *
 * **Confrontos são determinísticos; horários são relativos a hoje.** Os mesmos
 * times se enfrentam sempre nas mesmas rodadas, mas o calendário desliza com a
 * data atual para que existam sempre rodadas passadas (prazo fechado) e futuras
 * (prazo aberto) — ver `inicioTemporada`.
 *
 * `FONTE_FAKE_INDISPONIVEL=true` faz todas as chamadas falharem, para exercitar
 * o caminho de indisponibilidade (CC-1 BE-5) sem precisar derrubar nada.
 */
@Injectable()
export class FonteFakeAdapter implements FonteDeDados {
  private readonly log = new Logger(FonteFakeAdapter.name);

  /**
   * Rodada considerada "a atual". Rodadas antes dela ficam no passado (prazo
   * fechado), depois no futuro (prazo aberto).
   */
  private readonly rodadaAtual = 22;

  /**
   * Âncora da temporada, calculada para que `rodadaAtual` caia nesta semana.
   *
   * Uma âncora de data fixa era mais determinística, mas criava uma bomba de
   * tempo: conforme o tempo real passa, as rodadas iam todas para o passado e
   * nenhum teste de prazo aberto funcionaria mais. Num domínio em que **tudo**
   * gira em torno de prazo, ter sempre jogos passados e futuros vale mais que
   * a data ser idêntica entre execuções.
   */
  private get inicioTemporada(): Date {
    const semana = 7 * 24 * 60 * 60 * 1000;
    // Meia-noite UTC de hoje: estável dentro do mesmo dia.
    const hoje = new Date(
      new Date().toISOString().slice(0, 10) + 'T21:00:00.000Z',
    );
    return new Date(hoje.getTime() - (this.rodadaAtual - 1) * semana);
  }

  constructor() {
    this.log.warn(
      'usando a fonte de dados FALSA — jogos fictícios. Defina FOOTBALL_DATA_TOKEN para usar a fonte real.',
    );
  }

  private get indisponivel(): boolean {
    return process.env.FONTE_FAKE_INDISPONIVEL === 'true';
  }

  listarCompeticoes(): Promise<CompeticaoExterna[]> {
    if (this.indisponivel) {
      return Promise.reject(
        new FonteIndisponivelError('simulada por configuração'),
      );
    }
    return Promise.resolve([
      {
        code: 'BSA',
        nome: 'Campeonato Brasileiro Série A',
        rodadaAtual: this.rodadaAtual,
        totalRodadas: TOTAL_RODADAS,
      },
    ]);
  }

  listarJogos(
    competitionCode: string,
    rodadaInicial: number,
    rodadaFinal: number,
  ): Promise<JogoExterno[]> {
    if (this.indisponivel) {
      return Promise.reject(
        new FonteIndisponivelError('simulada por configuração'),
      );
    }
    if (competitionCode !== 'BSA') {
      return Promise.resolve([]);
    }

    const jogos: JogoExterno[] = [];
    const inicio = Math.max(1, rodadaInicial);
    const fim = Math.min(TOTAL_RODADAS, rodadaFinal);

    for (let rodada = inicio; rodada <= fim; rodada++) {
      for (let i = 0; i < JOGOS_POR_RODADA; i++) {
        // Rotação simples dos clubes para não repetir confronto na mesma rodada.
        const casa = CLUBES[(i + rodada) % CLUBES.length];
        const fora = CLUBES[(i + rodada + JOGOS_POR_RODADA) % CLUBES.length];

        // Rodada por semana; jogos espaçados de 2h dentro da rodada. Isso é o
        // que faz o prazo fechar por jogo, e não por rodada (PP-5).
        const inicioEm = new Date(
          this.inicioTemporada.getTime() +
            (rodada - 1) * 7 * 24 * 60 * 60 * 1000 +
            i * 2 * 60 * 60 * 1000,
        );

        // Jogo cujo horário já passou vem apurado, como a fonte real faria.
        // Sem isso não haveria o que pontuar em PR-1.
        const terminou = inicioEm.getTime() + 2 * 60 * 60 * 1000 < Date.now();

        jogos.push({
          externalId: `fake-BSA-${rodada}-${i}`,
          competitionCode: 'BSA',
          rodada,
          timeCasa: casa,
          timeFora: fora,
          inicioEm,
          status: terminou ? 'FINISHED' : 'TIMED',
          // Placar determinístico pela rodada e posição: os mesmos resultados
          // em toda execução, para o teste de pontuação ser reproduzível.
          golsCasa: terminou ? (rodada + i) % 4 : null,
          golsFora: terminou ? (rodada + i * 2) % 3 : null,
          atualizadoEm: terminou ? inicioEm : null,
        });
      }
    }

    return Promise.resolve(jogos);
  }
}
