import threading
import time
from unittest.mock import MagicMock, patch

import typer

from rendercv.cli.render_command import watcher


class TestRunFunctionIfFileChanges:
    def test_calls_function_immediately_on_start(self, tmp_path):
        file_path = tmp_path / "test.yaml"
        file_path.touch()
        mock_function = MagicMock()

        with (
            patch.object(watcher.watchdog.observers, "Observer"),
            patch.object(watcher.time, "sleep", side_effect=KeyboardInterrupt),
        ):
            watcher.run_function_if_file_changes(file_path, mock_function)

        mock_function.assert_called_once()

    def test_calls_function_when_file_changes(self, tmp_path):
        watched_file = tmp_path / "test.yaml"
        watched_file.write_text("initial")

        call_count = 0

        def tracked_function():
            nonlocal call_count
            call_count += 1

        watcher_thread = threading.Thread(
            target=watcher.run_function_if_file_changes,
            args=(watched_file, tracked_function),
            daemon=True,
        )
        watcher_thread.start()

        time.sleep(0.2)
        initial_count = call_count

        watched_file.write_text("first edit")
        time.sleep(0.2)

        assert call_count > initial_count

    def test_continues_running_after_function_raises_typer_exit(self, tmp_path):
        watched_file = tmp_path / "test.yaml"
        watched_file.write_text("initial")

        call_count = 0
        should_raise = False

        def tracked_function():
            nonlocal call_count
            call_count += 1
            if should_raise:
                raise typer.Exit(code=1)

        watcher_thread = threading.Thread(
            target=watcher.run_function_if_file_changes,
            args=(watched_file, tracked_function),
            daemon=True,
        )
        watcher_thread.start()

        time.sleep(0.2)
        should_raise = True
        count_before_exit = call_count
        watched_file.write_text("edit that raises exit")
        time.sleep(0.2)

        assert call_count > count_before_exit

        should_raise = False
        count_after_exit = call_count
        watched_file.write_text("edit after exit")
        time.sleep(0.2)

        assert call_count > count_after_exit
