"""Prove a subagent policy edit preserves unrelated parsed settings and source text."""

from pathlib import Path
import sys
import tomllib
import unittest
from unittest import mock

sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "lib"))
from codex_config_edit import KEY, updated_config
from codex_python import require_python311


class CodexConfigEditTests(unittest.TestCase):
    def test_models_comments_and_custom_agents_survive(self):
        source = ('# personal preference\nmodel = "chosen"\n[agents]\nmax_threads = 4\n'
                  'max_depth = 2\n[agents.reviewer]\ndescription = "review"\n'
                  '[features]\nmulti_agent_v2 = true\n[mcp_servers.example]\ncommand = "example"\n')
        updated = updated_config(source, 8)
        self.assertEqual(tomllib.loads(updated)["agents"][KEY], 8)
        self.assertNotIn("max_threads", tomllib.loads(updated)["agents"])
        for preserved in ('# personal preference', 'model = "chosen"', 'max_depth = 2',
                          'description = "review"', 'command = "example"'):
            self.assertIn(preserved, updated)
        self.assertEqual(updated_config(updated, 8), updated)

    def test_empty_regular_and_dotted_layouts_are_idempotent(self):
        for source in ("", '[features]\nmulti_agent_v2 = true',
                       'agents.max_threads = 4\nmodel = "chosen"\n',
                       '["agents"] # preference\nmax_concurrent_threads_per_session = 2\n'):
            with self.subTest(source=source):
                updated = updated_config(source, 8)
                self.assertEqual(tomllib.loads(updated)["agents"][KEY], 8)
                self.assertEqual(updated_config(updated, 8), updated)

    def test_conflicting_profile_overrides_are_rejected(self):
        for source in ('[profiles.quick.features.multi_agent_v2]\nmax_concurrent_threads_per_session = 20\n',
                       '[profiles.quick.agents]\nmax_threads = 20\n'):
            with self.subTest(source=source), self.assertRaisesRegex(ValueError, "overrides"):
                updated_config(source, 8)
        source = '[features.multi_agent_v2]\nmax_concurrent_threads_per_session = 8\n'
        self.assertEqual(tomllib.loads(updated_config(source, 8))["agents"][KEY], 8)

    def test_existing_eight_thread_policies_and_feature_overrides_change_to_seven(self):
        for feature in ('[features]\nmulti_agent_v2 = true\n',
                        '[features]\nmulti_agent_v2 = false\n',
                        '[features.multi_agent_v2]\nenabled = true\nmax_concurrent_threads_per_session = 20\n',
                        'features.multi_agent_v2.max_concurrent_threads_per_session = 8\n',
                        '[features]\nmulti_agent_v2.max_concurrent_threads_per_session = 8\n'):
            source = feature + '[agents]\nmax_concurrent_threads_per_session = 8\n'
            with self.subTest(feature=feature):
                updated = updated_config(source, 7)
                document = tomllib.loads(updated)
                self.assertEqual(document["agents"][KEY], 7)
                self.assertEqual(document["features"]["multi_agent_v2"][KEY], 7)
                if 'false' in feature:
                    self.assertFalse(document["features"]["multi_agent_v2"]["enabled"])
                self.assertEqual(updated_config(updated, 7), updated)

    def test_unsupported_layouts_and_string_lookalikes_fail_safely(self):
        for source in ('agents = { max_threads = 4 }\n',
                       'instructions = """\n[agents]\nmax_threads = 99\n"""\n'):
            with self.subTest(source=source), self.assertRaisesRegex(ValueError, "unsupported"):
                updated_config(source, 8)


class CodexPythonTests(unittest.TestCase):
    def test_explicit_modern_python_is_preserved(self):
        with mock.patch("codex_python.sys.version_info", (3, 12)), \
                mock.patch("codex_python.subprocess.run") as probe:
            require_python311("settings.py")
        probe.assert_not_called()

    def test_old_python_reexecutes_available_modern_interpreter_with_same_args(self):
        with mock.patch("codex_python.sys.version_info", (3, 9)), \
                mock.patch("codex_python.shutil.which", return_value="/installed/python3.12"), \
                mock.patch("codex_python.os.access", return_value=True), \
                mock.patch("codex_python.sys.argv", ["settings.py", "configure", "pool", "--check"]), \
                mock.patch("codex_python.subprocess.run", return_value=mock.Mock(returncode=0)) as probe, \
                mock.patch("codex_python.os.execv", side_effect=SystemExit(42)) as replace:
            with self.assertRaises(SystemExit):
                require_python311("settings.py")
        self.assertEqual(probe.call_args.kwargs["timeout"], 3)
        replace.assert_called_once_with("/installed/python3.12",
                                        ["/installed/python3.12", "settings.py", "configure", "pool", "--check"])

    def test_missing_modern_python_fails_without_downloads(self):
        with mock.patch("codex_python.sys.version_info", (3, 9)), \
                mock.patch("codex_python.shutil.which", return_value=None), \
                mock.patch("codex_python.os.access", return_value=False), \
                mock.patch("codex_python.subprocess.run") as probe:
            with self.assertRaisesRegex(SystemExit, "requires an installed Python 3.11"):
                require_python311("settings.py")
        probe.assert_not_called()


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