from __future__ import annotations

import json
from pathlib import Path

import pytest

from revibe.cli.history_manager import HistoryManager
from revibe.cli.textual_ui.app import VibeApp
from revibe.cli.textual_ui.widgets.chat_input.body import ChatInputBody
from revibe.cli.textual_ui.widgets.chat_input.container import ChatInputContainer
from revibe.core.config import SessionLoggingConfig, VibeConfig


@pytest.fixture
def vibe_config() -> VibeConfig:
    return VibeConfig(session_logging=SessionLoggingConfig(enabled=False))


@pytest.fixture
def vibe_app(vibe_config: VibeConfig, tmp_path: Path) -> VibeApp:
    return VibeApp(config=vibe_config)


@pytest.fixture
def history_file(tmp_path: Path) -> Path:
    history_file = tmp_path / "history.jsonl"
    history_entries = ["hello", "hi there", "how are you?"]
    history_file.write_text(
        "\n".join(json.dumps(entry) for entry in history_entries) + "\n",
        encoding="utf-8",
    )
    return history_file


def inject_history_file(vibe_app: VibeApp, history_file: Path) -> None:
    # Dependency Injection would help here, but as we don't have it yet: manual injection
    chat_input_body = vibe_app.query_one(ChatInputBody)
    chat_input_body.history = HistoryManager(history_file)


@pytest.mark.asyncio
async def test_ui_navigation_through_input_history(
    vibe_app: VibeApp, history_file: Path
) -> None:
    async with vibe_app.run_test() as pilot:
        inject_history_file(vibe_app, history_file)
        chat_input = vibe_app.query_one(ChatInputContainer)

        await pilot.press("up")
        assert chat_input.value == "how are you?"
        await pilot.press("up")
        assert chat_input.value == "hi there"
        await pilot.press("up")
        assert chat_input.value == "hello"
        await pilot.press("up")
        # cannot go further up
        assert chat_input.value == "hello"
        await pilot.press("down")
        assert chat_input.value == "hi there"
        await pilot.press("down")
        assert chat_input.value == "how are you?"
        await pilot.press("down")
        assert chat_input.value == ""


@pytest.mark.asyncio
async def test_ui_does_nothing_if_command_completion_is_active(
    vibe_app: VibeApp, history_file: Path
) -> None:
    async with vibe_app.run_test() as pilot:
        inject_history_file(vibe_app, history_file)
        chat_input = vibe_app.query_one(ChatInputContainer)

        await pilot.press("/")
        assert chat_input.value == "/"
        await pilot.press("up")
        assert chat_input.value == "/"
        await pilot.press("down")
        assert chat_input.value == "/"


@pytest.mark.asyncio
async def test_ui_does_not_prevent_arrow_down_to_move_cursor_to_bottom_lines(
    vibe_app: VibeApp,
):
    async with vibe_app.run_test() as pilot:
        chat_input = vibe_app.query_one(ChatInputContainer)
        textarea = chat_input.input_widget
        assert textarea is not None

        await pilot.press(*"test")
        await pilot.press("ctrl+j", "ctrl+j")
        assert chat_input.value == "test\n\n"
        assert textarea.text.count("\n") == 2
        initial_row = textarea.cursor_location[0]
        assert initial_row == 2, f"Expected cursor on line 2, got line {initial_row}"
        await pilot.press("up")
        assert textarea.cursor_location[0] == 1, "First arrow up should move to line 1"
        await pilot.press("up")
        assert textarea.cursor_location[0] == 0, (
            "Second arrow up should move to line 0 (first line)"
        )
        await pilot.press("down")
        final_row = textarea.cursor_location[0]
        assert final_row == 1, f"cursor is still on line {final_row}."


@pytest.mark.asyncio
async def test_ui_resumes_arrow_down_after_manual_move(
    vibe_app: VibeApp, tmp_path: Path
) -> None:
    history_path = tmp_path / "history.jsonl"
    history_path.write_text(
        json.dumps("first line\nsecond line") + "\n", encoding="utf-8"
    )

    async with vibe_app.run_test() as pilot:
        inject_history_file(vibe_app, history_path)
        chat_input = vibe_app.query_one(ChatInputContainer)
        textarea = chat_input.input_widget
        assert textarea is not None

        await pilot.press("up")
        assert chat_input.value == "first line\nsecond line"
        assert textarea.cursor_location == (0, len("first line"))
        await pilot.press("left")
        await pilot.press("down")
        assert textarea.cursor_location[0] == 1
        assert chat_input.value == "first line\nsecond line"
