package ua;

import org.junit.jupiter.api.DisplayName;
import org.slf4j.Logger;
import org.junit.jupiter.api.Test;

import static java.lang.invoke.MethodHandles.lookup;
import static org.junit.jupiter.api.Assertions.*;


class TqsStackTest {

    // Don't use System.out.println, use a logger instead
    static final Logger log = org.slf4j.LoggerFactory.getLogger(lookup().lookupClass());

    TqsStack t = new TqsStack();

    //basics
    @Test
    void testPushIncreasesSize() {
        TqsStack<String> stack = new TqsStack<>();
        stack.push("a");
        assertEquals(1, stack.size());
    }

    @Test
    void testPushAndPeek() {
        TqsStack<String> stack = new TqsStack<>();
        stack.push("a");
        assertEquals("a", stack.peek());
        stack.push("b");
        assertEquals("b", stack.peek());

    }

    @Test
    void testPushAndPop() {
        TqsStack<String> stack = new TqsStack<>();
        stack.push("a");
        stack.push("b");
        assertEquals(2, stack.size());
        assertFalse(stack.isEmpty());
        assertEquals("b", stack.pop());
        assertEquals("a", stack.pop());
        assertEquals(0, stack.size());
        assertTrue(stack.isEmpty());
    }

    //c)

    //a stack is empty on construction
    @Test
    void initialIsEmpty() {
        TqsStack<String> stack = new TqsStack<>();
        assertTrue(stack.isEmpty());
    }

    //a stack has size 0 on construction
    @Test
    void initialSizeIsZero() {
        TqsStack<String> stack = new TqsStack<>();
        assertEquals(0, stack.size());
    }

    //After n > 0 pushes to an empty stack, the stack is not empty and its size is n
    @Test
    void testPushIncreasesSizeIsntEmpty() {
        TqsStack<String> stack = new TqsStack<>();
        assertTrue(stack.isEmpty());
        stack.push("a");
        assertEquals(1, stack.size());
        assertFalse(stack.isEmpty());
    }

    //If one pushes x then pops, the value popped is x
    @Test
    void testPushAndPopValue() {
        TqsStack<String> stack = new TqsStack<>();
        stack.push("a");
        assertEquals("a", stack.pop());
    }

    //If one pushes x then peeks, the value returned is x, but the size stays the same
   @Test
    void testPushPeekAndSize() {
        TqsStack<String> stack = new TqsStack<>();
        stack.push("a");
        assertEquals(1, stack.size());
        assertEquals("a", stack.peek());
        assertEquals(1, stack.size());
    }

    
    //If the size is n, then after n pops, the stack is empty and has a size 0
    @Test
    void testPushPopAndIsEmpty() {
        TqsStack<String> stack = new TqsStack<>();
        stack.push("a");
        stack.push("b");
        assertEquals(2, stack.size());
        stack.pop();
        stack.pop();
        assertEquals(0, stack.size());
        assertTrue(stack.isEmpty());
    }

    //Popping from an empty stack throws a NoSuchElementException
    @Test
    void testPopEmptyStack() {
        TqsStack<String> stack = new TqsStack<>();
        assertThrows(java.util.NoSuchElementException.class, stack::pop);
    }

    //Peeking into an empty stack throws a NoSuchElementException
    @Test
    void testPeekEmptyStack() {
        TqsStack<String> stack = new TqsStack<>();
        assertThrows(java.util.NoSuchElementException.class, stack::peek);
    }

    @Test
    void testPopTopN() {
        TqsStack<String> stack = new TqsStack<>();
        stack.push("a");
        stack.push("b");
        stack.push("c");
        stack.push("d");

        //discard d, return c
        String result = stack.popTopN(2);
        assertEquals("c", result);
        assertEquals(2, stack.size());

        assertEquals("b", stack.popTopN(1));
        assertEquals(1, stack.size());

        assertThrows(IllegalArgumentException.class, () -> stack.popTopN(5));
    }

    @Test
    void testPopTopNWithZeroOrNegativeN() {
        TqsStack<String> stack = new TqsStack<>();
        stack.push("a");
        stack.push("b");

        assertThrows(IllegalArgumentException.class, () -> stack.popTopN(0));

        assertThrows(IllegalArgumentException.class, () -> stack.popTopN(-1));
    }

}