# -*- coding: utf-8 -*- #
# Copyright 2015 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.
"""Helper functions for DNS commands."""

from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals

from googlecloudsdk.api_lib.dns import util as api_util
from googlecloudsdk.api_lib.util import apis
from googlecloudsdk.command_lib.dns import flags


def ParseKey(algorithm, key_length, key_type, messages):
  """Generate a keyspec from the given (unparsed) command line arguments.

  Args:
    algorithm: (str) String mnemonic for the DNSSEC algorithm to be specified in
        the keyspec; must be a value from AlgorithmValueValuesEnum.
    key_length: (int) The key length value to include in the keyspec.
    key_type: (KeyTypeValueValuesEnum) Enum value for whether to create a
        keyspec for a KSK or a ZSK.
    messages: (module) Module (generally auto-generated by the API build rules)
        containing the API client's message classes.

  Returns:
    A messages.DnsKeySpec instance created from the given arguments.
  """

  key_spec = None

  if algorithm is not None or key_length is not None:
    spec_args = {}
    spec_args['keyType'] = key_type
    if algorithm is not None:
      spec_args['algorithm'] = messages.DnsKeySpec.AlgorithmValueValuesEnum(
          algorithm)
    if key_length is not None:
      spec_args['keyLength'] = key_length

    if spec_args:
      key_spec = messages.DnsKeySpec(**spec_args)

  return key_spec


def ParseDnssecConfigArgs(args, messages):
  """Parse all relevant command line arguments and generate a DNSSEC config.

  Args:
    args: (dict{str,(str|int)}) Dict of command line arguments; value type
        dependent on particular command line argument.
    messages: (module) Module (generally auto-generated by the API build rules)
        containing the API client's message classes.

  Returns:
    A messages.ManagedZoneDnsSecConfig instance populated from the given
    command line arguments.
  """

  dnssec_config = None
  key_specs = []

  ksk_key = ParseKey(args.ksk_algorithm, args.ksk_key_length,
                     messages.DnsKeySpec.KeyTypeValueValuesEnum.keySigning,
                     messages)
  if ksk_key is not None:
    key_specs.append(ksk_key)

  zsk_key = ParseKey(args.zsk_algorithm, args.zsk_key_length,
                     messages.DnsKeySpec.KeyTypeValueValuesEnum.zoneSigning,
                     messages)
  if zsk_key is not None:
    key_specs.append(zsk_key)

  dnssec_config_args = {}
  if key_specs:
    dnssec_config_args['defaultKeySpecs'] = key_specs
  if getattr(args, 'denial_of_existence', None) is not None:
    dnssec_config_args['nonExistence'] = (
        flags.GetDoeFlagMapper(messages).GetEnumForChoice(
            args.denial_of_existence))
  if args.dnssec_state is not None:
    dnssec_config_args['state'] = (
        flags.GetDnsSecStateFlagMapper(messages)
        .GetEnumForChoice(args.dnssec_state))

  if dnssec_config_args:
    dnssec_config = messages.ManagedZoneDnsSecConfig(**dnssec_config_args)

  return dnssec_config


def ParseManagedZoneForwardingConfig(server_list, messages):
  """Parses list of forwarding nameservers into ManagedZoneForwardingConfig.

  Args:
    server_list: (Sequence) List of IP addresses to use as forwarding targets
        for the DNS Managed Zone.
    messages: (module) Module (generally auto-generated by the API build rules)
        containing the API client's message classes.

  Returns:
    A messages.ManagedZoneForwardingConfig instance populated from the given
    command line arguments.
  """

  if not server_list:
    return None

  if server_list == ['']:  # Handle explicit unset case for update
    return messages.ManagedZoneForwardingConfig(targetNameServers=[])

  target_servers = [
      messages.ManagedZoneForwardingConfigNameServerTarget(ipv4Address=name)
      for name in server_list
  ]

  return messages.ManagedZoneForwardingConfig(targetNameServers=target_servers)


def ParseManagedZoneForwardingConfigWithForwardingPath(messages,
                                                       server_list=None,
                                                       private_server_list=None
                                                      ):
  """Parses list of forwarding nameservers into ManagedZoneForwardingConfig.

  Args:
    messages: (module) Module (generally auto-generated by the API build rules)
      containing the API client's message classes.
    server_list: (list) List of IP addresses to use as forwarding targets for
      the DNS Managed Zone that uses default forwarding logic (based on RFC1918
      check).
    private_server_list: (list) List of IP addresses to use as forwarding
      targets for the DNS Managed Zone that always use the private VPC path.

  Returns:
    A messages.ManagedZoneForwardingConfig instance populated from the given
    command line arguments.
  """
  target_servers = []
  default_enum = messages.ManagedZoneForwardingConfigNameServerTarget.ForwardingPathValueValuesEnum(
      0)
  private_enum = messages.ManagedZoneForwardingConfigNameServerTarget.ForwardingPathValueValuesEnum(
      1)
  if server_list is not None:
    target_servers += [
        messages.ManagedZoneForwardingConfigNameServerTarget(
            ipv4Address=name, forwardingPath=default_enum)
        for name in server_list
    ]
  if private_server_list is not None:
    target_servers += [
        messages.ManagedZoneForwardingConfigNameServerTarget(
            ipv4Address=name, forwardingPath=private_enum)
        for name in private_server_list
    ]

  return messages.ManagedZoneForwardingConfig(targetNameServers=target_servers)


def PolicyNetworkProcessor(parsed_value, version='v1'):
  """Build PolicyNetwork message from parsed_value."""
  # Parsed Value should be a list of compute.network resources
  messages = GetMessages(version)
  if not parsed_value:
    return []

  return [
      messages.PolicyNetwork(networkUrl=network_ref.SelfLink())
      for network_ref in parsed_value
  ]


def BetaPolicyNetworkProcessor(parsed_value):
  """Build Beta PolicyNetwork message from parsed_value."""
  # Parsed Value should be a list of compute.network resources
  return PolicyNetworkProcessor(parsed_value, version='v1beta2')


def TargetNameServerType(value, version='v1'):
  """Build a single TargetNameServer based on 'value'.

  Args:
    value: (str) A string representation of an IPV4 ip address representing the
      PrivateTargetNameServer.
    version: (str) A string indicating the version of the API to be used, should
      be one of 'v1', 'v1beta2' or 'v1alpha2'.

  Returns:
    A messages.PolicyAlternativeNameServerConfigTargetNameServer instance
    populated from the given ipv4 ip address.
  """
  messages = GetMessages(version)
  return messages.PolicyAlternativeNameServerConfigTargetNameServer(
      ipv4Address=value,
      forwardingPath=messages.PolicyAlternativeNameServerConfigTargetNameServer
      .ForwardingPathValueValuesEnum(0))


def PrivateTargetNameServerType(value, version='v1'):
  """Build a single PrivateTargetNameServer based on 'value'.

  Args:
    value: (str) A string representation of an IPV4 ip address representing the
      PrivateTargetNameServer.
    version: (str) A string indicating the version of the API to be used, should
      be one of 'v1', 'v1beta2' or 'v1alpha2'.

  Returns:
    A messages.PolicyAlternativeNameServerConfigTargetNameServer instance
    populated from the given ipv4 ip address.
  """
  messages = GetMessages(version)
  return messages.PolicyAlternativeNameServerConfigTargetNameServer(
      ipv4Address=value,
      forwardingPath=messages.PolicyAlternativeNameServerConfigTargetNameServer
      .ForwardingPathValueValuesEnum(1))


def ParseNetworks(value, project, version):
  """Build a list of PolicyNetworks from command line args."""
  if not value:
    return []
  registry = api_util.GetRegistry(version)
  networks = [
      registry.Parse(
          network_name,
          collection='compute.networks',
          params={'project': project}) for network_name in value
  ]
  return PolicyNetworkProcessor(networks, version)


def ParseAltNameServers(version, server_list=None, private_server_list=None):
  """Parses list of alternative nameservers into AlternativeNameServerConfig.

  Args:
    version: (str) A string indicating the version of the API to be used, should
      be one of 'v1', 'v1beta2' or 'v1alpha2'.
    server_list: (Sequence) List of IP addresses to use as forwarding targets
      for the DNS Managed Zone that uses default forwarding logic.
    private_server_list: (Sequence) List of IP addresses to use as forwarding
      targets for the DNS Managed Zone that always uses the private VPC path.

  Returns:
    A messages.PolicyAlternativeNameServerConfig instance populated from the
    given command line arguments.Only the not none server list will be parsed
    and
    an empty list will be returned if both are none.
  """
  if not server_list and not private_server_list:
    return None
  messages = GetMessages(version)
  result_list = []
  if server_list:
    result_list += [TargetNameServerType(ipv4, version) for ipv4 in server_list]
  if private_server_list:
    result_list += [
        PrivateTargetNameServerType(ipv4, version)
        for ipv4 in private_server_list
    ]
  return messages.PolicyAlternativeNameServerConfig(
      targetNameServers=result_list)


def GetMessages(version='v1'):
  return apis.GetMessagesModule('dns', version)
