# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import os
import sys
import unittest
from unittest.mock import MagicMock, patch

import buildconfig
from mach.registrar import Registrar
from mozunit import main

sys.path.insert(0, os.path.join(buildconfig.topsrcdir, "tools"))


class TestDevtoolsMcp(unittest.TestCase):
    def setUp(self):
        self.remove_cats = []
        for cat in ("build", "post-build", "misc", "testing", "devenv"):
            if cat in Registrar.categories:
                continue
            Registrar.register_category(cat, cat, cat)
            self.remove_cats.append(cat)

    def tearDown(self):
        for cat in self.remove_cats:
            del Registrar.categories[cat]
            del Registrar.commands_by_category[cat]

    def _mock_context(self, topobjdir="/obj", binary_path="/obj/dist/bin/firefox"):
        ctx = MagicMock()
        ctx.topobjdir = topobjdir
        ctx.get_binary_path.return_value = binary_path
        return ctx

    @patch("os.path.exists", return_value=True)
    @patch("mach_commands.npx")
    def test_delegates_to_npx_with_build_args(self, mock_npx, mock_exists):
        from mach_commands import devtools_mcp

        mock_context = self._mock_context()
        mock_npx.return_value = 0

        devtools_mcp(mock_context, args=["--pref", "x=1"])

        mock_context.get_binary_path.assert_called_once_with(validate_exists=True)
        mock_npx.assert_called_once_with(
            mock_context,
            [
                "@mozilla/firefox-devtools-mcp-moz",
                "--firefox-path",
                "/obj/dist/bin/firefox",
                "--profile-path",
                "/obj/tmp/profile-default",
                "--pref",
                "x=1",
            ],
        )

    @patch("mach_commands.npx")
    def test_starts_without_binary_when_no_build(self, mock_npx):
        from mach_commands import devtools_mcp

        from mozbuild.base import BinaryNotFoundException

        mock_context = self._mock_context()
        mock_context.get_binary_path.side_effect = BinaryNotFoundException(
            "/obj/dist/bin/firefox"
        )
        mock_npx.return_value = 0

        result = devtools_mcp(mock_context, args=[])

        self.assertEqual(result, 0)
        mock_npx.assert_called_once_with(
            mock_context,
            ["@mozilla/firefox-devtools-mcp-moz"],
        )

    @patch("mach_commands.npx")
    def test_starts_without_binary_when_no_build_environment(self, mock_npx):
        from mach_commands import devtools_mcp

        from mozbuild.base import BuildEnvironmentNotFoundException

        mock_context = self._mock_context()
        mock_context.get_binary_path.side_effect = BuildEnvironmentNotFoundException(
            "no build environment"
        )
        mock_npx.return_value = 0

        result = devtools_mcp(mock_context, args=[])

        self.assertEqual(result, 0)
        mock_npx.assert_called_once_with(
            mock_context,
            ["@mozilla/firefox-devtools-mcp-moz"],
        )

    @patch("os.path.exists", return_value=True)
    @patch("mach_commands.npx")
    def test_works_with_no_extra_args(self, mock_npx, mock_exists):
        from mach_commands import devtools_mcp

        mock_context = self._mock_context()
        mock_npx.return_value = 0

        devtools_mcp(mock_context, args=[])

        mock_npx.assert_called_once_with(
            mock_context,
            [
                "@mozilla/firefox-devtools-mcp-moz",
                "--firefox-path",
                "/obj/dist/bin/firefox",
                "--profile-path",
                "/obj/tmp/profile-default",
            ],
        )

    @patch("os.path.exists", return_value=True)
    @patch("mach_commands.npx")
    def test_profile_path_derives_from_topobjdir(self, mock_npx, mock_exists):
        from mach_commands import devtools_mcp

        mock_context = self._mock_context(
            topobjdir="/builds/objdir",
            binary_path="/somewhere/else/dist/bin/firefox",
        )
        mock_npx.return_value = 0

        devtools_mcp(mock_context, args=[])

        call_args = mock_npx.call_args[0][1]
        profile_idx = call_args.index("--profile-path")
        self.assertEqual(
            call_args[profile_idx + 1], "/builds/objdir/tmp/profile-default"
        )

    @patch("os.path.exists", return_value=True)
    @patch("mach_commands.npx")
    def test_skips_binary_detection_when_firefox_path_provided(
        self, mock_npx, mock_exists
    ):
        from mach_commands import devtools_mcp

        mock_context = self._mock_context()
        mock_npx.return_value = 0

        devtools_mcp(mock_context, args=["--firefox-path", "/custom/firefox"])

        mock_context.get_binary_path.assert_not_called()
        mock_npx.assert_called_once_with(
            mock_context,
            ["@mozilla/firefox-devtools-mcp-moz", "--firefox-path", "/custom/firefox"],
        )


if __name__ == "__main__":
    main()
