# -*- coding: utf-8 -*- #
# Copyright 2020 Google LLC. 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.
"""`gcloud domains registrations configure dns` command."""


from googlecloudsdk.api_lib.domains import registrations
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.domains import dns_util
from googlecloudsdk.command_lib.domains import flags
from googlecloudsdk.command_lib.domains import resource_args
from googlecloudsdk.command_lib.domains import util
from googlecloudsdk.core import exceptions
from googlecloudsdk.core import log


@base.DefaultUniverseOnly
class ConfigureDNS(base.UpdateCommand):
  """Configure DNS settings of a Cloud Domains registration.

  Configure DNS settings of a Cloud Domains registration.

  In most cases, this command is used for changing the authoritative name
  servers and DNSSEC options for the given domain. However, advanced options
  like glue records are available.

  ## EXAMPLES

  To start an interactive flow to configure DNS settings for ``example.com'',
  run:

    $ {command} example.com

  To use Cloud DNS managed-zone ``example-zone'' for ``example.com'', run:

    $ {command} example.com --cloud-dns-zone=example-zone

  DNSSEC will not be enabled as it may not be safe to enable it (e.g. when the
  Cloud DNS managed-zone was signed less than 24h ago).

  To use a signed Cloud DNS managed-zone ``example-zone'' for ``example.com''
  and enable DNSSEC, run:

    $ {command} example.com --cloud-dns-zone=example-zone --no-disable-dnssec

  To change DNS settings for ``example.com'' according to information from a
  YAML file ``dns_settings.yaml'', run:

    $ {command} example.com --dns-settings-from-file=dns_settings.yaml

  To disable DNSSEC, run:

    $ {command} example.com --disable-dnssec

  """

  @staticmethod
  def Args(parser):
    resource_args.AddRegistrationResourceArg(parser,
                                             'to configure DNS settings for')
    flags.AddConfigureDNSSettingsFlagsToParser(parser)
    flags.AddValidateOnlyFlagToParser(parser, 'configure DNS settings of the')
    flags.AddAsyncFlagToParser(parser)

  def Run(self, args):
    api_version = registrations.GetApiVersionFromArgs(args)
    client = registrations.RegistrationsClient(api_version)
    args.registration = util.NormalizeResourceName(args.registration)
    registration_ref = args.CONCEPTS.registration.Parse()
    dnssec_flag_provided = args.IsSpecified('disable_dnssec')
    if dnssec_flag_provided and args.dns_settings_from_file:
      raise exceptions.Error(
          'argument --disable-dnssec: At most one of '
          '--dns-settings-from-file | --disable-dnssec can be specified.')

    registration = client.Get(registration_ref)
    util.AssertRegistrationOperational(api_version, registration)

    dnssec_update = dns_util.DNSSECUpdate.NO_CHANGE
    if dnssec_flag_provided and args.disable_dnssec:
      dnssec_update = dns_util.DNSSECUpdate.DISABLE
    elif dnssec_flag_provided and not args.disable_dnssec:
      dnssec_update = dns_util.DNSSECUpdate.ENABLE

    dns_settings, update_mask = dns_util.ParseDNSSettings(
        api_version,
        args.name_servers,
        args.cloud_dns_zone,
        args.use_google_domains_dns,
        args.dns_settings_from_file,
        registration_ref.registrationsId,
        dnssec_update=dnssec_update,
        dns_settings=registration.dnsSettings,
    )

    if dns_settings is None:
      dns_settings, update_mask = dns_util.PromptForNameServers(
          api_version,
          registration_ref.registrationsId,
          dnssec_update=dnssec_update,
          dns_settings=registration.dnsSettings,
      )
      if dns_settings is None:
        return None

    if registration.dnsSettings.glueRecords and not update_mask.glue_records:
      # It's ok to leave Glue records while changing name servers.
      log.status.Print(
          'Glue records will not be cleared. If you want to clear '
          'them, use --dns-settings-from-file flag.'
      )

    ds_records_present = dns_util.DnssecEnabled(registration.dnsSettings)
    new_ds_records = dns_util.DnssecEnabled(dns_settings)
    name_servers_changed = (
        update_mask.name_servers
        and not dns_util.NameServersEquivalent(
            registration.dnsSettings, dns_settings
        )
    )
    if (ds_records_present or new_ds_records) and name_servers_changed:
      log.warning('Name servers should not be changed if DS '
                  'records are present or added. Disable DNSSEC first and wait '
                  '24 hours before you change name servers. Otherwise '
                  'your domain may stop serving.')
      if not args.unsafe_dns_update:
        dns_util.PromptForUnsafeDnsUpdate()

    response = client.ConfigureDNS(
        registration_ref,
        dns_settings,
        update_mask,
        validate_only=args.validate_only)

    if args.validate_only:
      log.status.Print('The command will not have any effect because '
                       'validate-only flag is present.')
    else:
      response = util.WaitForOperation(api_version, response, args.async_)
      log.UpdatedResource(registration_ref.Name(), 'registration', args.async_)
    return response
