import time
from unittest.mock import Mock

import pytest

from fenrirscreenreader.core.screenManager import ScreenManager


def _build_screen_manager(old_text, old_cursor):
    manager = ScreenManager()
    env = {
        "runtime": {
            "AttributeManager": Mock(
                reset_attributes=Mock(),
                reset_attribute_cursor=Mock(),
                set_attributes=Mock(),
                reset_attribute_delta=Mock(),
                is_attribute_change=Mock(return_value=False),
            ),
            "CursorManager": Mock(is_application_window_set=Mock(return_value=False)),
            "DebugManager": Mock(write_debug_out=Mock()),
            "InputManager": Mock(get_last_input_time=Mock(return_value=0)),
            "ScreenManager": manager,
            "SettingsManager": Mock(get_setting_as_bool=Mock(return_value=False)),
        },
        "screen": {
            "newContentBytes": b"",
            "oldContentBytes": b"",
            "new_content_text": old_text,
            "old_content_text": "",
            "new_cursor": old_cursor.copy(),
            "old_cursor": {"x": 0, "y": 0},
            "new_delta": "",
            "new_delta_is_typing": False,
            "oldDelta": "",
            "newNegativeDelta": "",
            "oldNegativeDelta": "",
            "oldTTY": "pty",
            "newTTY": "pty",
        },
    }
    manager.initialize = Mock()
    manager.env = env
    return manager, env


@pytest.mark.unit
def test_prompt_repaint_during_typing_keeps_only_typed_delta():
    manager, env = _build_screen_manager("[0] ", {"x": 4, "y": 0})

    manager.update(
        {
            "bytes": b"[1] h",
            "lines": 1,
            "columns": 20,
            "textCursor": {"x": 5, "y": 0},
            "screen": "pty",
            "text": "[1] h",
            "attributes": [],
        },
        "onScreenUpdate",
    )

    assert env["screen"]["new_delta"] == "h"
    assert env["screen"]["new_delta_is_typing"] is True


@pytest.mark.unit
def test_tui_input_line_append_is_typing_delta():
    manager, env = _build_screen_manager(
        "\n".join(
            [
                "<UserA> hi".ljust(20),
                "[Username] ".ljust(20),
            ]
        ),
        {"x": 11, "y": 1},
    )
    env["runtime"]["InputManager"].get_last_input_time.return_value = time.time()

    manager.update(
        {
            "bytes": b"",
            "lines": 2,
            "columns": 20,
            "textCursor": {"x": 12, "y": 1},
            "screen": "pty",
            "text": "\n".join(
                [
                    "<UserA> hi".ljust(20),
                    "[Username] l".ljust(20),
                ]
            ),
            "attributes": [],
        },
        "onScreenUpdate",
    )

    assert env["screen"]["new_delta"] == "l"
    assert env["screen"]["new_delta_is_typing"] is True


@pytest.mark.unit
def test_tui_input_line_cursor_jump_keeps_only_typed_delta():
    manager, env = _build_screen_manager(
        "[Username] ".ljust(30),
        {"x": 0, "y": 0},
    )

    manager.update(
        {
            "bytes": b"",
            "lines": 1,
            "columns": 30,
            "textCursor": {"x": 12, "y": 0},
            "screen": "pty",
            "text": "[Username] l".ljust(30),
            "attributes": [],
        },
        "onScreenUpdate",
    )

    assert env["screen"]["new_delta"] == "l"
    assert env["screen"]["new_delta_is_typing"] is True


@pytest.mark.unit
def test_tui_input_line_insert_with_channel_prefix_is_typing_delta():
    manager, env = _build_screen_manager(
        "\n".join(
            [
                "<UserA> hi".ljust(40),
                "[#channel] [Username]     | 12:00".ljust(40),
            ]
        ),
        {"x": 22, "y": 1},
    )
    env["runtime"]["InputManager"].get_last_input_time.return_value = time.time()

    manager.update(
        {
            "bytes": b"",
            "lines": 2,
            "columns": 40,
            "textCursor": {"x": 23, "y": 1},
            "screen": "pty",
            "text": "\n".join(
                [
                    "<UserA> hi".ljust(40),
                    "[#channel] [Username] l   | 12:00".ljust(40),
                ]
            ),
            "attributes": [],
        },
        "onScreenUpdate",
    )

    assert env["screen"]["new_delta"] == "l"
    assert env["screen"]["new_delta_is_typing"] is True


@pytest.mark.unit
def test_tui_input_line_typing_is_filtered_from_mixed_repaint_delta():
    manager, env = _build_screen_manager(
        "\n".join(
            [
                "<UserA> hi".ljust(40),
                "[#channel] [Username]     | 12:00".ljust(40),
                "status 12:00".ljust(40),
            ]
        ),
        {"x": 22, "y": 1},
    )
    env["runtime"]["InputManager"].get_last_input_time.return_value = time.time()

    manager.update(
        {
            "bytes": b"",
            "lines": 3,
            "columns": 40,
            "textCursor": {"x": 23, "y": 1},
            "screen": "pty",
            "text": "\n".join(
                [
                    "<UserA> hi".ljust(40),
                    "[#channel] [Username] l   | 12:00".ljust(40),
                    "status 12:01".ljust(40),
                ]
            ),
            "attributes": [],
        },
        "onScreenUpdate",
    )

    assert "Username" not in env["screen"]["new_delta"]
    assert "#channel" not in env["screen"]["new_delta"]
    assert env["screen"]["new_delta_is_typing"] is False
