import { describe, it, expect, beforeEach } from "vitest";

import { Scoreboard } from "../src/scoreboard";
import {
  ERROR_NO_MATCH,
  ERROR_SAME_TEAM,
  ERROR_ALREADY_PLAYING,
} from "../src/constants";

describe("Scoreboard Class", () => {
  let scoreboard: Scoreboard;
  let summary: string[];

  beforeEach(() => {
    scoreboard = new Scoreboard();
    summary = [];
  });

  it("should start a new match and add it to the scoreboard", () => {
    scoreboard.startMatch("Australia", "Cuba");
    summary = scoreboard.getSummary();

    expect(summary).toEqual(["1. Australia 0 - Cuba 0"]);
  });

  it("should be sorted by startTime", async () => {
    await startMatches(
      [
        ["Australia", "Cuba"],
        ["Mongolia", "Canada"],
      ],
      scoreboard
    );
    summary = scoreboard.getSummary();

    expect(summary).toEqual([
      "1. Mongolia 0 - Canada 0",
      "2. Australia 0 - Cuba 0",
    ]);
  });

  it("should update the score correctly", () => {
    scoreboard.startMatch("Australia", "Cuba");
    scoreboard.updateScore("Australia", "Cuba", 3, 2);
    summary = scoreboard.getSummary();

    expect(summary).toEqual(["1. Australia 3 - Cuba 2"]);
  });

  it("should maintain the correct order after a score update", async () => {
    await startMatches(
      [
        ["Israel", "Italy"],
        ["Ukraine", "England"],
        ["Poland", "Czechia"],
        ["China", "USA"],
      ],
      scoreboard
    );
    scoreboard.updateScore("Israel", "Italy", 2, 2);
    scoreboard.updateScore("Poland", "Czechia", 3, 2);
    scoreboard.updateScore("Ukraine", "England", 5, 0);
    scoreboard.updateScore("China", "USA", 3, 4);
    summary = scoreboard.getSummary();

    expect(summary).toEqual([
      "1. China 3 - USA 4",
      "2. Poland 3 - Czechia 2",
      "3. Ukraine 5 - England 0",
      "4. Israel 2 - Italy 2",
    ]);
  });

  it("should finish the selected match and remove it from both matches map and sortedMatches array", async () => {
    await startMatches(
      [
        ["Israel", "Italy"],
        ["Ukraine", "England"],
        ["Poland", "Czechia"],
        ["China", "USA"],
      ],
      scoreboard
    );
    scoreboard.finishMatch("China", "USA");
    scoreboard.finishMatch("Poland", "Czechia");
    scoreboard.finishMatch("Israel", "Italy");
    summary = scoreboard.getSummary();
    const isMatchDeleted = !scoreboard.matches.get("Poland vs Czechia");

    expect(summary).toEqual(["1. Ukraine 0 - England 0"]);
    expect(isMatchDeleted).toBe(true);
  });

  it("should throw an error when updating or finishing a non-existing match", async () => {
    await startMatches(
      [
        ["Israel", "Italy"],
        ["China", "USA"],
      ],
      scoreboard
    );

    expect(() => {
      scoreboard.finishMatch("Israel", "USA");
    }).toThrowError(ERROR_NO_MATCH);
    expect(() => {
      scoreboard.updateScore("USA", "China", 1, 2);
    }).toThrowError(ERROR_NO_MATCH);
  });

  it("should throw an error when starting a match that already exists", () => {
    scoreboard.startMatch("Australia", "Cuba");

    expect(() => {
      scoreboard.startMatch("Australia", "Cuba");
    }).toThrowError(ERROR_ALREADY_PLAYING);
  });

  it("should throw an error when trying to start a match with the same teams but with different casing", () => {
    scoreboard.startMatch("Australia", "Cuba");

    expect(() => {
      scoreboard.startMatch("AUStRALia", "CuBa");
    }).toThrowError(ERROR_ALREADY_PLAYING);
  });

  it("should not allow a team to play against itself", () => {
    expect(() => {
      scoreboard.startMatch("Austria", "Austria");
    }).toThrowError(ERROR_SAME_TEAM);
  });

  it("should prevent a match from starting if one or both teams are already playing", () => {
    scoreboard.startMatch("USA", "China");

    expect(() => {
      scoreboard.startMatch("Peru", "USA");
    }).toThrowError(ERROR_ALREADY_PLAYING);

    expect(() => {
      scoreboard.startMatch("Brazil", "China");
    }).toThrowError(ERROR_ALREADY_PLAYING);

    expect(() => {
      scoreboard.finishMatch("USA", "China");
      scoreboard.startMatch("Brazil", "China");
      scoreboard.startMatch("Peru", "USA");
    }).not.toThrowError();
  });
});

async function startMatches(
  matches: [string, string][],
  scoreboard: Scoreboard
) {
  for (const match of matches) {
    scoreboard.startMatch(match[0], match[1]);
    await new Promise<void>((resolve) => setTimeout(resolve, 1));
  }
}
