from unittest import TestCase
from unittest.mock import MagicMock, patch

from click.testing import CliRunner

from recce.cli import run as cli_command_run
from recce.cli import server as cli_command_server
from recce.cli import snapshot as cli_command_snapshot
from recce.cli import upload_session as cli_command_upload_session
from recce.core import RecceContext
from recce.state import CloudStateLoader


def test_cmd_version():
    from recce import __version__
    from recce.cli import version

    runner = CliRunner()
    result = runner.invoke(version, [])
    assert result.exit_code == 0
    assert result.output.replace("\n", "") == __version__


class TestCommandServer(TestCase):
    def setUp(self):
        self.runner = CliRunner()
        pass

    @patch.object(RecceContext, "verify_required_artifacts")
    @patch("recce.cli.uvicorn.run")
    def test_cmd_server(self, mock_run, mock_verify_required_artifacts):
        from recce.server import app

        mock_verify_required_artifacts.return_value = True, None
        self.runner.invoke(cli_command_server, ["--host", "unittest", "--port", 5566, "--single-env"])
        mock_run.assert_called_once_with(app, host="unittest", port=5566, lifespan="on")

    @patch("recce.cli.uvicorn.run")
    def test_cmd_server_with_cloud_without_password(self, mock_run):
        # Should fail if no password is provided
        result = self.runner.invoke(cli_command_server, ["--cloud"])
        assert result.exit_code == 1

    @patch("recce.cli.uvicorn.run")
    def test_cmd_server_with_cloud_without_token(self, mock_run):
        # Should fail if no token is provided
        result = self.runner.invoke(cli_command_server, ["--cloud", "--password", "unittest"])
        assert result.exit_code == 1

    @patch.object(RecceContext, "verify_required_artifacts")
    @patch("recce.util.recce_cloud.get_recce_cloud_onboarding_state")
    @patch("recce.cli.uvicorn.run")
    @patch("recce.cli.CloudStateLoader")
    def test_cmd_server_with_cloud(
        self, mock_state_loader_class, mock_run, mock_get_recce_cloud_onboarding_state, mock_verify_required_artifacts
    ):
        mock_state_loader = MagicMock(spec=CloudStateLoader)
        mock_state_loader.verify.return_value = True
        mock_state_loader.review_mode = True
        mock_get_recce_cloud_onboarding_state.return_value = "completed"
        mock_verify_required_artifacts.return_value = True, None

        mock_state_loader_class.return_value = mock_state_loader
        self.runner.invoke(
            cli_command_server, ["--cloud", "--password", "unittest", "--cloud-token", "unittest", "--single-env"]
        )
        mock_state_loader_class.assert_called_once()
        mock_run.assert_called_once()

    @patch.object(RecceContext, "verify_required_artifacts")
    @patch("recce.util.recce_cloud.get_recce_cloud_onboarding_state")
    @patch("recce.cli.uvicorn.run")
    @patch("recce.cli.CloudStateLoader")
    @patch("recce.cli.prepare_api_token", return_value="test_api_token")
    def test_cmd_server_with_session_id(
        self,
        mock_prepare_api_token,
        mock_state_loader_class,
        mock_run,
        mock_get_recce_cloud_onboarding_state,
        mock_verify_required_artifacts,
    ):
        """Test that --session-id automatically enables cloud and review mode"""
        mock_state_loader = MagicMock(spec=CloudStateLoader)
        mock_state_loader.verify.return_value = True
        mock_state_loader.review_mode = True
        mock_get_recce_cloud_onboarding_state.return_value = "completed"
        mock_verify_required_artifacts.return_value = True, None

        mock_state_loader_class.return_value = mock_state_loader

        # Test with --session-id (should automatically enable cloud and review)
        result = self.runner.invoke(cli_command_server, ["--session-id", "test-session-123", "--single-env"])

        # Should succeed
        assert result.exit_code == 0

        # Should create CloudStateLoader with session_id in cloud_options
        mock_state_loader_class.assert_called_once()
        call_args = mock_state_loader_class.call_args
        assert call_args.kwargs["review_mode"] is True
        assert "session_id" in call_args.kwargs["cloud_options"]
        assert call_args.kwargs["cloud_options"]["session_id"] == "test-session-123"

        mock_run.assert_called_once()

    @patch.object(RecceContext, "verify_required_artifacts")
    @patch("recce.util.recce_cloud.get_recce_cloud_onboarding_state")
    @patch("recce.cli.uvicorn.run")
    @patch("recce.cli.CloudStateLoader")
    @patch("recce.cli.prepare_api_token", return_value="test_api_token")
    def test_cmd_server_with_share_url(
        self,
        mock_prepare_api_token,
        mock_state_loader_class,
        mock_run,
        mock_get_recce_cloud_onboarding_state,
        mock_verify_required_artifacts,
    ):
        """Test that --share-url automatically enables cloud and review mode"""
        mock_state_loader = MagicMock(spec=CloudStateLoader)
        mock_state_loader.verify.return_value = True
        mock_state_loader.review_mode = True
        mock_get_recce_cloud_onboarding_state.return_value = "completed"
        mock_verify_required_artifacts.return_value = True, None

        mock_state_loader_class.return_value = mock_state_loader

        # Test with --share-url (should automatically enable cloud and review)
        result = self.runner.invoke(
            cli_command_server, ["--share-url", "https://cloud.recce.io/share/abc123", "--single-env"]
        )

        # Should succeed
        assert result.exit_code == 0

        # Should create CloudStateLoader with share_id in cloud_options
        mock_state_loader_class.assert_called_once()
        call_args = mock_state_loader_class.call_args
        assert call_args.kwargs["review_mode"] is True
        assert "share_id" in call_args.kwargs["cloud_options"]
        assert call_args.kwargs["cloud_options"]["share_id"] == "abc123"

        mock_run.assert_called_once()

    @patch.object(RecceContext, "verify_required_artifacts")
    @patch("os.path.isdir", side_effect=lambda path: True if path == "existed_folder" else False)
    @patch("recce.cli.uvicorn.run")
    @patch("recce.server.AppState")
    def test_cmd_server_with_single_env(self, mock_app_state, mock_run, mock_isdir, mock_verify_required_artifacts):
        mock_verify_required_artifacts.return_value = True, None
        self.runner.invoke(
            cli_command_server,
            ["--single-env", "--target-path", "existed_folder", "--target-base-path", "non_existed_folder"],
        )
        mock_run.assert_called_once()

        # Onboarding mode should be set to True
        app_state_call_args = mock_app_state.call_args
        app_state_flag = app_state_call_args.kwargs["flag"]
        assert "single_env_onboarding" in app_state_flag
        assert app_state_flag["single_env_onboarding"] is True
        assert "show_relaunch_hint" in app_state_flag
        assert app_state_flag["show_relaunch_hint"] is True

        # The target_base_path should be set to the same as target_path
        verify_required_artifacts_args = mock_verify_required_artifacts.call_args
        assert (
            verify_required_artifacts_args.kwargs["target_path"]
            == verify_required_artifacts_args.kwargs["target_base_path"]
        )

    @patch.object(RecceContext, "verify_required_artifacts")
    @patch("os.path.isdir", side_effect=lambda path: True if path == "existed_folder" else False)
    @patch("recce.cli.uvicorn.run")
    @patch("recce.server.AppState")
    def test_cmd_server_with_single_env_but_review_mode_enabled(
        self, mock_app_state, mock_run, mock_isdir, mock_verify_required_artifacts
    ):
        mock_verify_required_artifacts.return_value = True, None
        self.runner.invoke(
            cli_command_server,
            [
                "existed_state_file",
                "--review",
                "--single-env",
                "--target-path",
                "existed_folder",
                "--target-base-path",
                "non_existed_folder",
            ],
        )
        mock_run.assert_called_once()
        app_state_call_args = mock_app_state.call_args
        app_state_flag = app_state_call_args.kwargs["flag"]
        assert "single_env_onboarding" in app_state_flag
        assert app_state_flag["single_env_onboarding"] is False


class TestCommandRun(TestCase):
    def setUp(self):
        self.runner = CliRunner()
        pass

    @patch.object(RecceContext, "verify_required_artifacts")
    @patch("recce.cli.cli_run")
    def test_cmd_run(self, mock_cli_run, mock_verify_required_artifacts):
        mock_verify_required_artifacts.return_value = True, None

        self.runner.invoke(cli_command_run, [])
        mock_cli_run.assert_called_once()


class TestCommandUploadSession(TestCase):
    def setUp(self):
        self.runner = CliRunner()
        pass

    @patch("recce.cli.prepare_api_token", return_value="unittest_token")
    @patch("recce.cli.upload_artifacts_to_session", return_value=0)
    def test_cmd_upload_session(self, mock_upload_artifacts_to_session, mock_prepare_api_token):
        self.runner.invoke(
            cli_command_upload_session,
            ["--session-id", "unittest_session", "--api-token", mock_prepare_api_token.return_value],
        )
        mock_upload_artifacts_to_session.assert_called_once_with(
            "target", session_id="unittest_session", token="unittest_token", debug=False
        )

        self.runner.invoke(
            cli_command_snapshot,
            ["--snapshot-id", "unittest_session", "--api-token", mock_prepare_api_token.return_value],
        )
        mock_upload_artifacts_to_session.assert_called_once_with(
            "target", session_id="unittest_session", token="unittest_token", debug=False
        )
