// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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.

package google.registry.tools;

import static com.google.common.base.Verify.verify;

import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.google.api.services.dns.Dns;
import com.google.api.services.dns.model.ManagedZone;
import com.google.api.services.dns.model.ManagedZoneDnsSecConfig;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import google.registry.config.RegistryConfig.Config;
import jakarta.inject.Inject;
import java.io.IOException;
import java.util.stream.Collectors;
import javax.annotation.Nullable;

@Parameters(separators = " =", commandDescription = "Create a Managed Zone for a TLD in Cloud DNS.")
final class CreateCdnsTld extends ConfirmingCommand {

  @Parameter(names = "--description", description = "Description of the new TLD.")
  String description;

  @Parameter(
    names = "--dns_name",
    description = "DNS name of the new tld, including trailing period, e.g.: search.",
    required = true
  )
  String dnsName;

  @Nullable
  @Parameter(
    names = "--name",
    description = "Managed zone name.  If not specified, dns_name is used."
  )
  String name;

  @Parameter(
      names = "--skip_sandbox_tld_check",
      description = "In Sandbox, skip the dns_name format check.")
  boolean skipSandboxTldCheck;

  @Parameter(
      names = "--use_prod_name_servers_in_sandbox",
      description =
          "In Sandbox, create zone on the production name servers, e.g., for ICANN tests. "
              + "Ignored in other environments.")
  boolean useProdNameServersInSandbox;

  @Inject
  @Config("projectId")
  String projectId;

  @Inject Dns dnsService;

  private static final String KEY_VALUE_FORMAT = "  %s = %s";

  private ManagedZone managedZone;

  @Override
  protected void init() {
    // Sandbox talks to production Cloud DNS.  As a result, we can't configure any domains with a
    // suffix that might be used by customers on the same nameserver set.  Limit the user to setting
    // up *.test TLDs unless the user declares that the name is approved.
    //
    // The name format check simply provides a user-friendly error message. If the user wrongly
    // declares name approval, the request to the Cloud DNS API will still fail.
    if (RegistryToolEnvironment.get() == RegistryToolEnvironment.SANDBOX) {
      if (!skipSandboxTldCheck && !dnsName.endsWith(".test.")) {
        throw new IllegalArgumentException(
            "Sandbox TLDs must be approved or in the form \"*.test.\"");
      }
    }

    String nameServerSetName;
    if (RegistryToolEnvironment.get().equals(RegistryToolEnvironment.PRODUCTION)) {
      nameServerSetName = "cloud-dns-registry";
    } else if (RegistryToolEnvironment.get().equals(RegistryToolEnvironment.SANDBOX)
        && useProdNameServersInSandbox) {
      nameServerSetName = "cloud-dns-registry";
    } else {
      nameServerSetName = "cloud-dns-registry-test";
    }

    managedZone =
        new ManagedZone()
            .setDescription(description)
            .setNameServerSet(nameServerSetName)
            .setDnsName(dnsName)
            .setName(
                (name != null)
                    ? name
                    : Joiner.on('_').join(Splitter.on('.').omitEmptyStrings().split(dnsName)))
            .setDnssecConfig(new ManagedZoneDnsSecConfig().setNonExistence("nsec").setState("on"));
  }

  @Override
  protected String prompt() {
    return String.format(
        "Creating TLD with:\n%s\n%s",
        String.format(KEY_VALUE_FORMAT, "projectId", projectId),
        managedZone
            .entrySet()
            .stream()
            .map(entry -> String.format(KEY_VALUE_FORMAT, entry.getKey(), entry.getValue()))
            .collect(Collectors.joining("\n")));
  }

  @Override
  public String execute() throws IOException {
    validateDnsService();
    Dns.ManagedZones.Create request = dnsService.managedZones().create(projectId, managedZone);
    ManagedZone response = request.execute();
    return String.format("Created managed zone: %s", response);
  }

  private void validateDnsService() {
    // Sanity check to ensure only Production and Sandbox points to the CloudDns prod site.
    if (RegistryToolEnvironment.get() != RegistryToolEnvironment.PRODUCTION
        && RegistryToolEnvironment.get() != RegistryToolEnvironment.SANDBOX) {
      verify(!Dns.DEFAULT_ROOT_URL.equals(dnsService.getRootUrl()));
      verify(!Dns.DEFAULT_SERVICE_PATH.equals(dnsService.getServicePath()));
    }
  }
}
