import pytest
from easyansi import cursor as cur
from easyansi._core.codes import CSI


####################################################################################################
# up, up_code
####################################################################################################


def test_up_code_valid_rows():
    expected_value = CSI + "2A"
    actual_value = cur.up_code(2)
    assert expected_value == actual_value


def test_up_code_default_rows():
    expected_value = CSI + "1A"
    actual_value = cur.up_code()
    assert expected_value == actual_value


def test_up_invalid_rows():
    expected_msg_contains = "Rows up"
    with pytest.raises(ValueError) as vl_error:
        cur.up(cur.MIN_MOVEMENT - 1)
    assert expected_msg_contains in str(vl_error.value)


def test_up_and_up_code(capsys):
    value = 2
    actual_code = cur.up_code(value)
    cur.up(value)
    captured = capsys.readouterr()
    assert actual_code == captured.out


####################################################################################################
# down, down_code
####################################################################################################


def test_down_code_valid_rows():
    expected_value = CSI + "3B"
    actual_value = cur.down_code(3)
    assert expected_value == actual_value


def test_down_code_default_rows():
    expected_value = CSI + "1B"
    actual_value = cur.down_code()
    assert expected_value == actual_value


def test_down_invalid_rows():
    expected_msg_contains = "Rows down"
    with pytest.raises(ValueError) as vl_error:
        cur.down(cur.MIN_MOVEMENT - 1)
    assert expected_msg_contains in str(vl_error.value)


def test_down_and_down_code(capsys):
    value = 3
    actual_code = cur.down_code(value)
    cur.down(value)
    captured = capsys.readouterr()
    assert actual_code == captured.out


####################################################################################################
# left, left_code
####################################################################################################


def test_left_code_valid_cols():
    expected_value = CSI + "5D"
    actual_value = cur.left_code(5)
    assert expected_value == actual_value


def test_left_code_default_cols():
    expected_value = CSI + "1D"
    actual_value = cur.left_code()
    assert expected_value == actual_value


def test_left_invalid_rows():
    expected_msg_contains = "Columns left"
    with pytest.raises(ValueError) as vl_error:
        cur.left(cur.MIN_MOVEMENT - 1)
    assert expected_msg_contains in str(vl_error.value)


def test_left_and_left_code(capsys):
    value = 4
    actual_code = cur.left_code(value)
    cur.left(value)
    captured = capsys.readouterr()
    assert actual_code == captured.out


####################################################################################################
# right, right_code
####################################################################################################


def test_right_code_valid_cols():
    expected_value = CSI + "4C"
    actual_value = cur.right_code(4)
    assert expected_value == actual_value


def test_right_code_default_cols():
    expected_value = CSI + "1C"
    actual_value = cur.right_code()
    assert expected_value == actual_value


def test_right_invalid_cols():
    expected_msg_contains = "Columns right"
    with pytest.raises(ValueError) as vl_error:
        cur.right(cur.MIN_MOVEMENT - 1)
    assert expected_msg_contains in str(vl_error.value)


def test_right_and_right_code(capsys):
    value = 5
    actual_code = cur.right_code(value)
    cur.right(value)
    captured = capsys.readouterr()
    assert actual_code == captured.out


####################################################################################################
# next_line, next_line_code
####################################################################################################


def test_next_line_code_valid_rows():
    expected_value = CSI + "9E"
    actual_value = cur.next_line_code(9)
    assert expected_value == actual_value


def test_next_line_code_default_rows():
    expected_value = CSI + "1E"
    actual_value = cur.next_line_code()
    assert expected_value == actual_value


def test_next_line_invalid_rows():
    expected_msg_contains = "Rows down"
    with pytest.raises(ValueError) as vl_error:
        cur.next_line(cur.MIN_MOVEMENT - 1)
    assert expected_msg_contains in str(vl_error.value)


def test_next_line_and_next_line_code(capsys):
    value = 8
    actual_code = cur.next_line_code(value)
    cur.next_line(value)
    captured = capsys.readouterr()
    assert actual_code == captured.out


####################################################################################################
# previous_line, previous_line_code
####################################################################################################


def test_previous_line_code_valid_rows():
    expected_value = CSI + "7F"
    actual_value = cur.previous_line_code(7)
    assert expected_value == actual_value


def test_previous_line_code_default_rows():
    expected_value = CSI + "1F"
    actual_value = cur.previous_line_code()
    assert expected_value == actual_value


def test_previous_line_invalid_rows():
    expected_msg_contains = "Rows up"
    with pytest.raises(ValueError) as vl_error:
        cur.previous_line(cur.MIN_MOVEMENT - 1)
    assert expected_msg_contains in str(vl_error.value)


def test_previous_line_and_previous_line_code(capsys):
    value = 2
    actual_code = cur.previous_line_code(value)
    cur.previous_line(value)
    captured = capsys.readouterr()
    assert actual_code == captured.out


####################################################################################################
# locate, locate_code
####################################################################################################


def test_locate_code_valid_values():
    expected_value = CSI + "11;16H"
    actual_value = cur.locate_code(15, 10)
    assert expected_value == actual_value


@pytest.mark.parametrize(
    "x,y,expected_msg_contains",
    [(cur.MIN_COLUMN - 1, cur.MIN_ROW, "X-Coordinate"),
     (cur.MIN_COLUMN, cur.MIN_ROW - 1, "Y-Coordinate")
    ])
def test_locate_invalid_values(x, y, expected_msg_contains):
    with pytest.raises(ValueError) as vl_error:
        cur.locate(x, y)
    assert expected_msg_contains in str(vl_error.value)


def test_locate_and_locate_code(capsys):
    x = 20
    y = 15
    actual_code = cur.locate_code(x, y)
    cur.locate(x, y)
    captured = capsys.readouterr()
    assert actual_code == captured.out


####################################################################################################
# locate_column, locate_column_code
####################################################################################################


def test_locate_column_code_valid_values():
    expected_value = CSI + "71G"
    actual_value = cur.locate_column_code(70)
    assert expected_value == actual_value


def test_locate_column_invalid_values():
    expected_msg_contains = "Column Number"
    with pytest.raises(ValueError) as vl_error:
        cur.locate_column(cur.MIN_COLUMN - 1)
    assert expected_msg_contains in str(vl_error.value)


def test_locate_column_and_locate_column_code(capsys):
    x = 70
    actual_code = cur.locate_column_code(x)
    cur.locate_column(x)
    captured = capsys.readouterr()
    assert actual_code == captured.out


####################################################################################################
# hide, hide_code, show, show_code
####################################################################################################


def test_hide_code():
    expected_value = CSI + "?25l"
    actual_value = cur.hide_code()
    assert expected_value == actual_value


def test_hide_and_hide_code(capsys):
    actual_code = cur.hide_code()
    cur.hide()
    captured = capsys.readouterr()
    assert actual_code == captured.out


def test_show_code():
    expected_value = CSI + "?25h"
    actual_value = cur.show_code()
    assert expected_value == actual_value


def test_show_and_show_code(capsys):
    actual_code = cur.show_code()
    cur.show()
    captured = capsys.readouterr()
    assert actual_code == captured.out


####################################################################################################
# get_location
####################################################################################################


def test_get_location(monkeypatch):
    # Mock the function that makes low-level terminal calls
    def mock_get_location_response():
        return "40;20"  # y;x on 1-based indexes
    monkeypatch.setattr(cur, "_get_location_response", mock_get_location_response)
    # Run the test
    x, y = cur.get_location()
    assert x == 19
    assert y == 39
