# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Tests for gcp_utils."""

import unittest
from unittest import mock

from google.adk.cli.utils import gcp_utils
import google.auth
import google.auth.exceptions
import requests


class TestGcpUtils(unittest.TestCase):

  def setUp(self):
    super().setUp()
    patcher = mock.patch(
        "google.adk.cli.utils.gcp_utils._mtls_utils.use_client_cert_effective",
        return_value=False,
    )
    self.mock_use_client_cert_effective = patcher.start()
    self.addCleanup(patcher.stop)

  @mock.patch("google.auth.default")
  def test_check_adc_success(self, mock_auth_default):
    mock_auth_default.return_value = (mock.Mock(), "test-project")
    self.assertTrue(gcp_utils.check_adc())

  @mock.patch("google.auth.default")
  def test_check_adc_failure(self, mock_auth_default):
    mock_auth_default.side_effect = (
        google.auth.exceptions.DefaultCredentialsError()
    )
    self.assertFalse(gcp_utils.check_adc())

  @mock.patch("google.auth.default")
  def test_get_access_token(self, mock_auth_default):
    mock_creds = mock.Mock()
    mock_creds.token = "test-token"
    mock_creds.valid = True
    mock_auth_default.return_value = (mock_creds, "test-project")
    self.assertEqual(gcp_utils.get_access_token(), "test-token")

  @mock.patch("google.adk.cli.utils.gcp_utils.AuthorizedSession")
  @mock.patch("google.auth.default")
  def test_retrieve_express_project_success(
      self, mock_auth_default, mock_session_cls
  ):
    mock_auth_default.return_value = (mock.Mock(), "test-project-id")

    mock_session = mock.Mock()
    mock_session_cls.return_value = mock_session
    mock_response = mock.Mock()
    mock_response.json.return_value = {
        "expressProject": {
            "projectId": "test-project",
            "defaultApiKey": "test-api-key",
            "region": "us-central1",
        }
    }
    mock_session.get.return_value = mock_response

    result = gcp_utils.retrieve_express_project()
    self.assertEqual(result["project_id"], "test-project")
    self.assertEqual(result["api_key"], "test-api-key")
    self.assertEqual(result["region"], "us-central1")
    mock_session.get.assert_called_once()
    args, kwargs = mock_session.get.call_args
    self.assertEqual(
        args[0],
        "https://us-central1-aiplatform.googleapis.com/v1beta1/vertexExpress:retrieveExpressProject",
    )
    self.assertEqual(kwargs["params"], {"get_default_api_key": True})

  @mock.patch("google.adk.cli.utils.gcp_utils.AuthorizedSession")
  @mock.patch("google.auth.default")
  def test_retrieve_express_project_not_found(
      self, mock_auth_default, mock_session_cls
  ):
    mock_auth_default.return_value = (mock.Mock(), "test-project-id")

    mock_session = mock.Mock()
    mock_session_cls.return_value = mock_session
    mock_response = mock.Mock()
    mock_response.status_code = 404
    mock_response.raise_for_status.side_effect = requests.exceptions.HTTPError(
        response=mock_response
    )
    mock_session.get.return_value = mock_response

    result = gcp_utils.retrieve_express_project()
    self.assertIsNone(result)

  @mock.patch("google.adk.cli.utils.gcp_utils.AuthorizedSession")
  @mock.patch("google.auth.default")
  def test_check_express_eligibility(self, mock_auth_default, mock_session_cls):
    mock_auth_default.return_value = (mock.Mock(), "test-project-id")

    mock_session = mock.Mock()
    mock_session_cls.return_value = mock_session
    mock_response = mock.Mock()
    mock_response.json.return_value = {"eligibility": "IN_SCOPE"}
    mock_session.get.return_value = mock_response

    self.assertTrue(gcp_utils.check_express_eligibility())

  @mock.patch("google.adk.cli.utils.gcp_utils.AuthorizedSession")
  @mock.patch("google.auth.default")
  def test_sign_up_express(self, mock_auth_default, mock_session_cls):
    mock_auth_default.return_value = (mock.Mock(), "test-project-id")

    mock_session = mock.Mock()
    mock_session_cls.return_value = mock_session
    mock_response = mock.Mock()
    mock_response.json.return_value = {
        "projectId": "new-project",
        "defaultApiKey": "new-api-key",
        "region": "us-central1",
    }
    mock_session.post.return_value = mock_response

    result = gcp_utils.sign_up_express()
    self.assertEqual(result["project_id"], "new-project")
    self.assertEqual(result["api_key"], "new-api-key")
    args, kwargs = mock_session.post.call_args
    self.assertEqual(
        args[0],
        "https://us-central1-aiplatform.googleapis.com/v1beta1/vertexExpress:signUp",
    )
    self.assertEqual(
        kwargs["json"],
        {
            "region": "us-central1",
            "tos_accepted": True,
            "get_default_api_key": True,
        },
    )

  @mock.patch("google.cloud.resourcemanager_v3.ProjectsClient")
  def test_list_gcp_projects(self, mock_client_cls):
    mock_client = mock.Mock()
    mock_client_cls.return_value = mock_client

    mock_project1 = mock.Mock()
    mock_project1.project_id = "p1"
    mock_project1.display_name = "Project 1"

    mock_project2 = mock.Mock()
    mock_project2.project_id = "p2"
    mock_project2.display_name = None

    mock_client.search_projects.return_value = [mock_project1, mock_project2]

    projects = gcp_utils.list_gcp_projects()
    self.assertEqual(len(projects), 2)
    self.assertEqual(projects[0], ("p1", "Project 1"))
    self.assertEqual(projects[1], ("p2", "p2"))

  @mock.patch.dict("sys.modules", {"google.cloud": None})
  def test_list_gcp_projects_import_error(self):
    with self.assertRaisesRegex(
        RuntimeError,
        "Listing GCP projects requires the 'gcp' optional dependency",
    ):
      gcp_utils.list_gcp_projects()

  @mock.patch("google.adk.cli.utils.gcp_utils.AuthorizedSession")
  @mock.patch("google.auth.default")
  def test_retrieve_express_project_mtls_enabled(
      self, mock_auth_default, mock_session_cls
  ):
    # Enable mtls
    self.mock_use_client_cert_effective.return_value = True

    mock_auth_default.return_value = (mock.Mock(), "test-project-id")

    mock_session = mock.Mock()
    mock_session_cls.return_value = mock_session
    mock_response = mock.Mock()
    mock_response.json.return_value = {
        "expressProject": {
            "projectId": "test-project",
            "defaultApiKey": "test-api-key",
            "region": "us-central1",
        }
    }
    mock_session.get.return_value = mock_response

    with mock.patch(
        "google.adk.cli.utils.gcp_utils._mtls_utils.get_api_endpoint",
        return_value=(
            "https://us-central1-aiplatform.mtls.googleapis.com/v1beta1"
        ),
    ) as mock_get_api_endpoint:
      result = gcp_utils.retrieve_express_project()

    self.assertEqual(result["project_id"], "test-project")
    mock_session.configure_mtls_channel.assert_called_once()
    mock_get_api_endpoint.assert_called_once_with(
        location="us-central1",
        default_template="https://{location}-aiplatform.googleapis.com/v1beta1",
        mtls_template=(
            "https://{location}-aiplatform.mtls.googleapis.com/v1beta1"
        ),
    )
    mock_session.get.assert_called_once()
    args, _ = mock_session.get.call_args
    self.assertEqual(
        args[0],
        "https://us-central1-aiplatform.mtls.googleapis.com/v1beta1/vertexExpress:retrieveExpressProject",
    )


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