import unittest
from pathlib import Path
import sys
import tempfile
from unittest import mock

sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))

from livemcp import ableton


class QuitAbletonTests(unittest.TestCase):
    @mock.patch("livemcp.ableton.time.sleep", return_value=None)
    @mock.patch("livemcp.ableton.wait_for_dialog", return_value=None)
    @mock.patch("livemcp.ableton._dismiss_quit_dialog_via_livemcp", return_value=False)
    @mock.patch("livemcp.ableton.is_process_running", side_effect=[True, False])
    @mock.patch("livemcp.ableton._launch_osascript")
    @mock.patch("livemcp.ableton.find_ableton_app", return_value="Ableton Live 12 Suite")
    def test_quit_ableton_launches_quit_script_asynchronously(
        self,
        _find_ableton_app,
        launch_osascript,
        _is_process_running,
        dismiss_quit_dialog,
        wait_for_dialog,
        _sleep,
    ):
        ableton.quit_ableton(force=True)

        launch_osascript.assert_called_once_with('tell application "Ableton Live 12 Suite" to quit')
        dismiss_quit_dialog.assert_called()
        wait_for_dialog.assert_called()

    def test_find_remote_script_dir_prefers_packaged_layout(self):
        with tempfile.TemporaryDirectory() as temp_dir:
            temp_path = Path(temp_dir)
            package_dir = temp_path / "package"
            repo_root = temp_path / "repo"
            packaged_remote_script = package_dir / "remote_script"
            repo_remote_script = repo_root / "remote_script"
            packaged_remote_script.mkdir(parents=True)
            repo_remote_script.mkdir(parents=True)

            found = ableton._find_remote_script_dir(repo_root=repo_root, package_dir=package_dir)

        self.assertEqual(found, packaged_remote_script)

    def test_find_ableton_preferences_dir_prefers_matching_app_version(self):
        with tempfile.TemporaryDirectory() as temp_dir:
            prefs_root = Path(temp_dir)
            latest_other = prefs_root / "Live 12.3.1"
            matching = prefs_root / "Live 11.3.25"
            older_matching = prefs_root / "Live 11.2.10"
            for path in (latest_other, matching, older_matching):
                path.mkdir(parents=True)

            found = ableton.find_ableton_preferences_dir(
                app_name="Ableton Live 11 Suite",
                prefs_root=prefs_root,
            )

        self.assertEqual(found, matching)

    @mock.patch(
        "livemcp.installer.get_install_status",
        return_value={
            "ableton_found": True,
            "source_found": True,
            "installed": False,
            "in_sync": False,
            "needs_install": True,
        },
    )
    @mock.patch("builtins.print")
    def test_warn_if_install_outdated_prints_when_missing(self, print_mock, _get_install_status):
        with mock.patch("livemcp.installer.install") as install:
            ableton._sync_install_if_needed()

        install.assert_called_once_with(use_symlink=False)
        print_mock.assert_called_once()

    @mock.patch("livemcp.ableton.time.sleep", return_value=None)
    @mock.patch("livemcp.ableton.time.time", side_effect=[0, 16])
    @mock.patch("livemcp.ableton.is_process_running", return_value=True)
    @mock.patch("livemcp.ableton._launch_osascript")
    @mock.patch("livemcp.ableton.find_ableton_app", return_value="Ableton Live 12 Suite")
    def test_quit_ableton_raises_when_not_forced_and_process_still_running(
        self,
        _find_ableton_app,
        _launch_osascript,
        _is_process_running,
        _time,
        _sleep,
    ):
        with self.assertRaises(RuntimeError) as context:
            ableton.quit_ableton(force=False)

        self.assertIn("did not exit", str(context.exception))


if __name__ == "__main__":
    unittest.main()
