# region PROBLEM
'''
<> LL: Pop
Implement the pop method for the LinkedList class.

The pop method should remove the last node (tail) from the linked list and
return the removed node. If the list is empty, return None.

After the last node is removed, the second-to-last node should become the new
tail. If the list becomes empty after the pop, both head and tail should be
set to None.

Requirements:
    - Handle empty list, single-node list, and multi-node list cases.
    - Update the tail attribute correctly.
    - Update the length attribute to reflect the removal.
    - Return the removed node, or None if the list was empty.

Note: this is a singly-linked list, so finding the second-to-last node
requires walking from the head — O(n).
'''

class Node:
    def __init__(self, value):
        self.value = value
        self.next = None


class LinkedList:
    def __init__(self, value):
        new_node = Node(value)
        self.head = new_node
        self.tail = new_node
        self.length = 1

    def print_list(self):
        values = []
        temp = self.head
        while temp is not None:
            values.append(str(temp.value))
            temp = temp.next
        values.append("None")
        print(" -> ".join(values))

    def append(self, value):
        new_node = Node(value)
        if self.length == 0:
            self.head = new_node
            self.tail = new_node
        else:
            self.tail.next = new_node
            self.tail = new_node
        self.length += 1
        return True
# endregion


# region SOLUTION

def pop(self):
    if self.length <= 1:
        popped_node = self.head
        self.head = None
        self.tail = None
        self.length = 0
        return popped_node

    current_node = self.head
    while current_node.next is not self.tail:
        current_node = current_node.next

    current_node.next = None
    popped_node = self.tail
    self.tail = current_node
    self.length -= 1
    return popped_node

LinkedList.pop = pop

# endregion


# region TESTS

def test_pop_empty_list_returns_none():
    ll = LinkedList(1)
    ll.head = None
    ll.tail = None
    ll.length = 0
    assert ll.pop() is None
    assert ll.head is None
    assert ll.tail is None
    assert ll.length == 0


def test_pop_single_node():
    ll = LinkedList(1)
    popped = ll.pop()
    assert popped.value == 1
    assert ll.head is None
    assert ll.tail is None
    assert ll.length == 0


def test_pop_multiple_nodes():
    ll = LinkedList(1)
    ll.append(2)
    ll.append(3)
    popped = ll.pop()
    assert popped.value == 3
    assert ll.head.value == 1
    assert ll.tail.value == 2
    assert ll.tail.next is None
    assert ll.length == 2


def test_pop_until_empty():
    ll = LinkedList(1)
    ll.append(2)

    first = ll.pop()
    assert first.value == 2
    assert ll.head.value == 1
    assert ll.tail.value == 1
    assert ll.length == 1

    second = ll.pop()
    assert second.value == 1
    assert ll.head is None
    assert ll.tail is None
    assert ll.length == 0

    third = ll.pop()
    assert third is None
    assert ll.head is None
    assert ll.tail is None
    assert ll.length == 0


def test_pop_detaches_returned_node():
    ll = LinkedList(1)
    ll.append(2)
    popped = ll.pop()
    assert popped.next is None


if __name__ == "__main__":
    import pytest

    raise SystemExit(pytest.main([__file__, "-v"]))

# endregion
