using System;
using System.Reflection;

internal static class KoscTests
{
    private static int zaliczone;
    private static int niezaliczone;

    public static int Run()
    {
        zaliczone = 0;
        niezaliczone = 0;

        UruchomTest(
            "Rzut zwraca wartość od 1 do 6",
            Test_Rzut_ZwracaWartoscOd1Do6);

        UruchomTest(
            "Rzut nie zmienia wartości, gdy kość nie jest dostępna",
            Test_Rzut_NieZmieniaWartosciGdyKoscNieJestDostepna);

        Console.WriteLine();
        Console.WriteLine($"Wynik testów: {zaliczone} zaliczone, {niezaliczone} niezaliczone.");

        return niezaliczone == 0 ? 0 : 1;
    }

    private static void Test_Rzut_ZwracaWartoscOd1Do6()
    {
        Przygotuj();

        Kosc kosc = new Kosc(1);

        for (int i = 0; i < 100; i++)
        {
            kosc.Rzut();

            SprawdzWiekszeLubRowne(kosc.LiczbaOczek, 1, nameof(kosc.LiczbaOczek));
            SprawdzMniejszeLubRowne(kosc.LiczbaOczek, 6, nameof(kosc.LiczbaOczek));
            SprawdzRowne(kosc.IdentyfikatorPliku, kosc.LiczbaOczek, nameof(kosc.IdentyfikatorPliku));
        }
    }

    private static void Test_Rzut_NieZmieniaWartosciGdyKoscNieJestDostepna()
    {
        Przygotuj();

        Kosc kosc = new Kosc(4);
        kosc.Blokuj();

        int oczekiwanaLiczbaOczek = kosc.LiczbaOczek;
        int oczekiwanyIdentyfikatorPliku = kosc.IdentyfikatorPliku;

        for (int i = 0; i < 100; i++)
        {
            kosc.Rzut();

            SprawdzRowne(kosc.LiczbaOczek, oczekiwanaLiczbaOczek, nameof(kosc.LiczbaOczek));
            SprawdzRowne(kosc.IdentyfikatorPliku, oczekiwanyIdentyfikatorPliku, nameof(kosc.IdentyfikatorPliku));
        }
    }

    private static void Przygotuj()
    {
        ZresetujLiczbeInstancji();
    }

    private static void ZresetujLiczbeInstancji()
    {
        FieldInfo poleLicznika = typeof(Kosc).GetField(
            "<LiczbaInstancji>k__BackingField",
            BindingFlags.Static | BindingFlags.NonPublic);

        if (poleLicznika == null)
        {
            throw new InvalidOperationException(
                "Nie udało się zresetować Kosc.LiczbaInstancji. " +
                "Sprawdź, czy LiczbaInstancji nadal jest automatyczną właściwością statyczną.");
        }

        poleLicznika.SetValue(null, 0);
    }

    private static void UruchomTest(string nazwa, Action test)
    {
        try
        {
            test();
            zaliczone++;
            Console.WriteLine($"[OK] {nazwa}");
        }
        catch (Exception exception)
        {
            niezaliczone++;
            Console.WriteLine($"[BŁĄD] {nazwa}");
            Console.WriteLine($"       {exception.Message}");
        }
    }

    private static void SprawdzWiekszeLubRowne(int aktualna, int minimalna, string nazwa)
    {
        if (aktualna < minimalna)
        {
            throw new Exception($"{nazwa}: oczekiwano wartości >= {minimalna}, otrzymano {aktualna}.");
        }
    }

    private static void SprawdzMniejszeLubRowne(int aktualna, int maksymalna, string nazwa)
    {
        if (aktualna > maksymalna)
        {
            throw new Exception($"{nazwa}: oczekiwano wartości <= {maksymalna}, otrzymano {aktualna}.");
        }
    }

    private static void SprawdzRowne<T>(T aktualna, T oczekiwana, string nazwa)
    {
        if (!object.Equals(aktualna, oczekiwana))
        {
            throw new Exception($"{nazwa}: oczekiwano {oczekiwana}, otrzymano {aktualna}.");
        }
    }
}