from vt100wasm import apply_delta, screen_contents, screen_contents_formatted


class TestApplyDelta:
    def test_initial_state_from_none(self):
        state = apply_delta(None, b"hello", 24, 80)
        assert isinstance(state, bytes)
        assert len(state) > 5
        assert screen_contents(state).startswith("hello")

    def test_incremental_update(self):
        s1 = apply_delta(None, b"hello", 24, 80)
        s2 = apply_delta(s1, b" world", 24, 80)
        assert "hello world" in screen_contents(s2)

    def test_empty_delta_preserves_state(self):
        s1 = apply_delta(None, b"hello", 24, 80)
        s2 = apply_delta(s1, b"", 24, 80)
        assert screen_contents(s1) == screen_contents(s2)

    def test_newline_handling(self):
        state = apply_delta(None, b"line1\r\nline2\r\nline3", 24, 80)
        contents = screen_contents(state)
        assert "line1" in contents
        assert "line2" in contents
        assert "line3" in contents

    def test_ansi_escape_codes(self):
        state = apply_delta(None, b"\x1b[31mred\x1b[0m normal", 24, 80)
        contents = screen_contents(state)
        assert "red" in contents
        assert "normal" in contents

    def test_header_encodes_dimensions(self):
        state = apply_delta(None, b"x", 30, 120)
        rows = int.from_bytes(state[0:2], "little")
        cols = int.from_bytes(state[2:4], "little")
        assert rows == 30
        assert cols == 120

    def test_utf8_content(self):
        state = apply_delta(None, b"hello world", 24, 80)
        contents = screen_contents(state)
        assert "hello" in contents

    def test_large_output(self):
        data = (b"x" * 79 + b"\r\n") * 100
        state = apply_delta(None, data, 24, 80)
        assert isinstance(state, bytes)
        assert len(state) > 4


class TestResize:
    def test_resize_changes_dimensions(self):
        s1 = apply_delta(None, b"hello", 24, 80)
        s2 = apply_delta(s1, b"", 40, 120)
        rows = int.from_bytes(s2[0:2], "little")
        cols = int.from_bytes(s2[2:4], "little")
        assert rows == 40
        assert cols == 120

    def test_resize_preserves_content(self):
        s1 = apply_delta(None, b"hello", 24, 80)
        s2 = apply_delta(s1, b"", 40, 120)
        assert "hello" in screen_contents(s2)


class TestScreenContents:
    def test_empty_state(self):
        assert screen_contents(b"") == ""

    def test_short_state(self):
        assert screen_contents(b"\x00\x00") == ""

    def test_plain_text(self):
        state = apply_delta(None, b"hello world", 24, 80)
        assert screen_contents(state).startswith("hello world")


class TestScreenContentsFormatted:
    def test_empty_state(self):
        assert screen_contents_formatted(b"") == b""

    def test_short_state(self):
        assert screen_contents_formatted(b"\x00\x00") == b""

    def test_roundtrip(self):
        state = apply_delta(None, b"hello\r\nworld", 24, 80)
        formatted = screen_contents_formatted(state)
        assert isinstance(formatted, bytes)
        assert len(formatted) > 0
        roundtrip = apply_delta(None, formatted, 24, 80)
        assert screen_contents(roundtrip) == screen_contents(state)

    def test_ansi_preserved_in_formatted(self):
        state = apply_delta(None, b"\x1b[1mbold\x1b[0m", 24, 80)
        formatted = screen_contents_formatted(state)
        assert b"bold" in formatted


class TestCursorMovement:
    def test_cursor_home(self):
        state = apply_delta(None, b"overwrite\x1b[Hnew", 24, 80)
        contents = screen_contents(state)
        assert contents.startswith("newrwrite")

    def test_cursor_absolute_position(self):
        state = apply_delta(None, b"\x1b[3;5Hx", 24, 80)
        lines = screen_contents(state).split("\n")
        assert lines[2][4] == "x"

    def test_cursor_up(self):
        state = apply_delta(None, b"first\r\nsecond\x1b[1A\r\ninserted", 24, 80)
        contents = screen_contents(state)
        assert "inserted" in contents
        assert "first" in contents

    def test_cursor_forward_and_back(self):
        # cursor back 3 from end of "abcdef" lands on 'd', forward 1 lands on 'e'
        state = apply_delta(None, b"abcdef\x1b[3D\x1b[1CXYZ", 24, 80)
        contents = screen_contents(state)
        assert "abcdXYZ" in contents


class TestEraseOperations:
    def test_erase_to_end_of_line(self):
        state = apply_delta(None, b"hello world\x1b[H\x1b[5C\x1b[K", 24, 80)
        contents = screen_contents(state)
        assert "hello" in contents
        assert "world" not in contents

    def test_erase_entire_line(self):
        state = apply_delta(None, b"delete me\x1b[2K", 24, 80)
        first_line = screen_contents(state).split("\n")[0]
        assert first_line.strip() == ""

    def test_erase_display(self):
        state = apply_delta(None, b"line1\r\nline2\r\nline3\x1b[2J", 24, 80)
        contents = screen_contents(state)
        assert contents.strip() == ""

    def test_erase_to_beginning_of_line(self):
        state = apply_delta(None, b"hello world\x1b[6G\x1b[1K", 24, 80)
        first_line = screen_contents(state).split("\n")[0]
        assert "world" in first_line
        assert not first_line.startswith("hello")


class TestScrolling:
    def test_scroll_down_past_bottom(self):
        lines = b"".join(f"line{i}\r\n".encode() for i in range(30))
        state = apply_delta(None, lines, 24, 80)
        contents = screen_contents(state)
        assert "line29" in contents
        assert "line0" not in contents

    def test_scroll_region(self):
        state = apply_delta(None, b"\x1b[5;10r\x1b[10;1H\n\n\n", 24, 80)
        assert isinstance(screen_contents(state), str)

    def test_reverse_index_scrolls_up(self):
        state = apply_delta(None, b"\x1b[1;1H\x1bM", 24, 80)
        assert isinstance(screen_contents(state), str)


class TestTextAttributes:
    def test_sgr_bold_in_formatted(self):
        state = apply_delta(None, b"\x1b[1mbold text\x1b[0m", 24, 80)
        formatted = screen_contents_formatted(state)
        assert b"\x1b[1m" in formatted
        assert b"bold text" in formatted

    def test_sgr_colors_in_formatted(self):
        state = apply_delta(None, b"\x1b[31mred\x1b[32mgreen\x1b[0m", 24, 80)
        formatted = screen_contents_formatted(state)
        assert b"red" in formatted
        assert b"green" in formatted

    def test_sgr_256_color(self):
        state = apply_delta(None, b"\x1b[38;5;196mred256\x1b[0m", 24, 80)
        contents = screen_contents(state)
        assert "red256" in contents
        formatted = screen_contents_formatted(state)
        assert b"\x1b[38;5;196m" in formatted

    def test_sgr_24bit_color(self):
        state = apply_delta(None, b"\x1b[38;2;255;0;0mtrue red\x1b[0m", 24, 80)
        contents = screen_contents(state)
        assert "true red" in contents

    def test_multiple_attributes(self):
        state = apply_delta(None, b"\x1b[1;4;31mbold underline red\x1b[0m", 24, 80)
        formatted = screen_contents_formatted(state)
        assert b"bold underline red" in formatted

    def test_reset_clears_attributes(self):
        state = apply_delta(None, b"\x1b[1;31mcolored\x1b[0m plain", 24, 80)
        contents = screen_contents(state)
        assert "colored" in contents
        assert "plain" in contents


class TestTabsAndSpecialChars:
    def test_tab_stop(self):
        state = apply_delta(None, b"a\tb", 24, 80)
        contents = screen_contents(state)
        assert "a" in contents
        assert "b" in contents
        first_line = contents.split("\n")[0]
        assert first_line.index("b") >= 8

    def test_backspace(self):
        state = apply_delta(None, b"ab\x08c", 24, 80)
        contents = screen_contents(state)
        assert "ac" in contents

    def test_carriage_return(self):
        state = apply_delta(None, b"old text\rnew", 24, 80)
        contents = screen_contents(state)
        assert "new" in contents
        assert not contents.startswith("old")


class TestAlternateScreen:
    def test_switch_to_alt_clears(self):
        state = apply_delta(None, b"main screen\x1b[?1049h", 24, 80)
        contents_alt = screen_contents(state)
        assert "main screen" not in contents_alt

    def test_alt_screen_content(self):
        s1 = apply_delta(None, b"\x1b[?1049halt content", 24, 80)
        assert "alt content" in screen_contents(s1)

    def test_switch_to_alt_and_back(self):
        s1 = apply_delta(None, b"main screen\x1b[?1049h", 24, 80)
        s2 = apply_delta(s1, b"\x1b[?1049l", 24, 80)
        assert "main screen" in screen_contents(s2)

    def test_alt_screen_preserves_main_across_deltas(self):
        s1 = apply_delta(None, b"main content\x1b[?1049h", 24, 80)
        s2 = apply_delta(s1, b"alt content", 24, 80)
        assert "alt content" in screen_contents(s2)
        assert "main content" not in screen_contents(s2)
        s3 = apply_delta(s2, b"\x1b[?1049l", 24, 80)
        assert "main content" in screen_contents(s3)

    def test_alt_flag_in_header(self):
        s_main = apply_delta(None, b"hello", 24, 80)
        assert s_main[4] == 0
        s_alt = apply_delta(None, b"\x1b[?1049h", 24, 80)
        assert s_alt[4] == 1


class TestLineWrapping:
    def test_auto_wrap(self):
        state = apply_delta(None, b"x" * 85, 24, 80)
        contents = screen_contents(state)
        assert "x" * 85 in contents

    def test_wrap_preserves_all_chars(self):
        text = b"".join(bytes([ord("a") + (i % 26)]) for i in range(85))
        state = apply_delta(None, text, 24, 80)
        contents = screen_contents(state)
        joined = "".join(line.rstrip() for line in contents.split("\n"))
        assert joined == text.decode()
