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

#include <stdlib.h>

#include <iostream>

#include "base/check.h"
#include "base/compiler_specific.h"
#include "base/json/json_writer.h"
#include "base/strings/strcat.h"
#include "content/browser/first_party_sets/first_party_set_parser.h"
#include "content/browser/first_party_sets/test/related_website_sets.pb.h"
#include "content/browser/first_party_sets/test/related_website_sets_fuzzable.pb.h"
#include "net/first_party_sets/global_first_party_sets.h"
#include "net/first_party_sets/local_set_declaration.h"
#include "testing/libfuzzer/proto/lpm_interface.h"

namespace content {

namespace {

constexpr char kPrimary[] = "primary";
constexpr char kAssociated[] = "associatedSites";
constexpr char kService[] = "serviceSites";
constexpr char kCctld[] = "ccTLDs";

constexpr char const* kSubdomains[3] = {
    "sub-0",
    "sub-1",
    "sub-2",
};

constexpr char const* kSites[5] = {
    "site-0", "site-1", "site-2", "site-3", "site-4",
};

constexpr char const* kTlds[2] = {
    "test",
    "cctld",
};

std::string ConvertSite(const related_website_sets::proto::Site& site) {
  std::string out = "https://";
  if (site.has_subdomain_index()) {
    base::StrAppend(&out,
                    {UNSAFE_TODO(kSubdomains[site.subdomain_index()]), "."});
  }
  base::StrAppend(&out, {
                            UNSAFE_TODO(kSites[site.site_index()]),
                            ".",
                            UNSAFE_TODO(kTlds[site.tld()]),
                        });

  return out;
}

base::DictValue ConvertSet(const related_website_sets::proto::Set& set) {
  base::DictValue json_set;
  json_set.Set(kPrimary, ConvertSite(set.primary()));
  for (const auto& site : set.associated()) {
    json_set.EnsureList(kAssociated)->Append(ConvertSite(site));
  }
  for (const auto& site : set.service()) {
    json_set.EnsureList(kService)->Append(ConvertSite(site));
  }
  for (const related_website_sets::proto::SitePair& site_pair :
       set.cctld_aliases()) {
    json_set.EnsureDict(kCctld)->Set(ConvertSite(site_pair.alias()),
                                     ConvertSite(site_pair.canonical()));
  }

  return json_set;
}

std::string ConvertProto(
    const related_website_sets::proto::PublicSets& public_sets) {
  std::string out;

  for (const related_website_sets::proto::Set& set : public_sets.sets()) {
    base::StrAppend(&out, {base::WriteJson(ConvertSet(set)).value()});
  }

  return out;
}



std::string ConvertProto(
    const related_website_sets::proto::CommandLineSwitch& command_line_switch) {
  std::string out;

  if (command_line_switch.has_set()) {
    base::StrAppend(
        &out, {base::WriteJson(ConvertSet(command_line_switch.set())).value()});
  }

  return out;
}

struct NativeInputs {
  std::string public_sets;
  std::string command_line_switch;
};

NativeInputs ConvertProto(const related_website_sets::proto::AllInputs& input) {
  return NativeInputs{
      ConvertProto(input.public_sets()),
      ConvertProto(input.command_line_switch()),
  };
}

}  // namespace

DEFINE_PROTO_FUZZER(
    const fuzzable::related_website_sets::proto::AllInputs& fuzzable_input) {
  std::string serialized;
  CHECK(fuzzable_input.SerializeToString(&serialized));
  related_website_sets::proto::AllInputs input;
  // Recursion limits can cause parsing to fail.
  if (!input.ParseFromString(serialized)) {
    return;
  }

  NativeInputs native_inputs = ConvertProto(input);

  if (getenv("LPM_DUMP_NATIVE_INPUT")) {
    std::cout << native_inputs.public_sets << std::endl;
    std::cout << native_inputs.command_line_switch << std::endl;
  }

  std::istringstream stream(native_inputs.public_sets);
  net::GlobalFirstPartySets global_sets =
      FirstPartySetParser::ParseSetsFromStream(stream, base::Version("1.0"),
                                               false);

  net::LocalSetDeclaration local_set_declaration =
      FirstPartySetParser::ParseFromCommandLine(
          native_inputs.command_line_switch);

  global_sets.ApplyManuallySpecifiedSet(local_set_declaration);
}

}  // namespace content
