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

The pop_first method should remove the first node (head) from the linked list,
update the head and length attributes, and return the removed node.

Requirements:
    - Handle both empty-list and non-empty-list cases.
    - Save a reference to the current head before updating it.
    - Update head to point to the second node.
    - Disconnect the removed node by setting its next attribute to None.
    - Update length to reflect the removal.
    - If the list becomes empty after removal, set tail to None.
    - Return the removed node, or None if the list was empty.
'''

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):
        temp = self.head
        while temp is not None:
            print(temp.value)
            temp = temp.next

    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

    def pop(self):
        if self.length == 0:
            return None
        temp = self.head
        pre = self.head
        while temp.next:
            pre = temp
            temp = temp.next
        self.tail = pre
        self.tail.next = None
        self.length -= 1
        if self.length == 0:
            self.head = None
            self.tail = None
        return temp

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


# region SOLUTION

def pop_first(self):
    
    if self.length == 0: 
        return None
    
    if self.length == 1:
        popped_node = self.head
        self.head = None
        self.tail = None
        self.length = 0
        return popped_node
    
    popped_node = self.head
    self.head = self.head.next
    popped_node.next = None
    self.length= self.length - 1
    return popped_node

LinkedList.pop_first = pop_first

# endregion


# region TESTS

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


def test_pop_first_single_node():
    ll = LinkedList(1)
    popped = ll.pop_first()
    assert popped.value == 1
    assert popped.next is None
    assert ll.head is None
    assert ll.tail is None
    assert ll.length == 0


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


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

    first = ll.pop_first()
    assert first.value == 2

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

    third = ll.pop_first()
    assert third is None


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


if __name__ == "__main__":
    import pytest

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

# endregion
