package p11_klasy.enkapsulacja;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class KontoTest {
	private Konto konto;
	
	@BeforeEach
	void setUp() {
		// ta metoda wykona się przed każdym testem
		// wykorzystamy ją do inicjalizacji obiektów potrzebnych w teście - konto
		konto = new Konto(1, 1000, new Osoba("Ala", "Kowalska", "2000-01-01"));
	}

	@Test
	void testWplata() {
		konto.wplata(300);
		// w testach jednostkowych nie printujemy danych na ekran, tylko sprawdzamy za pomocą "asercji" czy wyniki się zgadzają
		// assert konto.getSaldo() == 1300;
		// zazwyczaj używa się asercji definiowanych przez JUnit lub inne dodatkowe biblioteki
		assertEquals(1300, konto.getSaldo());
	}

	@Test
	void testWyplata() throws BrakSrodkow {
		konto.wyplata(200);
		assertEquals(800, konto.getSaldo());
	}

	// Temat sprawdzania wyjątków w testach.
	
	// Aby stwierdzić, że w pewnej sytuacji metoda POWINNA skończyć się wyjątkiem,
	// możemy użyć odpowiednio try-catch (testWplataTry)
	// albo użyć dedykowanych rozwiązań JUnit - dalsze przykłady
	
	@Test
	void testWplataTry() {
		try {
			konto.wplata(-100);
			// Gdyby nie było wyjątku i program doszedłby do tego miejsca,
			// test powinien skończyć się porażką
			fail("Powinien być wyjątek, a nie ma.");
		} catch(IllegalArgumentException e) {
			// Jeśli wyłapiemy wyjątek, możemy sprawdzić w teście jego szczegóły,
			// np. jaki jest message (bardzo rzadko takie rzeczy sprawdza się w testach, ale można):
			assertEquals("Kwota nie jest dodatnia", e.getMessage());
		} finally {
			// Możemy też upewnić się, że w przypadku wystąpienia wyjątku stan konta się nie zmienił.
			assertEquals(1000, konto.getSaldo());
		}
	}
	
	// Analogiczny test można też napisać wykorzystując nową funkcjonalność JUnit 5:
	@Test
	void testWplataUjemna_v1() {
		// assertThrows wykonuje podany fragment kodu (w praktyce podaje się wyrażenie lambda)
		// i sprawdza czy ten kończy się podanym rodzajem wyjątku.
		// Jeśli jest wyjątek - dobrze
		// Jeśli nie ma wyjątku - test failuje
		
		assertThrows(IllegalArgumentException.class, () -> {
			konto.wplata(-100);
		});

		// Dodatkowo po wykonaniu assertThrows możemy sprawdzić jaki jest stan końcowy,
		// np. czy saldo się nie zmieniło.
		assertEquals(1000, konto.getSaldo());
	}
	
	// Aby sprawdzić jaki jest message w wyjątku itp, możemy odebrać obiekt wyjątku i sprawdzić bezpośrednio
	@Test
	void testWplataUjemna_v2() {
		IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
			konto.wplata(-100);			
		});

		assertEquals("Kwota nie jest dodatnia", exception.getMessage());
		assertEquals(1000, konto.getSaldo());
	}
	
	@Test
	void testWyplataUjemna() {
		assertThrows(IllegalArgumentException.class, () -> {
			konto.wyplata(-100);			
		});
		assertEquals(1000, konto.getSaldo());
	}
	
	@Test
	void testBrakSrodkow() {
		assertThrows(BrakSrodkow.class, () -> {
			konto.wyplata(1300);			
		});
		assertEquals(1000, konto.getSaldo());
	}
	
	@Test
	void testPrzelew() throws BrakSrodkow {
		Konto inne = new Konto(2, 2000, new Osoba("Ola", "Malinowska", "2000-01-01"));
		konto.przelew(inne, 400);
		assertEquals(600, konto.getSaldo());
		assertEquals(2400, inne.getSaldo());
	}
	
	@Test
	void testPrzelewUjemny() {
		Konto inne = new Konto(2, 2000, new Osoba("Ola", "Malinowska", "2000-01-01"));
		assertThrows(IllegalArgumentException.class, () -> {
			konto.przelew(inne, -100);			
		});
		assertEquals(1000, konto.getSaldo());
		assertEquals(2000, inne.getSaldo());
	}
	// analogicznie dla BrakSrodkow
}
