// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromeos/ash/components/boca/session_api/session_parser.h"

#include "ash/constants/ash_features.h"
#include "base/memory/values_equivalent.h"
#include "base/strings/string_util.h"
#include "base/test/scoped_feature_list.h"
#include "base/values.h"
#include "chromeos/ash/components/boca/proto/session.pb.h"
#include "chromeos/ash/components/boca/session_api/constants.h"
#include "google_apis/common/base_requests.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash::boca {
namespace {

// TODO(crbug.com/374364083):Refactor existing get session unit test.
// Currently this file doesn't have full coverage for session_parser, the reset
// is covered in get_session_request_unittest, we should move those here.
constexpr char kFullSessionResponse[] = R"(
  {
  "startTime":{
    "seconds": 1723773909
  },
  "sessionId": "111",
  "duration": {
    "seconds": 120
  },
  "joinCode":{
    "enabled":true,
    "code":"testCode"
  },
  "studentStatuses": {
    "2": {
      "state": "ADDED",
      "geminiEnablementState": "GEMINI_ENABLEMENT_STATE_DISABLED"
    },
    "3": {
      "state": "ACTIVE",
      "geminiEnablementState": "GEMINI_ENABLEMENT_STATE_ENABLED",
       "devices":
        {
          "kDummyDeviceId":
         {
            "info": {"deviceId":"kDummyDeviceId"},
            "state":"INACTIVE",
            "activity": {
              "activeTab": {
                "title": "google"
                }
              },
              "viewScreenConfig": {
                "viewScreenState": "AVAILABLE",
                "connectionParam": {
                  "connectionCode": "0123456789"
                },
                "viewScreenRequester": {
                  "user": {
                    "email": "requester@email.com",
                    "fullName": "requester",
                    "gaiaId": "456",
                    "photoUrl": "data:image/456"
                  },
                  "serviceAccount" : {
                    "email": "robot@email.com"
                  }
                }
              }
         }
        }
    },
    "22": {
      "state": "ADDED"
    }
  },
  "roster": {
    "studentGroups": [{
      "students": [
        {
          "email": "cat@gmail.com",
          "fullName": "cat",
          "gaiaId": "2",
          "photoUrl": "data:image/123"
        },
        {
          "email": "dog@gmail.com",
          "fullName": "dog",
          "gaiaId": "3",
          "photoUrl": "data:image/123"
        }
      ],
      "title": "main"
    },
    {
      "students": [
        {
          "email": "cat1@gmail.com",
          "fullName": "cat1",
          "gaiaId": "22",
          "photoUrl": "data:image/123"
        },
        {
          "email": "dog1@gmail.com",
          "fullName": "dog1",
          "gaiaId": "33",
          "photoUrl": "data:image/123"
        }
      ],
      "groupSource":"JOIN_CODE",
      "title": "accessCode"
    }]
  },
  "sessionState": "ACTIVE",
  "studentGroupConfigs": {
    "main": {
      "captionsConfig": {
        "captionsEnabled": true,
        "translationsEnabled": true
      },
      "onTaskConfig": {
        "activeBundle": {
          "contentConfigs": [
            {
              "faviconUrl": "data:image/123",
              "lockedNavigationOptions": {
                "navigationType": "OPEN_NAVIGATION"
              },
              "title": "google",
              "url": "https://google.com"
            },
            {
              "faviconUrl": "data:image/123",
              "lockedNavigationOptions": {
                "navigationType": "BLOCK_NAVIGATION"
              },
              "title": "youtube",
              "url": "https://youtube.com"
            }
          ],
          "locked": true,
          "lockToAppHome": true
        }
      }
    }
  },
  "teacher":
  {
    "email": "teacher@gmail.com",
    "fullName": "teacher",
    "gaiaId": "1",
      "photoUrl": "data:image/123"
        }
        }
       )";

constexpr char kPartialResponse[] = R"(
     {
    "sessionId": "111",
    "duration": {
        "seconds": 120
    },
    "studentStatuses": {
      "3": {
        "state": "ACTIVE",
        "geminiEnablementState": "GEMINI_ENABLEMENT_STATE_ENABLED",
        "devices":
          {
            "kDummyDeviceId":
          {
              "info": {"device_id":"kDummyDeviceId"},
              "state":"INACTIVE",
              "activity": {
                "activeTab": {
                  "title": "google"
                  }
                },
              "viewScreenConfig": {
                "viewScreenState": "REQUESTED",
                "viewScreenRequester": {
                  "serviceAccount" : {
                    "email": "robot@email.com"
                  }
                }
              }
          }
          }
      }
    },
    "roster": {
        "studentGroups": []
    },
    "sessionState": "ACTIVE",
    "studentGroupConfigs": {
        "main": {
            "captionsConfig": {},
            "onTaskConfig": {
                "activeBundle": {
                    "contentConfigs": []
                }
            }
        }
    },
    "teacher": {
        "gaiaId": "1"
    }
    }
    )";

class SessionParserTest : public testing::Test {
 protected:
  SessionParserTest() = default;
  void SetUp() override {
    session_dict_full = google_apis::ParseJson(kFullSessionResponse);
    session_dict_partial = google_apis::ParseJson(kPartialResponse);
    session_full = std::make_unique<::boca::Session>();
    session_partial = std::make_unique<::boca::Session>();
  }
  base::test::ScopedFeatureList scoped_feature_list_;
  std::unique_ptr<::boca::Session> session_full;
  std::unique_ptr<::boca::Session> session_partial;
  std::unique_ptr<base::Value> session_dict_full;
  std::unique_ptr<base::Value> session_dict_partial;
};

TEST_F(SessionParserTest, TestParseTeacherProtoFromJson) {
  ParseTeacherProtoFromJson(session_dict_full->GetIfDict(), session_full.get());
  EXPECT_EQ("teacher@gmail.com", session_full->teacher().email());
  EXPECT_EQ("teacher", session_full->teacher().full_name());
  EXPECT_EQ("1", session_full->teacher().gaia_id());
  EXPECT_EQ("data:image/123", session_full->teacher().photo_url());

  ParseTeacherProtoFromJson(session_dict_partial->GetIfDict(),
                            session_partial.get());
  EXPECT_EQ("1", session_partial->teacher().gaia_id());
}

TEST_F(SessionParserTest, TestParseJoinCodeProtoFromJson) {
  ParseJoinCodeProtoFromJson(session_dict_full->GetIfDict(),
                             session_full.get());
  EXPECT_TRUE(session_full->join_code().enabled());
  EXPECT_EQ("testCode", session_full->join_code().code());
}

TEST_F(SessionParserTest, TestParseRosterProtoFromJson) {
  ParseRosterProtoFromJson(session_dict_full->GetIfDict(), session_full.get());
  ASSERT_EQ(2, session_full->roster().student_groups().size());

  ASSERT_EQ(2, session_full->roster().student_groups()[0].students().size());
  EXPECT_EQ(kMainStudentGroupName,
            session_full->roster().student_groups()[0].title());
  EXPECT_EQ(::boca::StudentGroup::CLASSROOM,
            session_full->roster().student_groups()[0].group_source());
  EXPECT_EQ("cat@gmail.com",
            session_full->roster().student_groups()[0].students()[0].email());
  EXPECT_EQ(
      "cat",
      session_full->roster().student_groups()[0].students()[0].full_name());
  EXPECT_EQ("2",
            session_full->roster().student_groups()[0].students()[0].gaia_id());
  EXPECT_EQ(
      "data:image/123",
      session_full->roster().student_groups()[0].students()[0].photo_url());

  EXPECT_EQ("dog@gmail.com",
            session_full->roster().student_groups()[0].students()[1].email());
  EXPECT_EQ(
      "dog",
      session_full->roster().student_groups()[0].students()[1].full_name());
  EXPECT_EQ("3",
            session_full->roster().student_groups()[0].students()[1].gaia_id());
  EXPECT_EQ(
      "data:image/123",
      session_full->roster().student_groups()[0].students()[1].photo_url());

  ASSERT_EQ(2, session_full->roster().student_groups()[1].students().size());
  EXPECT_EQ("accessCode", session_full->roster().student_groups()[1].title());
  EXPECT_EQ(::boca::StudentGroup::JOIN_CODE,
            session_full->roster().student_groups()[1].group_source());
  EXPECT_EQ("cat1@gmail.com",
            session_full->roster().student_groups()[1].students()[0].email());
  EXPECT_EQ(
      "cat1",
      session_full->roster().student_groups()[1].students()[0].full_name());
  EXPECT_EQ("22",
            session_full->roster().student_groups()[1].students()[0].gaia_id());
  EXPECT_EQ(
      "data:image/123",
      session_full->roster().student_groups()[1].students()[0].photo_url());

  EXPECT_EQ("dog1@gmail.com",
            session_full->roster().student_groups()[1].students()[1].email());
  EXPECT_EQ(
      "dog1",
      session_full->roster().student_groups()[1].students()[1].full_name());
  EXPECT_EQ("33",
            session_full->roster().student_groups()[1].students()[1].gaia_id());
  EXPECT_EQ(
      "data:image/123",
      session_full->roster().student_groups()[1].students()[1].photo_url());

  ParseRosterProtoFromJson(session_dict_partial->GetIfDict(),
                           session_partial.get());
  EXPECT_TRUE(session_partial->roster().student_groups().empty());
}

TEST_F(SessionParserTest, TestParseSessionConfigProtoFromJson) {
  // For producer.
  ParseSessionConfigProtoFromJson(session_dict_full->GetIfDict(),
                                  session_full.get(), true);
  ASSERT_EQ(1u, session_full->student_group_configs().size());
  EXPECT_TRUE(session_full->student_group_configs()
                  .at(kMainStudentGroupName)
                  .captions_config()
                  .captions_enabled());
  EXPECT_TRUE(session_full->student_group_configs()
                  .at(kMainStudentGroupName)
                  .captions_config()
                  .translations_enabled());

  EXPECT_TRUE(session_full->student_group_configs()
                  .at(kMainStudentGroupName)
                  .on_task_config()
                  .active_bundle()
                  .locked());

  EXPECT_TRUE(session_full->student_group_configs()
                  .at(kMainStudentGroupName)
                  .on_task_config()
                  .active_bundle()
                  .lock_to_app_home());

  auto content_config = std::move(session_full->student_group_configs()
                                      .at(kMainStudentGroupName)
                                      .on_task_config()
                                      .active_bundle()
                                      .content_configs());
  ASSERT_EQ(2, content_config.size());

  EXPECT_EQ("data:image/123", content_config[0].favicon_url());
  EXPECT_EQ("google", content_config[0].title());
  EXPECT_EQ("https://google.com", content_config[0].url());
  EXPECT_EQ(::boca::LockedNavigationOptions::OPEN_NAVIGATION,
            content_config[0].locked_navigation_options().navigation_type());

  EXPECT_EQ("data:image/123", content_config[1].favicon_url());
  EXPECT_EQ("youtube", content_config[1].title());
  EXPECT_EQ("https://youtube.com", content_config[1].url());
  EXPECT_EQ(::boca::LockedNavigationOptions::BLOCK_NAVIGATION,
            content_config[1].locked_navigation_options().navigation_type());

  ParseSessionConfigProtoFromJson(session_dict_partial->GetIfDict(),
                                  session_partial.get(), true);
  ASSERT_EQ(1u, session_partial->student_group_configs().size());

  auto content_config_1 = std::move(session_partial->student_group_configs()
                                        .at(kMainStudentGroupName)
                                        .on_task_config()
                                        .active_bundle()
                                        .content_configs());
  ASSERT_EQ(0, content_config_1.size());

  EXPECT_FALSE(session_partial->student_group_configs()
                   .at(kMainStudentGroupName)
                   .captions_config()
                   .captions_enabled());
  EXPECT_FALSE(session_partial->student_group_configs()
                   .at(kMainStudentGroupName)
                   .captions_config()
                   .translations_enabled());

  EXPECT_FALSE(session_partial->student_group_configs()
                   .at(kMainStudentGroupName)
                   .on_task_config()
                   .active_bundle()
                   .locked());
}

TEST_F(SessionParserTest, TestParseStudentStatusProtoFromJson) {
  scoped_feature_list_.InitWithFeatures({::ash::features::kBocaSpotlight}, {});
  // Student status depends on roster info.
  ParseRosterProtoFromJson(session_dict_full->GetIfDict(), session_full.get());
  ParseStudentStatusProtoFromJson(session_dict_full->GetIfDict(),
                                  session_full.get(), true);
  ASSERT_EQ(3u, session_full->student_statuses().size());
  EXPECT_EQ(::boca::StudentStatus::ADDED,
            session_full->student_statuses().at("2").state());
  EXPECT_EQ(::boca::GEMINI_ENABLEMENT_STATE_DISABLED,
            session_full->student_statuses().at("2").gemini_enablement_state());
  EXPECT_EQ(::boca::StudentStatus::ACTIVE,
            session_full->student_statuses().at("3").state());
  EXPECT_EQ(::boca::GEMINI_ENABLEMENT_STATE_ENABLED,
            session_full->student_statuses().at("3").gemini_enablement_state());

  EXPECT_EQ("google", session_full->student_statuses()
                          .at("3")
                          .devices()
                          .at("kDummyDeviceId")
                          .activity()
                          .active_tab()
                          .title());
  EXPECT_EQ("kDummyDeviceId", session_full->student_statuses()
                                  .at("3")
                                  .devices()
                                  .at("kDummyDeviceId")
                                  .info()
                                  .device_id());
  EXPECT_EQ(::boca::ViewScreenConfig::AVAILABLE,
            session_full->student_statuses()
                .at("3")
                .devices()
                .at("kDummyDeviceId")
                .view_screen_config()
                .view_screen_state());
  EXPECT_EQ("0123456789", session_full->student_statuses()
                              .at("3")
                              .devices()
                              .at("kDummyDeviceId")
                              .view_screen_config()
                              .connection_param()
                              .connection_code());
  EXPECT_EQ("robot@email.com", session_full->student_statuses()
                                   .at("3")
                                   .devices()
                                   .at("kDummyDeviceId")
                                   .view_screen_config()
                                   .view_screen_requester()
                                   .service_account()
                                   .email());
  EXPECT_EQ("requester@email.com", session_full->student_statuses()
                                       .at("3")
                                       .devices()
                                       .at("kDummyDeviceId")
                                       .view_screen_config()
                                       .view_screen_requester()
                                       .user()
                                       .email());
  EXPECT_EQ(::boca::StudentStatus::ADDED,
            session_full->student_statuses().at("22").state());
  ParseStudentStatusProtoFromJson(session_dict_partial->GetIfDict(),
                                  session_partial.get(), false);
  EXPECT_EQ(::boca::StudentStatus::ACTIVE,
            session_partial->student_statuses().at("3").state());
  EXPECT_EQ(
      ::boca::GEMINI_ENABLEMENT_STATE_ENABLED,
      session_partial->student_statuses().at("3").gemini_enablement_state());

  EXPECT_EQ("google", session_partial->student_statuses()
                          .at("3")
                          .devices()
                          .at("kDummyDeviceId")
                          .activity()
                          .active_tab()
                          .title());
  EXPECT_EQ(::boca::StudentDevice::INACTIVE, session_partial->student_statuses()
                                                 .at("3")
                                                 .devices()
                                                 .at("kDummyDeviceId")
                                                 .state());
  EXPECT_EQ(::boca::ViewScreenConfig::REQUESTED,
            session_partial->student_statuses()
                .at("3")
                .devices()
                .at("kDummyDeviceId")
                .view_screen_config()
                .view_screen_state());
  EXPECT_EQ("robot@email.com", session_full->student_statuses()
                                   .at("3")
                                   .devices()
                                   .at("kDummyDeviceId")
                                   .view_screen_config()
                                   .view_screen_requester()
                                   .service_account()
                                   .email());
}

constexpr char kSessionResponseWithUrlTypeTemplate[] = R"(
          {
            "sessionId": "111",
            "duration": {
              "seconds": 120
            },
            "sessionState": "ACTIVE",
            "studentGroupConfigs": {
              "main": {
                "onTaskConfig": {
                  "activeBundle": {
                    "contentConfigs": [
                      {
                        "title": "gemini",
                        "url": "https://gemini.google.com",
                        "urlType": "$1"
                      }
                    ]
                  }
                }
              }
            },
            "teacher": {
              "gaiaId": "1"
            }
          }
        )";

struct SessionParserUrlTypeTestParam {
  std::string test_name;
  std::string url_type_str;
  ::boca::UrlType expected_url_type;
};

class SessionParserUrlTypeTest
    : public testing::TestWithParam<SessionParserUrlTypeTestParam> {};

TEST_P(SessionParserUrlTypeTest, TestParseSessionConfigUrlType) {
  std::string json = base::ReplaceStringPlaceholders(
      kSessionResponseWithUrlTypeTemplate, {GetParam().url_type_str}, nullptr);
  auto parsed_value = google_apis::ParseJson(json);
  ASSERT_TRUE(parsed_value);

  std::unique_ptr<::boca::Session> session =
      std::make_unique<::boca::Session>();
  ParseSessionConfigProtoFromJson(parsed_value->GetIfDict(), session.get(),
                                  /*is_producer=*/true);

  ASSERT_TRUE(session->student_group_configs().contains(kMainStudentGroupName));
  auto content_config = std::move(session->student_group_configs()
                                      .at(kMainStudentGroupName)
                                      .on_task_config()
                                      .active_bundle()
                                      .content_configs());
  ASSERT_EQ(1, content_config.size());

  EXPECT_EQ("gemini", content_config[0].title());
  EXPECT_EQ("https://gemini.google.com", content_config[0].url());
  EXPECT_EQ(GetParam().expected_url_type, content_config[0].url_type());
}

INSTANTIATE_TEST_SUITE_P(
    SessionParserUrlTypeTests,
    SessionParserUrlTypeTest,
    testing::Values(
        SessionParserUrlTypeTestParam{"GeminiRegular",
                                      "URL_TYPE_GEMINI_REGULAR",
                                      ::boca::URL_TYPE_GEMINI_REGULAR},
        SessionParserUrlTypeTestParam{"GeminiGuidedLearning",
                                      "URL_TYPE_GEMINI_GUIDED_LEARNING",
                                      ::boca::URL_TYPE_GEMINI_GUIDED_LEARNING},
        SessionParserUrlTypeTestParam{"UrlTypeUnspecified",
                                      "URL_TYPE_UNSPECIFIED",
                                      ::boca::URL_TYPE_UNSPECIFIED},
        SessionParserUrlTypeTestParam{"UrlTypeInvalid", "INVALID_TYPE",
                                      ::boca::URL_TYPE_UNSPECIFIED}),
    [](const testing::TestParamInfo<SessionParserUrlTypeTest::ParamType>&
           info) { return info.param.test_name; });

}  // namespace
}  // namespace ash::boca
