"""Tests for BrowserState navigation stack and pagination."""

from r36s_dlna_browser.dlna.browser_state import BrowserState
from r36s_dlna_browser.dlna.models import MediaServer, MediaItem, ItemType


def _server(name: str = "TestServer") -> MediaServer:
    return MediaServer(friendly_name=name, location=f"http://{name}:8080/desc.xml")


def _items(n: int, container: bool = False) -> list[MediaItem]:
    t = ItemType.CONTAINER if container else ItemType.AUDIO
    return [
        MediaItem(
            object_id=str(i),
            title=f"Item {i}",
            item_type=t,
            resource_url="" if container else f"http://srv/file{i}.mp3",
        )
        for i in range(n)
    ]


class TestServerList:
    def test_empty_initial(self):
        s = BrowserState()
        assert s.servers == []
        assert s.selected_server is None

    def test_set_servers(self):
        s = BrowserState()
        servers = [_server("A"), _server("B")]
        s.set_servers(servers)
        assert len(s.servers) == 2
        assert s.server_cursor == 0
        assert s.selected_server.friendly_name == "A"

    def test_cursor_bounds(self):
        s = BrowserState()
        s.set_servers([_server("A"), _server("B"), _server("C")])
        s.server_cursor = 2
        assert s.selected_server.friendly_name == "C"
        s.server_cursor = 5  # out of bounds
        assert s.selected_server is None


class TestBrowseStack:
    def test_not_in_browse_initially(self):
        s = BrowserState()
        assert not s.in_browse_mode

    def test_enter_server(self):
        s = BrowserState()
        s.set_servers([_server()])
        obj_id = s.enter_server(s.selected_server)
        assert obj_id == "0"
        assert s.in_browse_mode
        assert s.current_level.object_id == "0"

    def test_set_items_and_cursor(self):
        s = BrowserState()
        s.set_servers([_server()])
        s.enter_server(s.selected_server)
        items = _items(10)
        s.set_items(items, "0")
        assert len(s.current_items) == 10
        assert s.cursor == 0
        s.cursor = 3
        assert s.cursor == 3
        assert s.selected_item().title == "Item 3"

    def test_cursor_clamps(self):
        s = BrowserState()
        s.set_servers([_server()])
        s.enter_server(s.selected_server)
        s.set_items(_items(5), "0")
        s.cursor = 100
        assert s.cursor == 4  # clamped to last
        s.cursor = -5
        assert s.cursor == 0

    def test_push_and_pop(self):
        s = BrowserState()
        s.set_servers([_server()])
        s.enter_server(s.selected_server)
        s.set_items(_items(3, container=True), "0")

        # Drill into child
        s.set_items(_items(5), "1")
        assert len(s.current_items) == 5

        # Go back
        assert s.go_back()
        assert len(s.current_items) == 3
        assert s.current_level.object_id == "0"

        # Go back again exits browse
        assert s.go_back()
        assert not s.in_browse_mode

    def test_go_back_at_root(self):
        s = BrowserState()
        assert not s.go_back()

    def test_reset(self):
        s = BrowserState()
        s.set_servers([_server()])
        s.enter_server(s.selected_server)
        s.set_items(_items(5), "0")
        s.error = "fail"
        s.playback_title = "song"
        s.playback_paused = True
        s.reset()
        assert not s.in_browse_mode
        assert s.error == ""
        assert s.playback_title == ""
        assert not s.playback_paused


class TestScrollOffset:
    def test_scroll_offset_default(self):
        s = BrowserState()
        s.set_servers([_server()])
        s.enter_server(s.selected_server)
        s.set_items(_items(3), "0")
        assert s.scroll_offset == 0

    def test_scroll_offset_set(self):
        s = BrowserState()
        s.set_servers([_server()])
        s.enter_server(s.selected_server)
        s.set_items(_items(30), "0")
        s.scroll_offset = 10
        assert s.scroll_offset == 10
