# -*- 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.
"""Common flags for some of the SQL commands.

Flags are specified with functions that take in a single argument, the parser,
and add the newly constructed flag to that parser.

Example:

def AddFlagName(parser):
  parser.add_argument(
    '--flag-name',
    ... // Other flag details.
  )
"""

import dataclasses
import sys
from typing import Any
from googlecloudsdk.api_lib.compute import utils as compute_utils
from googlecloudsdk.api_lib.storage import storage_util
from googlecloudsdk.api_lib.util import apis
from googlecloudsdk.calliope import actions
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import parser_extensions
from googlecloudsdk.command_lib.kms import resource_args as kms_resource_args
from googlecloudsdk.command_lib.sql import constants
from googlecloudsdk.command_lib.util import completers
from googlecloudsdk.core import properties

messages = apis.GetMessagesModule('sql', 'v1beta4')
DEFAULT_INSTANCE_DATABASE_VERSION = 'MYSQL_8_0'
_BACKUPDR_MAX_RETENTION_DAYS = 3653

_IP_ADDRESS_PART = r'(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})'  # Match decimal 0-255
_CIDR_PREFIX_PART = r'([0-9]|[1-2][0-9]|3[0-2])'  # Match decimal 0-32
# Matches either IPv4 range in CIDR notation or a naked IPv4 address.
_CIDR_REGEX = r'{addr_part}(\.{addr_part}){{3}}(\/{prefix_part})?$'.format(
    addr_part=_IP_ADDRESS_PART, prefix_part=_CIDR_PREFIX_PART
)


class DatabaseCompleter(completers.ListCommandCompleter):

  def __init__(self, **kwargs):
    super(DatabaseCompleter, self).__init__(
        collection='sql.databases',
        api_version='v1beta4',
        list_command='sql databases list --uri',
        flags=['instance'],
        **kwargs
    )


class InstanceCompleter(completers.ListCommandCompleter):

  def __init__(self, **kwargs):
    super(InstanceCompleter, self).__init__(
        collection='sql.instances',
        list_command='sql instances list --uri',
        **kwargs
    )


class UserCompleter(completers.ListCommandCompleter):

  def __init__(self, **kwargs):
    super(UserCompleter, self).__init__(
        collection=None,  # TODO(b/62961917): Should be 'sql.users',
        api_version='v1beta4',
        list_command='sql users list --flatten=name[] --format=disable',
        flags=['instance'],
        **kwargs
    )


class _MajorVersionMatchList(list):

  def __contains__(self, database_version):
    """Check if <database_version> begins with a major_version in <self>."""
    return any(
        database_version.startswith(major_version) for major_version in self
    )


@dataclasses.dataclass(frozen=True)
class EntraIdCertForPrint:
  """Holds Entra ID certificate details for printing.

  Attrs:
    ssl_cert: The Entra ID certificate.
    status: The status of the certificate.
  """

  ssl_cert: Any
  status: Any


# The class for printing a server certificate.
class ServerCertForPrint:

  def __init__(self, ssl_cert, status, ca_cert=None):
    self.ssl_cert = ssl_cert
    self.status = status
    self.ca_cert = ca_cert


def AddInstance(parser, support_wildcard_instances=False):
  parser.add_argument(
      '--instance',
      '-i',
      required=True,
      completer=InstanceCompleter,
      help='Cloud SQL instance ID.'
      if not support_wildcard_instances
      else 'Cloud SQL instance ID or "-" for all instances.',
  )


def AddOptionalInstance(parser, support_wildcard_instances=False):
  parser.add_argument(
      '--instance',
      '-i',
      required=False,
      completer=InstanceCompleter,
      help='Cloud SQL instance ID.'
      if not support_wildcard_instances
      else 'Cloud SQL instance ID or "-" for all instances.',
  )


def AddInstanceArgument(parser):
  """Add the 'instance' argument to the parser."""
  parser.add_argument(
      'instance', completer=InstanceCompleter, help='Cloud SQL instance ID.'
  )


# The max storage size specified can be the int's max value, and min is 10.
def AddInstanceResizeLimit(parser):
  parser.add_argument(
      '--storage-auto-increase-limit',
      type=arg_parsers.BoundedInt(10, sys.maxsize, unlimited=True),
      help=(
          'Allows you to set a maximum storage capacity, in GB. Automatic'
          ' increases to your capacity will stop once this limit has been'
          ' reached. Default capacity is *unlimited*.'
      ),
  )


def AddUsername(parser):
  parser.add_argument(
      'username', completer=UserCompleter, help='Cloud SQL username.'
  )


def AddHost(parser):
  """Add the '--host' flag to the parser."""
  parser.add_argument(
      '--host',
      help=(
          "Cloud SQL user's hostname expressed as a specific IP address or"
          ' address range. `%` denotes an unrestricted hostname. Applicable'
          ' flag for MySQL instances; ignored for all other engines. Note, if'
          ' you connect to your instance using IP addresses, you must add your'
          ' client IP address as an authorized address, even if your hostname'
          ' is unrestricted. For more information, see [Configure'
          ' IP](https://cloud.google.com/sql/docs/mysql/configure-ip).'
      ),
  )


def AddAvailabilityType(parser, hidden=False):
  """Add the '--availability-type' flag to the parser."""
  availabilty_type_flag = base.ChoiceArgument(
      '--availability-type',
      required=False,
      choices={
          'regional': (
              'Provides high availability and is recommended for '
              'production instances; instance automatically fails over '
              'to another zone within your selected region.'
          ),
          'zonal': 'Provides no failover capability. This is the default.',
      },
      help_str='Specifies level of availability.',
      hidden=hidden,
  )
  availabilty_type_flag.AddToParser(parser)


def AddPassword(parser):
  parser.add_argument('--password', help="Cloud SQL user's password.")


def AddRootPassword(parser, hidden=False):
  """Add the root password field to the parser."""
  parser.add_argument(
      '--root-password',
      required=False,
      help="Root Cloud SQL user's password.",
      hidden=hidden,
  )


def AddPromptForPassword(parser):
  parser.add_argument(
      '--prompt-for-password',
      action='store_true',
      help=(
          "Prompt for the Cloud SQL user's password with character echo "
          'disabled. The password is all typed characters up to but not '
          'including the RETURN or ENTER key.'
      ),
  )


def AddType(parser, required=False):
  parser.add_argument(
      '--type',
      required=required,
      help=(
          "Cloud SQL user's type. It determines "
          'the method to authenticate the user during login. '
          'See the list of user types at '
          'https://cloud.google.com/sql/docs/postgres/admin-api/'
          'rest/v1beta4/SqlUserType'
      ),
  )


# Instance create and patch flags


def AddActivationPolicy(parser, hidden=False):
  base.ChoiceArgument(
      '--activation-policy',
      required=False,
      choices=['always', 'never'],
      default=None,
      hidden=hidden,
      help_str=(
          'Activation policy for this instance. This specifies when '
          'the instance should be activated and is applicable only when '
          'the instance state is `RUNNABLE`. The default is `always`. '
          'More information on activation policies can be found here: '
          'https://cloud.google.com/sql/docs/mysql/start-stop-restart-instance#activation_policy'
      ),
  ).AddToParser(parser)


def AddAssignIp(parser, hidden=False):
  parser.add_argument(
      '--assign-ip',
      help=(
          'Assign a public IP address to the instance. This is a public,'
          ' externally available IPv4 address that you can use to connect to'
          ' your instance when properly authorized.'
      ),
      hidden=hidden,
      action=arg_parsers.StoreTrueFalseAction,
  )


def AddEnableGooglePrivatePath(
    parser, show_negated_in_help=False, hidden=False
):
  kwargs = _GetKwargsForBoolFlag(show_negated_in_help)
  parser.add_argument(
      '--enable-google-private-path',
      required=False,
      help=(
          'Enable a private path for Google Cloud services. '
          'This flag specifies whether the instance is accessible to '
          'internal Google Cloud services such as BigQuery. '
          'This is only applicable to MySQL and PostgreSQL instances that '
          "don't use public IP. Currently, SQL Server isn't supported."
      ),
      hidden=hidden,
      **kwargs
  )


def AddAuthorizedGAEApps(parser, update=False):
  help_ = (
      'First Generation instances only. List of project IDs for App Engine '
      'applications running in the Standard environment that '
      'can access this instance.'
  )
  if update:
    help_ += (
        '\n\nThe value given for this argument *replaces* the existing list.'
    )
  parser.add_argument(
      '--authorized-gae-apps',
      type=arg_parsers.ArgList(min_length=1),
      metavar='APP',
      required=False,
      help=help_,
  )


def AddAuthorizedNetworks(parser, update=False, hidden=False):
  """Adds the `--authorized-networks` flag."""
  cidr_validator = arg_parsers.RegexpValidator(
      _CIDR_REGEX,
      (
          'Must be specified in CIDR notation, also known as '
          "'slash' notation (e.g. 192.168.100.0/24)."
      ),
  )
  help_ = (
      'The list of external networks that are allowed to connect to '
      'the instance. Specified in CIDR notation, also known as '
      "'slash' notation (e.g. 192.168.100.0/24)."
  )
  if update:
    help_ += (
        '\n\nThe value given for this argument *replaces* the existing list.'
    )
  parser.add_argument(
      '--authorized-networks',
      type=arg_parsers.ArgList(min_length=1, element_type=cidr_validator),
      metavar='NETWORK',
      required=False,
      default=[],
      help=help_,
      hidden=hidden,
  )


def AddBackupStartTime(parser, hidden=False):
  parser.add_argument(
      '--backup-start-time',
      required=False,
      help=(
          'Start time of daily backups, specified in the HH:MM format, in '
          'the UTC timezone.'
      ),
      hidden=hidden,
  )


def AddBackupLocation(parser, allow_empty, hidden=False):
  help_text = (
      'Choose where to store your backups. Backups are stored in the closest '
      'multi-region location to you by default. Only customize if needed.'
  )
  if allow_empty:
    help_text += ' Specify empty string to revert to default.'
  parser.add_argument(
      '--backup-location',
      required=False,
      help=help_text,
      hidden=hidden,
  )


def AddBackup(parser, hidden=False):
  parser.add_argument(
      '--backup',
      required=False,
      action='store_true',
      default=True,
      hidden=hidden,
      help='Enables daily backup.',
  )


def AddEnableFinalBackup(parser):
  parser.add_argument(
      '--enable-final-backup',
      required=False,
      action='store_true',
      default=None,
      help=(
          'Enables the final backup to be taken at the time of instance'
          ' deletion.'
      ),
  )


def AddFinalBackup(parser, hidden=False):
  parser.add_argument(
      '--final-backup',
      required=False,
      action=arg_parsers.StoreTrueFalseAction,
      hidden=hidden,
      help=(
          'Enables the final backup to be taken at the time of instance'
          ' deletion.'
      ),
  )


def AddFinalbackupRetentionDays(parser, hidden=False):
  help_text = (
      'Specifies number of days to retain final backup. The valid range is'
      ' between 1 and 365. For instances managed by BackupDR, the valid range'
      ' is between 1 day and 10 years ({_BACKUPDR_MAX_RETENTION_DAYS} days).'
      ' Default value is 30 days.'
  )
  parser.add_argument(
      '--final-backup-retention-days',
      type=arg_parsers.BoundedInt(
          1, _BACKUPDR_MAX_RETENTION_DAYS, unlimited=False
      ),
      required=False,
      hidden=hidden,
      help=help_text.format(
          _BACKUPDR_MAX_RETENTION_DAYS=_BACKUPDR_MAX_RETENTION_DAYS
      ),
  )


def AddBackupTtlDays(parser):
  """Add the flag to specify the retention days of the backup.

  Args:
    parser: The current parser to add this argument.
  """
  parser.add_argument(
      '--ttl-days',
      required=False,
      type=arg_parsers.BoundedInt(1, 365, unlimited=False),
      default=None,
      help=(
          ' Specifies the number of days to retain the final backup.'
          ' The valid range is between 1 and 365. The Default value is 30 days.'
          ' Provide either ttl-days or expiry-time.'
      ),
  )


def AddFinalbackupDescription(parser):
  parser.add_argument(
      '--final-backup-description',
      required=False,
      help='Provides description for the final backup going to be taken.',
  )


def AddBackupDescription(parser):
  parser.add_argument(
      '--backup-description',
      required=False,
      help='Provides description for the backup going to be taken.',
  )


def AddFinalBackupExpiryTimeArgument(parser):
  parser.add_argument(
      '--final-backup-expiry-time',
      type=arg_parsers.Datetime.Parse,
      required=False,
      help=(
          'Specifies the time at which the final backup will expire. Maximum'
          ' time allowed is 365 days. For instances managed by BackupDR, the'
          ' maximum time allowed is 10 years ({_BACKUPDR_MAX_RETENTION_DAYS}'
          ' days). Format: YYYY-MM-DDTHH:MM:SS.'
      ).format(_BACKUPDR_MAX_RETENTION_DAYS=_BACKUPDR_MAX_RETENTION_DAYS),
  )


def AddBackupExpiryTime(parser):
  """Add the flag to specify the expiration time of the backup.

  Args:
    parser: The current parser to add this argument.
  """
  parser.add_argument(
      '--expiry-time',
      required=False,
      type=arg_parsers.Datetime.Parse,
      default=None,
      help=(
          'Specifies when the final backup expires. The Maximum'
          ' time allowed is 365 days from now. Format: YYYY-MM-DDTHH:MM:SS.'
          ' Provide either ttl-days or expiry-time.'
      ),
  )


# Currently, MAX_BACKUP_RETENTION_COUNT=365, and MIN_BACKUP_RETENTION_COUNT=1.
def AddRetainedBackupsCount(parser, hidden=False):
  help_text = (
      'How many backups to keep. The valid range is between 1 and 365. '
      'Default value is 7 for Enterprise edition instances. For '
      'Enterprise_Plus, default value is 15. Applicable only if --no-backups '
      'is not specified.'
  )
  parser.add_argument(
      '--retained-backups-count',
      type=arg_parsers.BoundedInt(1, 365, unlimited=False),
      help=help_text,
      hidden=hidden,
  )


# Currently, MAX_TRANSACTION_LOG_RETENTION_DAYS=35, and
# MIN_TRANSACTION_LOG_RETENTION_DAYS=1.
def AddRetainedTransactionLogDays(parser, hidden=False):
  help_text = (
      'How many days of transaction logs to keep. The valid range is between '
      '1 and 35. Only use this option when point-in-time recovery is enabled. '
      'If logs are stored on disk, storage size for transaction logs could '
      'increase when the number of days for log retention increases. '
      'For Enterprise, default and max retention values are 7 and 7 '
      'respectively. For Enterprise_Plus, default and max '
      'retention values are 14 and 35.'
  )
  parser.add_argument(
      '--retained-transaction-log-days',
      type=arg_parsers.BoundedInt(1, 35, unlimited=False),
      help=help_text,
      hidden=hidden,
  )


def AddDatabaseFlags(parser, update=False, hidden=False):
  """Adds the `--database-flags` flag."""
  help_ = (
      'Comma-separated list of database flags to set on the '
      'instance. Use an equals sign to separate flag name and value. '
      'Flags without values, like skip_grant_tables, can be written '
      'out without a value after, e.g., `skip_grant_tables=`. Use '
      'on/off for booleans. View the Instance Resource API for allowed '
      'flags. (e.g., `--database-flags max_allowed_packet=55555,'
      'skip_grant_tables=,log_output=1`)'
  )
  if update:
    help_ += (
        '\n\nThe value given for this argument *replaces* the existing list.'
    )
  parser.add_argument(
      '--database-flags',
      type=arg_parsers.ArgDict(min_length=1),
      metavar='FLAG=VALUE',
      required=False,
      help=help_,
      hidden=hidden,
  )


def AddTags(parser, hidden=False):
  """Adds the `--tags` flag."""
  help_ = (
      'Comma-separated list of tags to set on the instance. Use an equals sign'
      'to separate tag name and value.(e.g., `--tags tag1:value1,tag2=value2`)'
  )
  parser.add_argument(
      '--tags',
      type=arg_parsers.ArgDict(min_length=1),
      metavar='TAG=VALUE',
      required=False,
      help=help_,
      hidden=hidden,
  )


def AddDatabaseVersion(
    parser,
    restrict_choices=True,
    hidden=False,
    support_default_version=True,
    additional_help_text=None,
):
  """Adds `--database-version` to the parser with choices restricted or not."""
  # Section for engine-specific content.
  # This section is auto-generated by //cloud/storage_fe/sql/sync_engines.
  # Do not make manual edits.
  choices = [
      'MYSQL_5_6',
      'MYSQL_5_7',
      'MYSQL_8_0',
      'MYSQL_8_4',
      'MYSQL_9_7',
      'POSTGRES_9_6',
      'POSTGRES_10',
      'POSTGRES_11',
      'POSTGRES_12',
      'POSTGRES_13',
      'POSTGRES_14',
      'POSTGRES_15',
      'POSTGRES_16',
      'POSTGRES_17',
      'POSTGRES_18',
      'SQLSERVER_2017_EXPRESS',
      'SQLSERVER_2017_WEB',
      'SQLSERVER_2017_STANDARD',
      'SQLSERVER_2017_ENTERPRISE',
      'SQLSERVER_2019_EXPRESS',
      'SQLSERVER_2019_WEB',
      'SQLSERVER_2019_STANDARD',
      'SQLSERVER_2019_ENTERPRISE',
      'SQLSERVER_2022_EXPRESS',
      'SQLSERVER_2022_WEB',
      'SQLSERVER_2022_STANDARD',
      'SQLSERVER_2022_ENTERPRISE',
      'SQLSERVER_2025_EXPRESS',
      'SQLSERVER_2025_STANDARD',
      'SQLSERVER_2025_ENTERPRISE',
  ]
  # End of engine-specific content.

  help_text_unspecified_part = (
      DEFAULT_INSTANCE_DATABASE_VERSION + ' is used.'
      if support_default_version
      else 'no changes occur.'
  )
  help_text = (
      'The database engine type and versions. If left unspecified, '
      + help_text_unspecified_part
      + ' See the list of database versions at '
      + 'https://cloud.google.com/sql/docs/mysql/admin-api/rest/v1beta4/SqlDatabaseVersion.'
  )

  if restrict_choices:
    help_text += (
        ' Apart from listed major versions, DATABASE_VERSION also accepts'
        ' supported minor versions.'
    )
  if additional_help_text:
    help_text += additional_help_text

  parser.add_argument(
      '--database-version',
      required=False,
      default=DEFAULT_INSTANCE_DATABASE_VERSION
      if support_default_version
      else None,
      choices=_MajorVersionMatchList(choices) if restrict_choices else None,
      help=help_text,
      hidden=hidden,
  )


def AddIncludeReplicasForMajorVersionUpgrade(parser, hidden=False):
  """Adds `--include-replicas-for-major-version-upgrade` to the parser with boolean choice."""
  help_text = (
      'Enable the major version upgrade of replicas when the in-place '
      'major version upgrade of a primary instance is initated with '
      '`--database-version`. Use '
      '`--include-replicas-for-major-version-upgrade`  to enable and '
      '`--no-include-replicas-for-major--version-upgrade` to disable.'
  )
  parser.add_argument(
      '--include-replicas-for-major-version-upgrade',
      action=arg_parsers.StoreTrueFalseAction,
      help=help_text,
      hidden=hidden,
  )


def AddCPU(parser, hidden=False):
  parser.add_argument(
      '--cpu',
      type=int,
      required=False,
      help=(
          'Whole number value indicating how many cores are desired in the'
          ' machine. Both --cpu and --memory must be specified if a custom'
          ' machine type is desired, and the --tier flag must be omitted.'
          '--cpu and --memory flags are not compatible with the Enterprise Plus'
          ' edition. These flags should not be used when creating an Enterprise'
          ' Plus edition, as the machine configuration is determined by the'
          ' --tier flag instead.'
      ),
      hidden=hidden,
  )


def _GetKwargsForBoolFlag(show_negated_in_help):
  if show_negated_in_help:
    return {
        'action': arg_parsers.StoreTrueFalseAction,
    }
  else:
    return {'action': 'store_true', 'default': None}


def AddInstanceCollation(parser, hidden=False):
  parser.add_argument(
      '--collation',
      help=(
          'Cloud SQL server-level collation setting, which specifies '
          'the set of rules for comparing characters in a character set.'
      ),
      hidden=hidden,
  )


def AddEnableBinLog(parser, show_negated_in_help=False, hidden=False):
  kwargs = _GetKwargsForBoolFlag(show_negated_in_help)
  parser.add_argument(
      '--enable-bin-log',
      required=False,
      help=(
          'Allows for data recovery from a specific point in time, down to a '
          'fraction of a second. Must have automatic backups enabled to use. '
          'Make sure storage can support at least 7 days of logs.'
      ),
      hidden=hidden,
      **kwargs
  )


def AddEnablePointInTimeRecovery(
    parser, show_negated_in_help=False, hidden=False
):
  kwargs = _GetKwargsForBoolFlag(show_negated_in_help)
  parser.add_argument(
      '--enable-point-in-time-recovery',
      required=False,
      help=(
          'Allows for data recovery from a specific point in time, down to a '
          'fraction of a second, using write-ahead logs. Must have automatic '
          'backups enabled to use. Make sure storage can support at least 7 '
          'days of logs.'
      ),
      hidden=hidden,
      **kwargs
  )


def AddExternalMasterGroup(parser):
  """Add flags to the parser for creating an external master and replica."""

  # Group for creating external primary instances.
  external_master_group = parser.add_group(
      required=False,
      help='Options for creating a wrapper for an external data source.',
  )
  external_master_group.add_argument(
      '--source-ip-address',
      required=True,
      type=compute_utils.IPV4Argument,
      help=(
          'Public IP address used to connect to and replicate from '
          'the external data source.'
      ),
  )
  external_master_group.add_argument(
      '--source-port',
      type=arg_parsers.BoundedInt(lower_bound=1, upper_bound=65535),
      # Default MySQL port number.
      default=3306,
      help=(
          'Port number used to connect to and replicate from the '
          'external data source.'
      ),
  )

  # Group for creating replicas of external primary instances.
  internal_replica_group = parser.add_group(
      required=False,
      help=(
          'Options for creating an internal replica of an external data source.'
      ),
  )
  internal_replica_group.add_argument(
      '--master-username',
      required=True,
      help='Name of the replication user on the external data source.',
  )

  # TODO(b/78648703): Make group required when mutex required status is fixed.
  # For entering the password of the replication user of an external primary.
  master_password_group = internal_replica_group.add_group(
      'Password group.', mutex=True
  )
  master_password_group.add_argument(
      '--master-password',
      help='Password of the replication user on the external data source.',
  )
  master_password_group.add_argument(
      '--prompt-for-master-password',
      action='store_true',
      help=(
          'Prompt for the password of the replication user on the '
          'external data source. The password is all typed characters up '
          'to but not including the RETURN or ENTER key.'
      ),
  )
  internal_replica_group.add_argument(
      '--master-dump-file-path',
      required=True,
      type=storage_util.ObjectReference.FromArgument,
      help=(
          'Path to the MySQL dump file in Google Cloud Storage from '
          'which the seed import is made. The URI is in the form '
          'gs://bucketName/fileName. Compressed gzip files (.gz) are '
          'also supported.'
      ),
  )

  # For specifying SSL certs for connecting to an external primary.
  credential_group = internal_replica_group.add_group(
      'Client and server credentials.', required=False
  )
  credential_group.add_argument(
      '--master-ca-certificate-path',
      required=True,
      help=(
          'Path to a file containing the X.509v3 (RFC5280) PEM encoded '
          "certificate of the CA that signed the external data source's "
          'certificate.'
      ),
  )

  # For specifying client certs for connecting to an external primary.
  client_credential_group = credential_group.add_group(
      'Client credentials.', required=False
  )
  client_credential_group.add_argument(
      '--client-certificate-path',
      required=True,
      help=(
          'Path to a file containing the X.509v3 (RFC5280) PEM encoded '
          'certificate that will be used by the replica to authenticate '
          'against the external data source.'
      ),
  )
  client_credential_group.add_argument(
      '--client-key-path',
      required=True,
      help=(
          'Path to a file containing the unencrypted PKCS#1 or PKCS#8 '
          'PEM encoded private key associated with the '
          'clientCertificate.'
      ),
  )


def AddFailoverFlag(parser, show_negated_in_help=True):
  kwargs = _GetKwargsForBoolFlag(show_negated_in_help)
  parser.add_argument(
      '--failover',
      required=False,
      help='Whether the promote operation is a failover.',
      **kwargs
  )


def AddFailoverReplicaName(parser, hidden=False):
  parser.add_argument(
      '--failover-replica-name',
      required=False,
      hidden=hidden,
      help='Also create a failover replica with the specified name.',
  )


def AddFailoverDrReplicaName(parser, hidden=False):
  parser.add_argument(
      '--failover-dr-replica-name',
      required=False,
      hidden=hidden,
      help=(
          'Set a Disaster Recovery (DR) replica with the specified name for '
          'the primary instance. This must be one of the existing cross region '
          'replicas of the primary instance. Flag is only available for MySQL '
          'and PostgreSQL database instances.'
      ),
  )


def AddClearFailoverDrReplicaName(parser, hidden=False):
  kwargs = _GetKwargsForBoolFlag(False)
  parser.add_argument(
      '--clear-failover-dr-replica-name',
      required=False,
      hidden=hidden,
      help=(
          'Clear the DR replica setting for the primary instance. Flag is only '
          'available for MySQL and PostgreSQL database instances.'
      ),
      **kwargs
  )


def AddMasterInstanceName(parser, hidden=False):
  parser.add_argument(
      '--master-instance-name',
      required=False,
      hidden=hidden,
      help=(
          'Name of the instance which will act as master in the '
          'replication setup. The newly created instance will be a read '
          'replica of the specified master instance.'
      ),
  )


def AddStorageType(parser, hidden=False):
  parser.add_argument(
      '--storage-type',
      required=False,
      choices=(
          ['SSD', 'HDD', 'HYPERDISK_BALANCED']
          if properties.IsDefaultUniverse()
          else ['HYPERDISK_BALANCED']
      ),
      default=None,
      hidden=hidden,
      help=(
          'The storage type for the instance, determined by the selected'
          ' machine type.'
      ),
  )


def AddReplicaType(parser, hidden=False):
  parser.add_argument(
      '--replica-type',
      choices=['READ', 'FAILOVER'],
      hidden=hidden,
      help='The type of replica to create.',
  )


def AddRequireSsl(parser, hidden=False):
  parser.add_argument(
      '--require-ssl',
      required=False,
      action='store_true',
      default=None,
      hidden=hidden,
      help='Specified if users connecting over IP must use SSL.',
  )


def AddFollowGAEApp(parser):
  parser.add_argument(
      '--follow-gae-app',
      required=False,
      help=(
          'First Generation instances only. The App Engine app '
          'this instance should follow. It must be in the same region as '
          'the instance. WARNING: Instance may be restarted.'
      ),
  )


def AddMaintenanceReleaseChannel(parser, hidden=False):
  base.ChoiceArgument(
      '--maintenance-release-channel',
      choices={
          'week5': (
              'week5 updates release after the production '
              'updates. Use the week5 channel to receive a 5 week '
              'advance notification about the upcoming maintenance, '
              'so you can prepare your application for the release.'
          ),
          'production': (
              'Production updates are stable and recommended '
              'for applications in production.'
          ),
          'preview': (
              'Preview updates release prior to production '
              'updates. You may wish to use the preview channel '
              'for dev/test applications so that you can preview '
              'their compatibility with your application prior '
              'to the production release.'
          ),
      },
      help_str=(
          "Which channel's updates to apply during the maintenance "
          'window. If not specified, Cloud SQL chooses the timing of '
          'updates to your instance.'
      ),
      hidden=hidden,
  ).AddToParser(parser)


def AddMaintenanceWindowDay(parser, hidden=False):
  parser.add_argument(
      '--maintenance-window-day',
      choices=arg_parsers.DayOfWeek.DAYS,
      type=arg_parsers.DayOfWeek.Parse,
      help='Day of week for maintenance window, in UTC time zone.',
      hidden=hidden,
  )


def AddMaintenanceWindowHour(parser, hidden=False):
  parser.add_argument(
      '--maintenance-window-hour',
      type=arg_parsers.BoundedInt(lower_bound=0, upper_bound=23),
      help='Hour of day for maintenance window, in UTC time zone.',
      hidden=hidden,
  )


def AddDenyMaintenancePeriodStartDate(parser, hidden=False):
  parser.add_argument(
      '--deny-maintenance-period-start-date',
      help=(
          'Date when the deny maintenance period begins, that is'
          " ``2020-11-01''."
      ),
      hidden=hidden,
  )


def AddDenyMaintenancePeriodEndDate(parser, hidden=False):
  parser.add_argument(
      '--deny-maintenance-period-end-date',
      help=(
          "Date when the deny maintenance period ends, that is ``2021-01-10''."
      ),
      hidden=hidden,
  )


def AddDenyMaintenancePeriodTime(parser, hidden=False):
  parser.add_argument(
      '--deny-maintenance-period-time',
      help=(
          'Time when the deny maintenance period starts or ends, that is'
          " ``05:00:00''."
      ),
      hidden=hidden,
  )


def AddInsightsConfigQueryInsightsEnabled(
    parser, show_negated_in_help=False, hidden=False
):
  kwargs = _GetKwargsForBoolFlag(show_negated_in_help)
  parser.add_argument(
      '--insights-config-query-insights-enabled',
      required=False,
      help="""Enable query insights feature to provide query and query plan
        analytics.""",
      hidden=hidden,
      **kwargs
  )


def AddInsightsConfigEnhancedQueryInsightsEnabled(
    parser, show_negated_in_help=False, hidden=False
):
  kwargs = _GetKwargsForBoolFlag(show_negated_in_help)
  parser.add_argument(
      '--insights-config-enhanced-query-insights-enabled',
      required=False,
      help="""Enable enhanced query insights for Enterprise Plus edition to provide more detailed query
        analytics.""",
      hidden=hidden,
      **kwargs
  )


def AddInsightsConfigQueryStringLength(parser, hidden=False):
  parser.add_argument(
      '--insights-config-query-string-length',
      required=False,
      type=arg_parsers.BoundedInt(lower_bound=256, upper_bound=100000),
      help="""Sets the default query length limit. For Cloud SQL Enterprise edition,
      the range is from 256 to 4500 (in bytes) and the default query length is 1024
      bytes. For Cloud SQL Enterprise Plus edition, the range is from 1024 to 100,000
      (in bytes) and the default query length is 10,000 bytes.""",
      hidden=hidden,
  )


def AddInsightsConfigRecordApplicationTags(
    parser, show_negated_in_help=False, hidden=False
):
  kwargs = _GetKwargsForBoolFlag(show_negated_in_help)
  parser.add_argument(
      '--insights-config-record-application-tags',
      required=False,
      help="""Allow application tags to be recorded by the query insights
        feature.""",
      hidden=hidden,
      **kwargs
  )


def AddInsightsConfigRecordClientAddress(
    parser, show_negated_in_help=False, hidden=False
):
  kwargs = _GetKwargsForBoolFlag(show_negated_in_help)
  parser.add_argument(
      '--insights-config-record-client-address',
      required=False,
      help="""Allow the client address to be recorded by the query insights
        feature.""",
      hidden=hidden,
      **kwargs
  )


def AddInsightsConfigQueryPlansPerMinute(parser, hidden=False):
  parser.add_argument(
      '--insights-config-query-plans-per-minute',
      required=False,
      type=arg_parsers.BoundedInt(lower_bound=0, upper_bound=20),
      help="""Number of query plans to sample every minute.
        Default value is 5. Allowed range: 0 to 20.""",
      hidden=hidden,
  )


def AddMemory(parser, hidden=False):
  parser.add_argument(
      '--memory',
      type=arg_parsers.BinarySize(),
      required=False,
      help=(
          'Whole number value indicating how much memory is desired in the'
          ' machine. A size unit should be provided (eg. 3072MiB or 9GiB) - if'
          ' no units are specified, GiB is assumed. Both --cpu and --memory'
          ' must be specified if a custom machine type is desired, and the'
          ' --tier flag must be omitted. --cpu and --memory flags are not'
          ' compatible with the Enterprise Plus edition. These flags should not'
          ' be used when creating an Enterprise Plus edition, as the machine'
          ' configuration is determined by the --tier flag instead.'
      ),
      hidden=hidden,
  )


def AddNetwork(parser, hidden=False):
  """Adds the `--network` flag to the parser."""
  parser.add_argument(
      '--network',
      help=(
          'Network in the current project that the instance will be part '
          'of. To specify using a network with a shared VPC, use the full '
          "URL of the network. For an example host project, 'testproject', "
          "and shared network, 'testsharednetwork', this would use the "
          'form: '
          '`--network`=`projects/testproject/global/networks/'
          'testsharednetwork`'
      ),
      hidden=hidden,
  )


def AddClearNetwork(parser, hidden=False):
  """Adds the `--clear-network` flag to the parser."""
  parser.add_argument(
      '--clear-network',
      required=False,
      action='store_true',
      help=(
          'Clears the network setting. This is useful to restore a backup to a'
          ' different project or region where the original network'
          " configuration isn't available."
      ),
      hidden=hidden,
  )


def AddClearDiskEncryption(
    parser: parser_extensions.Namespace, hidden: bool = False
) -> None:
  """Adds the `--clear-disk-encryption` flag to the parser."""
  parser.add_argument(
      '--clear-disk-encryption',
      required=False,
      help='Disables CMEK in the restored instance.',
      hidden=hidden,
  )


def AddAllocatedIpRangeName(parser):
  """Adds the `--allocated-ip-range-name` flag to the parser."""
  parser.add_argument(
      '--allocated-ip-range-name',
      required=False,
      help=(
          'The name of the IP range allocated for a Cloud SQL instance with '
          'private network connectivity. For example: '
          "'google-managed-services-default'. If set, the instance IP is "
          'created in the allocated range represented by this name.'
      ),
  )


def AddMaintenanceVersion(parser):
  """Adds the `--maintenance-version` flag to the parser."""
  parser.add_argument(
      '--maintenance-version',
      required=False,
      help='The desired maintenance version of the instance.',
  )


def AddSimulateMaintenanceEvent(parser):
  """Adds the `--simulate-maintenance-event` flag to the parser."""
  parser.add_argument(
      '--simulate-maintenance-event',
      action='store_true',
      required=False,
      help=(
          'Simulate a maintenance event without changing the version. Only'
          ' applicable to instances that support near-zero downtime planned'
          ' maintenance.'
      ),
  )


def AddReconcilePsaNetworking(parser):
  """Adds the `--reconcile-psa-networking` flag to the parser."""
  parser.add_argument(
      '--reconcile-psa-networking',
      action='store_true',
      required=False,
      help=(
          "Reconciles the instance's PSA networking configuration. If the "
          'instance is already on a PSA network, the DNS zone and records '
          'associated with the PSA write endpoint are either added if missing '
          'or updated if incorrect.'
      ),
  )


def AddSqlServerAudit(parser, hidden=False):
  """Adds SQL Server audit related flags to the parser."""
  parser.add_argument(
      '--audit-bucket-path',
      required=False,
      help=(
          'The location, as a Cloud Storage bucket, to which audit files are '
          'uploaded. The URI is in the form gs://bucketName/folderName. Only '
          'available for SQL Server instances.'
      ),
      hidden=hidden,
  )

  parser.add_argument(
      '--audit-retention-interval',
      default=None,
      type=arg_parsers.Duration(upper_bound='7d'),
      required=False,
      help=(
          'The number of days for audit log retention on disk, for example, 3d'
          'for 3 days. Only available for SQL Server instances.'
      ),
      hidden=hidden,
  )

  parser.add_argument(
      '--audit-upload-interval',
      default=None,
      type=arg_parsers.Duration(upper_bound='720m'),
      required=False,
      help=(
          'How often to upload audit logs (audit files), for example, 30m'
          'for 30 minutes. Only available for SQL Server instances.'
      ),
      hidden=hidden,
  )


def AddReplication(parser, hidden=False):
  base.ChoiceArgument(
      '--replication',
      required=False,
      choices=['synchronous', 'asynchronous'],
      default=None,
      help_str=(
          'Type of replication this instance uses. The default is synchronous.'
      ),
      hidden=hidden,
  ).AddToParser(parser)


def AddStorageAutoIncrease(parser, show_negated_in_help=True, hidden=False):
  kwargs = _GetKwargsForBoolFlag(show_negated_in_help)
  parser.add_argument(
      '--storage-auto-increase',
      help=(
          'Storage size can be increased, but it cannot be decreased; '
          'storage increases are permanent for the life of the instance. '
          'With this setting enabled, a spike in storage requirements '
          'can result in permanently increased storage costs for your '
          'instance. However, if an instance runs out of available space, '
          'it can result in the instance going offline, dropping existing '
          'connections. This setting is enabled by default.'
      ),
      hidden=hidden,
      **kwargs
  )


def AddStorageSize(parser, hidden=False):
  parser.add_argument(
      '--storage-size',
      type=arg_parsers.BinarySize(
          lower_bound='10GB',
          upper_bound='65536GB',
          suggested_binary_size_scales=['GB'],
      ),
      help=(
          'Amount of storage allocated to the instance. Must be an integer '
          'number of GB. The default is 10GB. Information on storage '
          'limits can be found here: '
          'https://cloud.google.com/sql/docs/quotas#storage_limits'
      ),
      hidden=hidden,
  )


def AddStorageProvisionedIops(parser, hidden=False):
  help_text = (
      'Indicates how many IOPS to provision for the data disk. This sets the'
      ' number of I/O operations per second that the disk can handle.'
  )
  parser.add_argument(
      '--storage-provisioned-iops',
      type=arg_parsers.BoundedInt(lower_bound=1, unlimited=True),
      required=False,
      help=help_text,
      hidden=hidden,
  )


def AddStorageProvisionedThroughput(parser, hidden=False):
  help_text = (
      'Indicates how much throughput to provision for the data disk. This sets'
      ' the throughput in MB per second that the disk can handle.'
  )
  parser.add_argument(
      '--storage-provisioned-throughput',
      type=arg_parsers.BoundedInt(lower_bound=1, unlimited=True),
      required=False,
      help=help_text,
      hidden=hidden,
  )


def AddStorageSizeForStorageShrink(parser):
  parser.add_argument(
      '--storage-size',
      type=arg_parsers.BinarySize(
          lower_bound='10GB',
          upper_bound='65536GB',
          suggested_binary_size_scales=['GB'],
      ),
      help=(
          'The target storage size must be an integer that represents the'
          ' number of GB.  For example, --storage-size=10GB. Only supported for'
          ' primary or standalone instances. You can\'t set a target storage'
          ' size for a replica instance.'
      ),
  )


def AddTier(parser, is_patch=False, hidden=False):
  """Adds '--tier' flag to the parser."""
  help_text = (
      "Machine type for a shared-core instance e.g. ``db-g1-small''. "
      'For all other instances, instead of using tiers, customize '
      'your instance by specifying its CPU and memory. You can do so '
      'with the `--cpu` and `--memory` flags. Learn more about how '
      'CPU and memory affects pricing: '
      'https://cloud.google.com/sql/pricing.'
  )
  if is_patch:
    help_text += ' WARNING: Instance will be restarted.'

  parser.add_argument(
      '--tier', '-t', required=False, help=help_text, hidden=hidden
  )


def AddEdition(parser, hidden=False):
  """Adds '-edition-' flag to the parser."""
  edition_flag = base.ChoiceArgument(
      '--edition',
      required=False,
      choices=['enterprise', 'enterprise-plus'],
      default=None,
      help_str='Specifies the edition of Cloud SQL instance.',
      hidden=hidden,
  )
  edition_flag.AddToParser(parser)


def AddZone(parser, help_text, hidden=False):
  """Adds the mutually exclusive `--gce-zone` and `--zone` to the parser."""
  zone_group = parser.add_mutually_exclusive_group()
  zone_group.add_argument(
      '--gce-zone',
      required=False,
      action=actions.DeprecationAction(
          '--gce-zone',
          removed=False,
          warn=(
              'Flag `{flag_name}` is deprecated and will be removed by '
              'release 255.0.0. Use `--zone` instead.'
          ),
      ),
      help=help_text,
      hidden=hidden,
  )

  AddZonesPrimarySecondary(zone_group, help_text, hidden=hidden)


def AddZonesPrimarySecondary(parser, help_text, hidden=False):
  """Adds the `--zone` and `--secondary-zone` to the parser."""

  zone_group = parser.add_group(required=False, hidden=hidden)
  zone_group.add_argument(
      '--zone', required=False, help=help_text, hidden=hidden
  )
  zone_group.add_argument(
      '--secondary-zone',
      required=False,
      help=(
          'Preferred secondary Compute Engine zone '
          '(e.g. us-central1-a, us-central1-b, etc.).'
      ),
      hidden=hidden,
  )


def AddRegion(
    parser, hidden=False, specify_default_region=True, required=False
):
  parser.add_argument(
      '--region',
      required=required,
      default='us-central' if specify_default_region and not required else None,
      help=(
          'Regional location (e.g. asia-east1, us-east1). See the full '
          'list of regions at '
          'https://cloud.google.com/sql/docs/instance-locations.'
      ),
      hidden=hidden,
  )


# TODO(b/31989340): add remote completion
# TODO(b/73362371): Make specifying a location required.
def AddLocationGroup(parser, hidden=False, specify_default_region=True):
  location_group = parser.add_mutually_exclusive_group(hidden=hidden)
  AddRegion(
      location_group,
      hidden=hidden,
      specify_default_region=specify_default_region,
  )
  AddZone(
      location_group,
      help_text=(
          'Preferred Compute Engine zone (e.g. us-central1-a, '
          'us-central1-b, etc.).'
      ),
      hidden=hidden,
  )


# Database specific flags


def AddDatabaseName(parser):
  parser.add_argument(
      'database', completer=DatabaseCompleter, help='Cloud SQL database name.'
  )


def AddCharset(parser):
  parser.add_argument(
      '--charset',
      help=(
          'Cloud SQL database charset setting, which specifies the set of'
          ' symbols and encodings used to store the data in your database. Each'
          ' database version may support a different set of charsets.'
      ),
  )


def AddCollation(parser, custom_help=None):
  parser.add_argument(
      '--collation',
      help=custom_help
      or 'Cloud SQL database collation setting, which specifies the set of rules for comparing characters in a character set. Each database version may support a different set of collations. For PostgreSQL database versions, this may only be set to the collation of the template database.',
  )


def AddOperationArgument(parser):
  parser.add_argument(
      'operation',
      nargs='+',
      help='An identifier that uniquely identifies the operation.',
  )


# Blue-green deployment flags


def AddDeploymentId(parser):
  """Add the deployment ID argument to the parser."""
  parser.add_argument(
      'deployment',
      help='The ID of the blue-green deployment.',
  )


def AddSourceInstance(parser):
  """Add the --source-instance flag to the parser."""
  parser.add_argument(
      '--source-instance',
      required=True,
      completer=InstanceCompleter,
      help='Cloud SQL instance to be used as source for the blue-green deployment.',
  )


def AddDeploymentDescription(parser):
  """Add the --description flag to the parser."""
  parser.add_argument(
      '--description',
      required=False,
      help='User-provided description for the blue-green deployment.',
  )


def AddTargetDatabaseVersion(parser):
  """Add the --target-database-version flag to the parser."""
  parser.add_argument(
      '--target-database-version',
      required=False,
      help='Database version for the target instance, for major version upgrade.',
  )


def AddDeleteOldSource(parser):
  """Add the --delete-old-source flag to the parser."""
  parser.add_argument(
      '--delete-old-source',
      action='store_true',
      help=(
          'Delete the old source instance. This flag is only applicable when'
          ' deleting a deployment after a switchover has been successfully'
          ' completed.\n\nIf deleting a deployment before switchover or after a'
          ' failed switchover, the target instance will be deleted by default,'
          ' and this flag will be ignored.'
      ),
  )


def AddShowConfigDiff(parser):
  """Add the --show-config-diff flag to the parser."""
  parser.add_argument(
      '--show-config-diff',
      action='store_true',
      help=(
          'Show the configuration difference between source and target '
          'instances.'
      ),
  )


# Instance export / import flags.


def AddUriArgument(parser, help_text):
  """Add the 'uri' argument to the parser, with help text help_text."""
  parser.add_argument('uri', help=help_text)


def AddBakImportUriArgument(parser, help_text):
  """Add the 'uri' argument to the parser, with help text help_text."""
  parser.add_argument('uri', help=help_text, nargs='?', default='')


def AddOffloadArgument(parser):
  """Add the 'offload' argument to the parser."""
  parser.add_argument(
      '--offload',
      action='store_true',
      help=(
          'Offload an export to a temporary instance. Doing so reduces strain '
          'on source instances and allows other operations to be performed '
          'while the export is in progress.'
      ),
  )


def AddParallelArgument(parser, operation):
  """Add the 'parallel' argument to the parser."""
  help_text = (
      'Perform a parallel {operation}. This flag is only applicable to'
      ' MySQL and Postgres.'
  ).format(operation=operation)
  if operation == 'export':
    help_text += (
        ' When this flag is used, the URI specifies a folder in the'
        ' Cloud Storage bucket. After the parallel export completes, you will'
        ' have multiple files in that folder.'
    )
  elif operation == 'import':
    help_text += (
        ' When this flag is used, the URI specifies a folder in the'
        ' Cloud Storage bucket where the multiple files to be imported are'
        ' located.'
    )
  parser.add_argument(
      '--parallel',
      action='store_true',
      help=help_text,
  )


def AddThreadsArgument(parser, operation):
  """Add the 'threads' argument to the parser."""
  parser.add_argument(
      '--threads',
      type=arg_parsers.BoundedInt(unlimited=True),
      help=(
          'Specifies the number of threads to use for the parallel {operation}.'
          ' If `--parallel` is specified and this flag is not provided, Cloud'
          ' SQL uses a default thread count to optimize performance.'
      ).format(operation=operation),
  )


def AddCleanExportArgument(parser):
  """Add the 'clean' argument to the parser."""
  parser.add_argument(
      '--clean',
      action='store_true',
      help=(
          'Include SQL statements (DROP <object>) required to'
          ' drop database objects prior to import; corresponds to the clean'
          ' flag for pg_dump. Only applies to PostgreSQL non-parallel exports.'
      ),
  )


def AddIfExistsExportsArgument(parser):
  """Add the 'if-exists' argument to the parser."""
  parser.add_argument(
      '--if-exists',
      action='store_true',
      help=(
          'Include an SQL statement (IF EXISTS) with each drop statement'
          ' produced by the clean flag; corresponds to the if-exists flag'
          ' for pg_dump. Only applies to PostgreSQL non-parallel exports.'
      ),
  )


def AddCleanImportArgument(parser):
  """Add the 'clean' argument to the parser."""
  parser.add_argument(
      '--clean',
      action='store_true',
      help=(
          'Option to clean (DROP) database objects before recreating them.'
          ' corresponds to the clean flag for pg_restore. Only applies if'
          ' --parallel is set. PostgreSQL only.'
      ),
  )


def AddIfExistsImportsArgument(parser):
  """Add the 'if-exists' argument to the parser."""
  parser.add_argument(
      '--if-exists',
      action='store_true',
      help=(
          'Include an SQL statement (IF EXISTS) with each DROP statement'
          ' produced by --clean; corresponds to the if-exists flag for'
          ' pg_restore. Only applies if --parallel is set. PostgreSQL only.'
      ),
  )


def AddQuoteArgument(parser):
  """Add the 'quote' argument to the parser."""
  parser.add_argument(
      '--quote',
      help=(
          'Specifies the character that encloses values from columns that have '
          'string data type. The value of this argument has to be a character '
          'in Hex ASCII Code. For example, "22" represents double quotes. '
          'This flag is only available for MySQL and Postgres. If this flag is '
          'not provided, double quotes character will be used as the default '
          'value.'
      ),
  )


def AddEscapeArgument(parser):
  """Add the 'escape' argument to the parser."""
  parser.add_argument(
      '--escape',
      help=(
          'Specifies the character that should appear before a data character '
          'that needs to be escaped. The value of this argument has to be a '
          'character in Hex ASCII Code. For example, "22" represents double '
          'quotes. This flag is only available for MySQL and Postgres. If this '
          'flag is not provided, double quotes character will be used as the '
          'default value.'
      ),
  )


def AddFieldsDelimiterArgument(parser):
  """Add the 'fields-terminated-by' argument to the parser."""
  parser.add_argument(
      '--fields-terminated-by',
      help=(
          'Specifies the character that splits column values. The value of this'
          ' argument has to be a character in Hex ASCII Code. For example, "2C"'
          ' represents a comma. This flag is only available for MySQL and'
          ' Postgres. If this flag is not provided, a comma character will be'
          ' used as the default value.'
      ),
  )


def AddLinesDelimiterArgument(parser):
  """Add the 'lines-terminated-by' argument to the parser."""
  parser.add_argument(
      '--lines-terminated-by',
      help=(
          'Specifies the character that split line records. The value of this '
          'argument has to be a character in Hex ASCII Code. For example, '
          '"0A" represents a new line. This flag is only available for MySQL. '
          'If this flag is not provided, a new line character will be used as '
          'the default value.'
      ),
  )


DEFAULT_DATABASE_IMPORT_HELP_TEXT = (
    'Database to which the import is made. The database needs to be created'
    ' before importing. If not set, it is assumed that the database is'
    ' specified in the file to be imported. If your SQL dump file includes a'
    ' database statement, it will override the database set in this flag.'
)

SQLSERVER_DATABASE_IMPORT_HELP_TEXT = (
    'A new database into which the import is made.'
)


def AddDatabase(parser, help_text, required=False):
  """Add the '--database' and '-d' flags to the parser.

  Args:
    parser: The current argparse parser to add these database flags to.
    help_text: String, specifies the help text for the database flags.
    required: Boolean, specifies whether the database flag is required.
  """
  parser.add_argument('--database', '-d', required=required, help=help_text)


DEFAULT_DATABASE_LIST_EXPORT_HELP_TEXT = (
    'Database(s) from which the export is made. Information on requirements '
    'can be found here: https://cloud.google.com/sql/docs/mysql/admin-api/'
    'v1beta4/instances/export#exportContext.databases'
)

SQLSERVER_DATABASE_LIST_EXPORT_HELP_TEXT = (
    'Database from which the export is made. Information on requirements '
    'can be found here: https://cloud.google.com/sql/docs/sqlserver/admin-api/'
    'v1beta4/instances/export#exportContext.databases'
)


def AddDatabaseList(parser, help_text, required=False):
  """Add the '--database' and '-d' list flags to the parser.

  Args:
    parser: The current argparse parser to add these database flags to.
    help_text: String, specifies the help text for the database flags.
    required: Boolean, specifies whether the database flag is required.
  """
  if required:
    group = parser.add_group(mutex=False, required=True)
    group.add_argument(
        '--database',
        '-d',
        type=arg_parsers.ArgList(min_length=1),
        metavar='DATABASE',
        help=help_text,
    )
  else:
    parser.add_argument(
        '--database',
        '-d',
        type=arg_parsers.ArgList(min_length=1),
        metavar='DATABASE',
        required=False,
        help=help_text,
    )


def AddUser(parser, help_text):
  """Add the '--user' flag to the parser, with help text help_text."""
  parser.add_argument('--user', help=help_text)


def AddEncryptedBakFlags(parser):
  """Add the flags for importing encrypted BAK files.

  Add the --cert-path, --pvk-path, --pvk-password and
  --prompt-for-pvk-password flags to the parser

  Args:
    parser: The current argparse parser to add these database flags to.
  """
  enc_group = parser.add_group(
      mutex=False,
      required=False,
      help='Encryption info to support importing an encrypted .bak file',
  )
  enc_group.add_argument(
      '--cert-path',
      required=True,
      help=(
          'Path to the encryption certificate file in Google Cloud Storage '
          'associated with the BAK file. The URI is in the form '
          '`gs://bucketName/fileName`.'
      ),
  )
  enc_group.add_argument(
      '--pvk-path',
      required=True,
      help=(
          'Path to the encryption private key file in Google Cloud Storage '
          'associated with the BAK file. The URI is in the form '
          '`gs://bucketName/fileName`.'
      ),
  )
  password_group = enc_group.add_group(mutex=True, required=True)
  password_group.add_argument(
      '--pvk-password',
      help='The private key password associated with the BAK file.',
  )
  password_group.add_argument(
      '--prompt-for-pvk-password',
      action='store_true',
      help=(
          'Prompt for the private key password associated with the BAK file '
          'with character echo disabled. The password is all typed characters '
          'up to but not including the RETURN or ENTER key.'
      ),
  )


def AddBakExportStripeCountArgument(parser):
  """Add the 'stripe_count' argument to the parser for striped export."""
  parser.add_argument(
      '--stripe_count',
      type=int,
      default=None,
      help='Specifies the number of stripes to use for SQL Server exports.',
  )


def AddBakExportStripedArgument(parser, show_negated_in_help=True):
  """Add the 'striped' argument to the parser for striped export."""
  kwargs = _GetKwargsForBoolFlag(show_negated_in_help)
  parser.add_argument(
      '--striped',
      required=False,
      help='Whether SQL Server export should be striped.',
      **kwargs
  )


def AddBakExportBakTypeArgument(parser):
  """Add the 'bak-type' argument to the parser for bak import."""
  choices = [
      messages.ExportContext.BakExportOptionsValue.BakTypeValueValuesEnum.FULL.name,
      messages.ExportContext.BakExportOptionsValue.BakTypeValueValuesEnum.DIFF.name,
      messages.ExportContext.BakExportOptionsValue.BakTypeValueValuesEnum.TLOG.name,
  ]
  help_text = (
      'Type of bak file that will be exported, FULL or DIFF. SQL Server only.'
  )
  parser.add_argument(
      '--bak-type',
      choices=choices,
      required=False,
      default=messages.ExportContext.BakExportOptionsValue.BakTypeValueValuesEnum.FULL.name,
      help=help_text,
  )


def AddBakExportDifferentialBaseArgument(parser):
  """Add the 'dfferential-base' argument to the parser for export."""
  parser.add_argument(
      '--differential-base',
      required=False,
      default=False,
      action='store_true',
      help=(
          'Whether the bak file export can be used as differential base for'
          ' future differential backup. SQL Server only'
      ),
  )


def AddBakExportLogStartTimeArgument(parser):
  """Add the 'export-log-start-time' argument to the parser for bak export."""
  parser.add_argument(
      '--export-log-start-time',
      type=arg_parsers.Datetime.Parse,
      required=False,
      help=(
          'Optional flag. The start time of the transaction log files that are'
          ' included in the export file. Use this flag to export transaction'
          ' logs for Cloud SQL for SQL Server only. Format:'
          ' YYYY-MM-DDTHH:MM:SSZ, UTC timezone only.'
      ),
  )


def AddBakExportLogEndTimeArgument(parser):
  """Add the 'xport-log-end-time' argument to the parser for bak import."""
  parser.add_argument(
      '--export-log-end-time',
      type=arg_parsers.Datetime.Parse,
      required=False,
      help=(
          'Optional flag. The end time of the transaction log files that are'
          ' included in the export file. Use this flag to export transaction'
          ' logs for Cloud SQL for SQL Server only. Format:'
          ' YYYY-MM-DDTHH:MM:SSZ, UTC timezone only.'
      ),
  )


def AddBakImportStripedArgument(parser, show_negated_in_help=True):
  """Add the 'striped' argument to the parser for striped import."""
  kwargs = _GetKwargsForBoolFlag(show_negated_in_help)
  parser.add_argument(
      '--striped',
      required=False,
      help='Whether SQL Server import should be striped.',
      **kwargs
  )


def AddBakImportNoRecoveryArgument(parser):
  """Add the 'no-recovery' argument to the parser for import with no recovery option."""
  parser.add_argument(
      '--no-recovery',
      required=False,
      default=False,
      action='store_true',
      help=(
          'Whether or not the SQL Server import '
          'is executed with NORECOVERY keyword.'
      ),
  )


def AddBakImportRecoveryOnlyArgument(parser):
  """Add the 'recovery-only' argument to the parser for bak import."""
  parser.add_argument(
      '--recovery-only',
      required=False,
      default=False,
      action='store_true',
      help=(
          'Whether or not the SQL Server import '
          'skip download and bring database online.'
      ),
  )


def AddBakImportBakTypeArgument(parser):
  """Add the 'bak-type' argument to the parser for bak import."""
  choices = [
      messages.ImportContext.BakImportOptionsValue.BakTypeValueValuesEnum.FULL.name,
      messages.ImportContext.BakImportOptionsValue.BakTypeValueValuesEnum.DIFF.name,
      messages.ImportContext.BakImportOptionsValue.BakTypeValueValuesEnum.TLOG.name,
  ]
  help_text = (
      'Type of bak file that will be imported. Applicable to SQL Server only.'
  )
  parser.add_argument(
      '--bak-type',
      choices=choices,
      required=False,
      default=messages.ImportContext.BakImportOptionsValue.BakTypeValueValuesEnum.FULL.name,
      help=help_text,
  )


def AddBakImportStopAtArgument(parser):
  """Add the 'stop-at' argument to the parser for bak import."""
  parser.add_argument(
      '--stop-at',
      type=arg_parsers.Datetime.Parse,
      required=False,
      help=(
          'Equivalent to SQL Server STOPAT keyword. '
          'Used in transaction log import only. '
          'Transaction log import stop at this timestamp. '
          'Format: YYYY-MM-DDTHH:MM:SS.'
      ),
  )


def AddBakImportStopAtMarkArgument(parser):
  """Add the 'stop-at-mark' argument to the parser for bak import."""
  parser.add_argument(
      '--stop-at-mark',
      required=False,
      help=(
          'Equivalent to SQL Server STOPATMARK keyword. '
          'Used in transaction log import only. '
          'Transaction log import stop at the given mark. '
          'To stop at given LSN, use --stop-at-mark=lsn:xxx. '
      ),
  )


def AddBakImportKeepEncryptedArgument(parser):
  """Add the 'keep-encrypted' argument to the parser for bak import."""
  parser.add_argument(
      '--keep-encrypted',
      action='store_true',
      required=False,
      default=False,
      help='Whether or not to decrypt the imported encrypted BAK file.',
  )


def AddRescheduleType(parser):
  """Add the flag to specify reschedule type.

  Args:
    parser: The current argparse parser to add this to.
  """
  choices = [
      messages.Reschedule.RescheduleTypeValueValuesEnum.IMMEDIATE.name,
      messages.Reschedule.RescheduleTypeValueValuesEnum.NEXT_AVAILABLE_WINDOW.name,
      messages.Reschedule.RescheduleTypeValueValuesEnum.SPECIFIC_TIME.name,
  ]
  help_text = 'The type of reschedule operation to perform.'
  parser.add_argument(
      '--reschedule-type', choices=choices, required=True, help=help_text
  )


def AddScheduleTime(parser):
  """Add the flag for maintenance reschedule schedule time.

  Args:
    parser: The current argparse parser to add this to.
  """
  parser.add_argument(
      '--schedule-time',
      type=arg_parsers.Datetime.Parse,
      help=(
          'When specifying SPECIFIC_TIME, the date and time at which to '
          'schedule the maintenance in ISO 8601 format.'
      ),
  )


def AddBackupRunId(parser):
  """Add the flag for ID of backup run.

  Args:
    parser: The current argparse parser to add this to.
  """
  parser.add_argument(
      'id',
      type=arg_parsers.BoundedInt(lower_bound=1, unlimited=True),
      help=(
          'The ID of the backup run. You can find the ID by running '
          '$ gcloud sql backups list -i {instance}.'
      ),
  )


def AddBackupId(
    parser,
    help_text=(
        'The ID of the backup run. To find the ID, run the following command: $'
        ' gcloud sql backups list -i {instance}.Or, the NAME of the backup. To'
        ' find the NAME, run the following command: '
        ' $ gcloud sql backups list --filter=instance:{instance}'
    ),
):
  """Add the flag for the ID of the backup run.

  Args:
    parser: The current argparse parser to which to add this.
    help_text: The help text to display.
  """
  parser.add_argument(
      'id',
      help=help_text,
  )


def AddBackupName(parser):
  """Add the flag for the NAME of the backup.

  Args:
    parser: The current parser to add this argument.
  """
  parser.add_argument(
      'name',
      help=(
          'The NAME of the backup. To find the NAME, run the following command:'
          ' $ gcloud sql backups list  --filter=type:FINAL instance:{instance}.'
      ),
  )


def AddProjectLevelBackupEndpoint(parser):
  """Add the flag to specify requests to route to new backup service end point.

  Args:
    parser: The current argparse parser to add this to.
  """
  parser.add_argument(
      '--project-level',
      hidden=True,
      required=False,
      default=False,
      action='store_true',
      help=(
          "If true, then invoke project level backup endpoint. Use 'Name' as"
          " the value for backup ID. You can find the 'Name' by running $"
          ' gcloud sql backups list.'
      ),
  )


def AddPasswordPolicyMinLength(parser, hidden=False):
  """Add the flag to specify password policy min length.

  Args:
    parser: The current argparse parser to add this to.
    hidden: if the field needs to be hidden.
  """
  parser.add_argument(
      '--password-policy-min-length',
      type=int,
      required=False,
      default=None,
      help='Minimum number of characters allowed in the password.',
      hidden=hidden,
  )


def AddPasswordPolicyComplexity(parser, hidden=False):
  """Add the flag to specify password policy complexity.

  Args:
    parser: The current argparse parser to add this to.
    hidden: if the field needs to be hidden.
  """
  parser.add_argument(
      '--password-policy-complexity',
      choices={
          'COMPLEXITY_UNSPECIFIED': (
              'The default value if COMPLEXITY_DEFAULT is not specified. It'
              ' implies that complexity check is not enabled.'
          ),
          'COMPLEXITY_DEFAULT': (
              'A combination of lowercase, uppercase, numeric, and'
              ' non-alphanumeric characters.'
          ),
      },
      required=False,
      default=None,
      help=(
          'The complexity of the password. This flag is available only for'
          ' PostgreSQL.'
      ),
      hidden=hidden,
  )


def AddPasswordPolicyReuseInterval(parser, hidden=False):
  """Add the flag to specify password policy reuse interval.

  Args:
    parser: The current argparse parser to add this to.
    hidden: if the field needs to be hidden.
  """
  parser.add_argument(
      '--password-policy-reuse-interval',
      type=arg_parsers.BoundedInt(lower_bound=0, upper_bound=100),
      required=False,
      default=None,
      help=(
          'Number of previous passwords that cannot be reused. The valid range'
          ' is 0 to 100.'
      ),
      hidden=hidden,
  )


def AddPasswordPolicyDisallowUsernameSubstring(
    parser, show_negated_in_help=True, hidden=False
):
  """Add the flag to specify password policy disallow username as substring.

  Args:
    parser: The current argparse parser to add this to.
    show_negated_in_help: Show nagative action in help.
    hidden: if the field needs to be hidden.
  """
  kwargs = _GetKwargsForBoolFlag(show_negated_in_help)
  parser.add_argument(
      '--password-policy-disallow-username-substring',
      required=False,
      help='Disallow username as a part of the password.',
      hidden=hidden,
      **kwargs
  )


def AddPasswordPolicyPasswordChangeInterval(parser, hidden=False):
  """Add the flag to specify password policy password change interval.

  Args:
    parser: The current argparse parser to add this to.
    hidden: if the field needs to be hidden.
  """
  parser.add_argument(
      '--password-policy-password-change-interval',
      default=None,
      type=arg_parsers.Duration(lower_bound='1s'),
      required=False,
      help="""\
        Minimum interval after which the password can be changed, for example,
        2m for 2 minutes. See <a href="/sdk/gcloud/reference/topic/datetimes">
        $ gcloud topic datetimes</a> for information on duration formats.
        This flag is available only for PostgreSQL.
      """,
      hidden=hidden,
  )


def AddPasswordPolicyEnablePasswordPolicy(
    parser, show_negated_in_help=False, hidden=False
):
  """Add the flag to enable password policy.

  Args:
    parser: The current argparse parser to add this to.
    show_negated_in_help: Show nagative action in help.
    hidden: if the field needs to be hidden.
  """
  kwargs = _GetKwargsForBoolFlag(show_negated_in_help)
  parser.add_argument(
      '--enable-password-policy',
      required=False,
      help="""\
        Enable the password policy, which enforces user password management with
        the policies configured for the instance. This flag is only available for Postgres.
      """,
      hidden=hidden,
      **kwargs
  )


def AddPasswordPolicyClearPasswordPolicy(parser, show_negated_in_help=False):
  """Add the flag to clear password policy.

  Args:
    parser: The current argparse parser to add this to.
    show_negated_in_help: Show nagative action in help.
  """
  kwargs = _GetKwargsForBoolFlag(show_negated_in_help)
  parser.add_argument(
      '--clear-password-policy',
      required=False,
      help=(
          'Clear the existing password policy. This flag is only available for'
          ' Postgres.'
      ),
      **kwargs
  )


def AddPasswordPolicyAllowedFailedAttempts(parser):
  """Add the flag to set number of failed login attempts allowed before a user is locked.

  Args:
    parser: The current argparse parser to add this to.
  """
  parser.add_argument(
      '--password-policy-allowed-failed-attempts',
      type=int,
      required=False,
      default=None,
      help=(
          'Number of failed login attempts allowed before a user is locked out.'
      ),
  )


def AddPasswordPolicyPasswordExpirationDuration(parser):
  """Add the flag to specify expiration duration after password is updated.

  Args:
    parser: The current argparse parser to add this to.
  """
  parser.add_argument(
      '--password-policy-password-expiration-duration',
      default=None,
      type=arg_parsers.Duration(lower_bound='1s'),
      required=False,
      help="""\
        Expiration duration after a password is updated, for example,
        2d for 2 days. See `gcloud topic datetimes` for information on
        duration formats.
      """,
  )


def AddPasswordPolicyEnableFailedAttemptsCheck(
    parser, show_negated_in_help=True
):
  """Add the flag to enable the failed login attempts check.

  Args:
    parser: The current argparse parser to add this to.
    show_negated_in_help: Show nagative action in help.
  """
  kwargs = _GetKwargsForBoolFlag(show_negated_in_help)
  parser.add_argument(
      '--password-policy-enable-failed-attempts-check',
      required=False,
      help='Enables the failed login attempts check if set to true.',
      **kwargs
  )


def AddPasswordPolicyEnablePasswordVerification(
    parser, show_negated_in_help=True
):
  """Add the flag to specify password policy password verification.

  Args:
    parser: The current argparse parser to add this to.
    show_negated_in_help: Show nagative action in help.
  """
  kwargs = _GetKwargsForBoolFlag(show_negated_in_help)
  parser.add_argument(
      '--password-policy-enable-password-verification',
      required=False,
      help='The current password must be specified when altering the password.',
      **kwargs
  )


def AddUserRetainPassword(parser):
  """Will retain the old password when changing to the new password.

  Args:
    parser: The current argparse parser to add this to.
  """
  kwargs = _GetKwargsForBoolFlag(False)
  parser.add_argument(
      '--retain-password',
      required=False,
      help=(
          'Retain the old password when changing to the new password. Must set'
          ' password with this flag. This flag is only available for MySQL 8.0.'
      ),
      **kwargs
  )


def AddUserDiscardDualPassword(parser):
  """Will discard the user's secondary password.

  Args:
    parser: The current argparse parser to add this to.
  """
  kwargs = _GetKwargsForBoolFlag(False)
  parser.add_argument(
      '--discard-dual-password',
      required=False,
      help=(
          "Discard the user's secondary password. Cannot set password and set"
          ' this flag. This flag is only available for MySQL 8.0.'
      ),
      **kwargs
  )


def AddSqlServerTimeZone(parser, hidden=False):
  """Adds the `--time-zone` flag to the parser."""
  parser.add_argument(
      '--time-zone',
      required=False,
      help=(
          'Set a non-default time zone. '
          'Only available for SQL Server instances.'
      ),
      hidden=hidden,
  )


def AddMsdtcEnabled(
    parser: parser_extensions.Namespace, hidden: bool = False
) -> None:
  """Adds the `--msdtc-enabled` flag to the parser."""
  parser.add_argument(
      '--msdtc-enabled',
      action=arg_parsers.StoreTrueFalseAction,
      required=False,
      help=(
          'Enable/Disable Microsoft Distributed Transaction Coordinator '
          '(MSDTC) for SQL Server instances. Enabling MSDTC requires a SQL '
          'Server restart to take effect.'
      ),
      hidden=hidden,
  )


def AddMsdtcRpcAuthMode(
    parser: parser_extensions.Namespace, hidden: bool = False
) -> None:
  """Adds the '--msdtc-rpc-auth-mode' flag to the parser."""
  help_text = (
      'RPC authentication mode for Microsoft Distributed Transaction'
      ' Coordinator (MSDTC) for SQL Server instances.'
  )
  base.ChoiceArgument(
      '--msdtc-rpc-auth-mode',
      required=False,
      choices={
          'allow-only-secure-rpc-calls': (
              'Allow only secure RPC calls. Both servers (caller and receiver)'
              " must successfully verify each other's identities."
          ),
          'fallback-to-unsecure-rpc-if-necessary': (
              'The caller authenticates to the receiver, but not vice-versa.'
          ),
          'turn-off-rpc-security': 'No authentication.',
      },
      help_str=help_text,
      hidden=hidden,
  ).AddToParser(parser)


def AddMsdtcHostMappings(
    parser: parser_extensions.Namespace, hidden: bool = False
) -> None:
  """Adds the `--msdtc-host-mappings` flag to the parser."""
  parser.add_argument(
      '--msdtc-host-mappings',
      type=arg_parsers.ArgDict(min_length=1),
      metavar='KEY=VALUE',
      required=False,
      help=(
          'Host mappings for the VM to be used by MSDTC. '
          'Example: `--msdtc-host-mappings=10.0.0.1=host1,10.0.0.2=host2`'
      ),
      hidden=hidden,
  )


def AddThreadsPerCore(parser, hidden=False):
  """Adds the `--threads-per-core` flag to the parser."""
  parser.add_argument(
      '--threads-per-core',
      type=int,
      required=False,
      help="""\
        The number of threads per core. The value of this flag can be 1 or 2.
        To disable SMT, set this flag to 1. Only available in Cloud SQL for SQL Server instances.
      """,
      hidden=hidden,
  )


def AddShowSqlNetworkArchitecture(parser):
  """Adds the `--show-sql-network-architecture` flag to the parser."""
  kwargs = _GetKwargsForBoolFlag(False)
  parser.add_argument(
      '--show-sql-network-architecture',
      required=False,
      help="""Show the instance's current SqlNetworkArchitecture backend in addition
        to the default output list. An instance could use either the old or new
        network architecture. The new network architecture offers better
        isolation, reliability, and faster new feature adoption.""",
      **kwargs
  )


def AddShowTransactionalLogStorageState(
    parser, show_negated_in_help=False, hidden=False
):
  """Adds the `--show-transactional-log-storage-state` flag to the parser."""
  kwargs = _GetKwargsForBoolFlag(show_negated_in_help)
  parser.add_argument(
      '--show-transactional-log-storage-state',
      required=False,
      help=(
          'Show the storage location of the transactional logs used for'
          ' point-in-time recovery (PITR) by the instance.'
      ),
      hidden=hidden,
      **kwargs
  )


INSTANCES_USERLABELS_FORMAT = ':(settings.userLabels:alias=labels:label=LABELS)'

INSTANCES_FORMAT_COLUMNS = [
    'name',
    'databaseVersion',
    'firstof(gceZone,region):label=LOCATION',
    'settings.tier',
    (
        'ip_addresses.filter("type:PRIMARY").*extract(ip_address).flatten()'
        '.yesno(no="-"):label=PRIMARY_ADDRESS'
    ),
    (
        'ip_addresses.filter("type:PRIVATE").*extract(ip_address).flatten()'
        '.yesno(no="-"):label=PRIVATE_ADDRESS'
    ),
    'state:label=STATUS',
]

INSTANCES_FORMAT_COLUMNS_EDITION = [
    'name',
    'databaseVersion',
    'firstof(gceZone,region):label=LOCATION',
    'settings.tier',
    'settings.edition',
    (
        'ip_addresses.filter("type:PRIMARY").*extract(ip_address).flatten()'
        '.yesno(no="-"):label=PRIMARY_ADDRESS'
    ),
    (
        'ip_addresses.filter("type:PRIVATE").*extract(ip_address).flatten()'
        '.yesno(no="-"):label=PRIVATE_ADDRESS'
    ),
    'state:label=STATUS',
]

INSTANCES_FORMAT_COLUMNS_WITH_NETWORK_ARCHITECTURE = [
    'name',
    'databaseVersion',
    'firstof(gceZone,region):label=LOCATION',
    'settings.tier',
    (
        'ip_addresses.filter("type:PRIMARY").*extract(ip_address).flatten()'
        '.yesno(no="-"):label=PRIMARY_ADDRESS'
    ),
    (
        'ip_addresses.filter("type:PRIVATE").*extract(ip_address).flatten()'
        '.yesno(no="-"):label=PRIVATE_ADDRESS'
    ),
    'state:label=STATUS',
    'sqlNetworkArchitecture:label=NETWORK_ARCHITECTURE',
]


def GetInstanceListFormatForNetworkArchitectureUpgrade():
  """Returns the table format for listing instances with current network architecture field."""
  table_format = '{} table({})'.format(
      INSTANCES_USERLABELS_FORMAT,
      ','.join(INSTANCES_FORMAT_COLUMNS_WITH_NETWORK_ARCHITECTURE),
  )

  return table_format


INSTANCES_FORMAT_COLUMNS_WITH_TRANSACTIONAL_LOG_STORAGE_STATE = [
    'name',
    'databaseVersion',
    'firstof(gceZone,region):label=LOCATION',
    'settings.tier',
    (
        'ip_addresses.filter("type:PRIMARY").*extract(ip_address).flatten()'
        '.yesno(no="-"):label=PRIMARY_ADDRESS'
    ),
    (
        'ip_addresses.filter("type:PRIVATE").*extract(ip_address).flatten()'
        '.yesno(no="-"):label=PRIVATE_ADDRESS'
    ),
    'state:label=STATUS',
    'settings.backupConfiguration.transactionalLogStorageState:label=TRANSACTIONAL_LOG_STORAGE_STATE',
]


def GetInstanceListFormatForTransactionalLogStorageSwitch():
  """Returns the table format for listing instances with the storage location of their transactional logs."""
  table_format = '{} table({})'.format(
      INSTANCES_USERLABELS_FORMAT,
      ','.join(INSTANCES_FORMAT_COLUMNS_WITH_TRANSACTIONAL_LOG_STORAGE_STATE),
  )

  return table_format


def GetInstanceListFormat():
  """Returns the table format for listing instances."""
  table_format = '{} table({})'.format(
      INSTANCES_USERLABELS_FORMAT, ','.join(INSTANCES_FORMAT_COLUMNS)
  )
  return table_format


def GetInstanceListFormatEdition():
  """Returns the table format for listing instances."""
  table_format = '{} table({})'.format(
      INSTANCES_USERLABELS_FORMAT, ','.join(INSTANCES_FORMAT_COLUMNS_EDITION)
  )
  return table_format


OPERATION_FORMAT = """
  table(
    operation,
    operationType:label=TYPE,
    startTime.iso():label=START,
    endTime.iso():label=END,
    error.errors[0].code.yesno(no="-"):label=ERROR,
    state:label=STATUS
  )
"""

OPERATION_FORMAT_BETA = """
  table(
    name,
    operationType:label=TYPE,
    startTime.iso():label=START,
    endTime.iso():label=END,
    error.errors[0].code.yesno(no="-"):label=ERROR,
    status:label=STATUS
  )
"""

CLIENT_CERTS_FORMAT = """
  table(
    commonName:label=NAME,
    sha1Fingerprint,
    expirationTime.yesno(no="-"):label=EXPIRATION
  )
"""

SERVER_CA_CERTS_FORMAT = """
  table(
    sha1Fingerprint,
    expirationTime.yesno(no="-"):label=EXPIRATION
  )
"""

SERVER_CERTS_FORMAT = """
  table(
    ssl_cert.sha1Fingerprint,
    ssl_cert.expirationTime.yesno(no="-"):label=EXPIRATION,
    status
  )
"""

ENTRAID_CERTS_FORMAT = """
  table(
    ssl_cert.sha1Fingerprint,
    ssl_cert.expirationTime.yesno(no="-"):label=EXPIRATION,
    status
  )
"""

TIERS_FORMAT = """
  table(
    tier,
    region.list():label=AVAILABLE_REGIONS,
    RAM.size(),
    DiskQuota.size():label=DISK
  )
"""

TIERS_FORMAT_EDITION = """
  table(
    tier,
    edition,
    region.list():label=AVAILABLE_REGIONS,
    RAM.size(),
    DiskQuota.size():label=DISK
  )
"""


def AddShowEdition(parser):
  """Show the instance or tier edition."""
  kwargs = _GetKwargsForBoolFlag(False)
  parser.add_argument(
      '--show-edition', required=False, help='Show the edition field.', **kwargs
  )


def AddActiveDirectoryDomain(parser, hidden=False):
  """Adds the '--active-directory-domain' flag to the parser.

  Args:
    parser: The current argparse parser to add this to.
    hidden: if the field needs to be hidden.
  """
  help_text = (
      'Managed Service for Microsoft Active Directory domain this instance is '
      'joined to. Only available for SQL Server instances.'
  )
  parser.add_argument(
      '--active-directory-domain',
      help=help_text,
      hidden=hidden,
  )


def AddActiveDirectorySecretManagerKey(parser, hidden=False):
  """Adds the '--active-directory-secret-manager-key' flag to the parser.

  Args:
    parser: The current argparse parser to add this to.
    hidden: if the field needs to be hidden.
  """
  help_text = (
      'The secret manager key storing administrator credentials. Only available'
      ' for SQL Server instances.'
  )
  parser.add_argument(
      '--active-directory-secret-manager-key',
      help=help_text,
      hidden=hidden,
  )


def ClearActiveDirectoryDNSServers(parser, hidden=False):
  kwargs = _GetKwargsForBoolFlag(False)
  parser.add_argument(
      '--clear-active-directory-dns-servers',
      required=False,
      help='Removes the list of DNS Servers from the Active Directory Config.',
      hidden=hidden,
      **kwargs
  )


def AddActiveDirectoryMode(parser, hidden=False):
  """Adds the '--active-directory-mode' flag to the parser.

  Args:
    parser: The current argparse parser to add this to.
    hidden: if the field needs to be hidden.
  """
  choices = [
      messages.SqlActiveDirectoryConfig.ModeValueValuesEnum.MANAGED_ACTIVE_DIRECTORY.name,
      messages.SqlActiveDirectoryConfig.ModeValueValuesEnum.CUSTOMER_MANAGED_ACTIVE_DIRECTORY.name,
  ]
  help_text = (
      'Defines the Active Directory mode. Only available for SQL Server'
      ' instances.'
  )
  parser.add_argument(
      '--active-directory-mode',
      choices=choices,
      required=False,
      help=help_text,
      hidden=hidden,
  )


def AddActiveDirectoryDNSServers(parser, hidden=False):
  """Adds the '--active-directory-dns-servers' flag to the parser.

  Args:
    parser: The current argparse parser to add this to.
    hidden: if the field needs to be hidden.
  """
  help_text = (
      'A comma-separated list of the DNS servers to be used for Active'
      ' Directory. Only available for SQL Server instances. E.g:'
      ' 10.0.0.1,10.0.0.2'
  )

  parser.add_argument(
      '--active-directory-dns-servers',
      type=arg_parsers.ArgList(),
      metavar='DNS_SERVER_IP_ADDRESS',
      help=help_text,
      hidden=hidden,
      action=arg_parsers.FlattenAction(),
  )


def AddActiveDirectoryOrganizationalUnit(parser, hidden=False):
  """Adds the '--active-directory-organizational-unit' flag to the parser.

  Args:
    parser: The current argparse parser to add this to.
    hidden: if the field needs to be hidden.
  """
  help_text = (
      'Defines the organizational unit to be used for Active Directory. Only'
      ' available for SQL Server instances. E.g:'
      ' OU=Cloud,DC=ad,DC=example,DC=com'
  )

  parser.add_argument(
      '--active-directory-organizational-unit',
      help=help_text,
      hidden=hidden,
  )


def AddClearActiveDirectory(parser, hidden=False):
  """Adds the '--clear-active-directory' flag to the parser.

  Args:
    parser: The current argparse parser to add this to.
    hidden: if the field needs to be hidden.
  """
  kwargs = _GetKwargsForBoolFlag(False)
  parser.add_argument(
      '--clear-active-directory',
      required=False,
      help='Clears the Active Directory configuration.',
      hidden=hidden,
      **kwargs
  )


def AddDeletionProtection(parser, hidden=False):
  """Adds the '--deletion-protection' flag to the parser for instances patch action.

  Args:
    parser: The current argparse parser to add this to.
    hidden: if the field needs to be hidden.
  """
  help_text = 'Enable deletion protection on a Cloud SQL instance.'
  parser.add_argument(
      '--deletion-protection',
      action=arg_parsers.StoreTrueFalseAction,
      help=help_text,
      hidden=hidden,
  )


def AddConnectorEnforcement(parser, hidden=False):
  """Adds the '--connector-enforcement' flag to the parser.

  Args:
    parser: The current argparse parser to add this to.
    hidden: if the field needs to be hidden.
  """
  help_text = (
      'Cloud SQL Connector enforcement mode. It determines how Cloud SQL '
      'Connectors are used in the connection. See the list of modes '
      '[here](https://cloud.google.com/sql/docs/mysql/admin-api/rest/v1beta4/instances#connectorenforcement).'
  )
  parser.add_argument(
      '--connector-enforcement',
      choices={
          'CONNECTOR_ENFORCEMENT_UNSPECIFIED': (
              'The requirement for Cloud SQL connectors is unknown.'
          ),
          'NOT_REQUIRED': 'Does not require Cloud SQL connectors.',
          'REQUIRED': (
              'Requires all connections to use Cloud SQL connectors, '
              'including the Cloud SQL Auth Proxy and Cloud SQL Java, Python, '
              'and Go connectors. Note: This disables all existing authorized '
              'networks.'
          ),
      },
      required=False,
      default=None,
      help=help_text,
      hidden=hidden,
  )


def AddTimeout(
    parser,
    default_max_wait,
    help_text='Time to synchronously wait for the operation to complete, after which the operation continues asynchronously. Ignored if --async flag is specified. By default, set to 3600s. To wait indefinitely, set to *unlimited*.',
    hidden=False,
):
  """Adds --timeout flag."""
  parser.add_argument(
      '--timeout',
      required=False,
      default=default_max_wait,
      help=help_text,
      type=arg_parsers.BoundedInt(lower_bound=0, unlimited=True),
      hidden=hidden,
  )


def AddEnablePrivateServiceConnect(
    parser, show_negated_in_help=False, hidden=False
):
  kwargs = _GetKwargsForBoolFlag(show_negated_in_help)
  parser.add_argument(
      '--enable-private-service-connect',
      required=False,
      help=(
          'Enable connecting to the Cloud SQL instance with Private Service'
          ' Connect.'
      ),
      hidden=hidden,
      **kwargs
  )


def AddAllowedPscProjects(parser, hidden=False):
  parser.add_argument(
      '--allowed-psc-projects',
      type=arg_parsers.ArgList(min_length=1),
      required=False,
      metavar='PROJECT',
      help=(
          'A comma-separated list of projects. Each project in this list might'
          ' be represented by a project number (numeric) or by a project ID'
          ' (alphanumeric). This allows Private Service Connect connections to'
          ' be established from specified consumer projects.'
      ),
      hidden=hidden,
  )


def AddClearAllowedPscProjects(parser):
  kwargs = _GetKwargsForBoolFlag(False)
  parser.add_argument(
      '--clear-allowed-psc-projects',
      required=False,
      help=(
          'This will clear the project allowlist of Private Service Connect,'
          ' disallowing all projects from creating new Private Service Connect'
          ' bindings to the instance.'
      ),
      **kwargs
  )


def AddEnablePscAutoConnectionPolicy(
    parser: parser_extensions.Namespace, *, hidden: bool = False
) -> None:
  """Adds --enable-psc-auto-connection-policy argument."""
  parser.add_argument(
      '--enable-psc-auto-connection-policy',
      action=arg_parsers.StoreTrueFalseAction,
      required=False,
      help=(
          'If set, service connection policy will be created by Cloud SQL'
          ' when missing for PSC enabled instance.'
      ),
      hidden=hidden)


def AddPscAutoConnections(parser, hidden=False):
  """Adds --psc-auto-connections argument."""
  parser.add_argument(
      '--psc-auto-connections',
      type=arg_parsers.ArgObject(
          spec={
              'project': str,
              'network': str,
          },
          required_keys=['network'],
      ),
      required=False,
      help=(
          'A comma-separated list of networks or network-project pairs. Each'
          ' project is represented by a project number (numeric) or by a'
          ' project ID (alphanumeric). This allows Private Service Connect'
          ' connections to be created automatically for the specified networks.'
          ' For example, this connection uses "the form'
          ' `psc-auto-connections`=`network=projects/testproject1/global/'
          'networks/testnetwork1`" or "the form'
          ' `psc-auto-connections`=`project=testproject1,network=projects/'
          'testproject1/global/networks/testnetwork1`"'
      ),
      hidden=hidden,
      action=arg_parsers.FlattenAction(),
  )


def AddEnablePscAutoDns(parser, hidden=False):
  """Adds --enable-psc-auto-dns flag."""
  parser.add_argument(
      '--enable-psc-auto-dns',
      required=False,
      hidden=hidden,
      help='Enable per instance DNS records for PSC auto-connections.',
      action=arg_parsers.StoreTrueFalseAction,
  )


def AddEnablePscWriteEndpointDns(parser, hidden=False):
  """Adds --enable-psc-write-endpoint-dns flag."""
  parser.add_argument(
      '--enable-psc-write-endpoint-dns',
      required=False,
      hidden=hidden,
      help=(
          'Enable the global DNS record pointing to the primary, for Enterprise'
          ' Plus edition only.'
      ),
      action=arg_parsers.StoreTrueFalseAction,
  )


def AddClearPscAutoConnections(parser, hidden=False):
  kwargs = _GetKwargsForBoolFlag(False)
  parser.add_argument(
      '--clear-psc-auto-connections',
      required=False,
      help=(
          'This removes all automatically created connections. Cloud SQL'
          ' uses these connections to connect to an instance using Private'
          ' Service Connect.'
      ),
      hidden=hidden,
      **kwargs
  )


def AddCustomSubjectAlternativeNames(parser, hidden=False):
  """Adds --custom-subject-alternative-names argument."""
  parser.add_argument(
      '--custom-subject-alternative-names',
      type=arg_parsers.ArgList(min_length=1, max_length=3),
      metavar='DNS',
      required=False,
      help=(
          "A comma-separated list of DNS names to add to the instance's SSL"
          ' certificate. A custom SAN is a structured way to add additional'
          ' DNS names (host names) that are not managed by Cloud SQL to an'
          ' instance. It allows for hostname verification during establishment'
          ' of a database connection using the DNS name over SSL/TLS.'
          ' When you create and/or update an instance, you can add a'
          ' comma-separated list of up to three DNS names to the server'
          ' certificate of your instance.'
      ),
      hidden=hidden,
      action=arg_parsers.FlattenAction(),
  )


def AddClearCustomSubjectAlternativeNames(parser, hidden=False):
  kwargs = _GetKwargsForBoolFlag(False)
  parser.add_argument(
      '--clear-custom-subject-alternative-names',
      required=False,
      help='This clears the customer specified DNS names.',
      hidden=hidden,
      **kwargs
  )


def AddRecreateReplicasOnPrimaryCrash(parser, hidden=False):
  """Adds --recreate-replicas-on-primary-crash flag."""
  parser.add_argument(
      '--recreate-replicas-on-primary-crash',
      required=False,
      help=(
          'Allow/Disallow replica recreation when a primary MySQL instance '
          'operating in reduced durability mode crashes. Not recreating the '
          'replicas might lead to data inconsistencies between the primary and '
          'its replicas. This setting is only applicable for MySQL instances '
          'and is enabled by default.'
      ),
      action=arg_parsers.StoreTrueFalseAction,
      hidden=hidden,
  )


def AddUpgradeSqlNetworkArchitecture(parser):
  """Adds --upgrade-sql-network-architecture flag."""
  kwargs = _GetKwargsForBoolFlag(False)
  parser.add_argument(
      '--upgrade-sql-network-architecture',
      required=False,
      help=(
          """Upgrade from old network architecture to new network architecture. The
       new network architecture offers better isolation, reliability, and faster
       new feature adoption."""
      ),
      **kwargs
  )


def AddForceSqlNetworkArchitecture(parser, hidden=False):
  """Adds --enforce-new-sql-network-architecture flag."""
  kwargs = _GetKwargsForBoolFlag(False)
  parser.add_argument(
      '--enforce-new-sql-network-architecture',
      required=False,
      help="""Force the instance to use the new network architecture.""",
      hidden=hidden,
      **kwargs
  )


def AddCascadableReplica(parser, hidden=False):
  """Adds --cascadable-replica flag."""
  kwargs = _GetKwargsForBoolFlag(False)
  parser.add_argument(
      '--cascadable-replica',
      required=False,
      help=(
          'Specifies whether a SQL Server replica is a cascadable replica. A'
          ' cascadable replica is a SQL Server cross-region replica that'
          ' supports replica(s) under it. This flag only takes effect when the'
          ' `--master-instance-name` flag is set, and the replica under'
          ' creation is in a different region than the primary instance.'
      ),
      hidden=hidden,
      **kwargs
  )


def AddEnableDataCache(parser, show_negated_in_help=True, hidden=False):
  """Adds '--enable-data-cache' flag to the parser."""
  kwargs = _GetKwargsForBoolFlag(show_negated_in_help)
  parser.add_argument(
      '--enable-data-cache',
      required=False,
      help=(
          'Enable use of data cache for accelerated read performance. This flag'
          ' is only available for Enterprise_Plus edition instances.'
      ),
      hidden=hidden,
      **kwargs
  )


def AddEnableDbAlignedAtomicWrites(parser):
  """Adds '--enable-db-aligned-atomic-writes' flag to the parser."""
  parser.add_argument(
      '--enable-db-aligned-atomic-writes',
      action=arg_parsers.StoreTrueFalseAction,
      required=False,
      help=(
          'Enabling DB Aligned Atomic Writes will reformat the underlying'
          ' filesystem to use write block size similar to the DB block size.'
      ),
      hidden=True,
  )


def AddEnableAutoUpgrade(parser, hidden=False):
  """Adds '--enable-auto-upgrade-minor-version' flag to the parser."""
  kwargs = _GetKwargsForBoolFlag(False)
  parser.add_argument(
      '--enable-auto-upgrade-minor-version',
      # action=arg_parsers.StoreTrueFalseAction,
      required=False,
      help=(
          'Enables auto-upgrade for MySQL 8.0 minor versions. The MySQL version'
          ' must be 8.0.35 or higher.'
      ),
      hidden=hidden,
      **kwargs
  )


def AddReadPoolAutoScaleConfig(parser, hidden=False):
  """Adds flags for read pool auto-scale config."""
  read_pool_auto_scale_group = parser.add_group(
      help='Options for configuring read pool auto scale.', hidden=hidden
  )
  read_pool_auto_scale_group.add_argument(
      '--auto-scale-enabled',
      action=arg_parsers.StoreTrueFalseAction,
      help=(
          'Enables read pool auto scaling. Supports automatically increasing'
          " and decreasing the read pool's node count based on need."
      ),
  )
  read_pool_auto_scale_group.add_argument(
      '--auto-scale-min-node-count',
      type=arg_parsers.BoundedInt(lower_bound=1),
      help='Minimum number of read pool nodes to be maintained.',
  )
  read_pool_auto_scale_group.add_argument(
      '--auto-scale-max-node-count',
      type=arg_parsers.BoundedInt(lower_bound=1),
      help='Maximum number of read pool nodes to be maintained.',
  )
  read_pool_auto_scale_group.add_argument(
      '--auto-scale-target-metrics',
      type=arg_parsers.ArgDict(
          spec={
              'AVERAGE_CPU_UTILIZATION': float,
              'AVERAGE_DB_CONNECTIONS': float,
          }
      ),
      metavar='METRIC=VALUE',
      help=(
          'Target metrics for read pool auto scaling. Options are:'
          ' AVERAGE_CPU_UTILIZATION and AVERAGE_DB_CONNECTIONS. Example:'
          ' --auto-scale-target-metrics=AVERAGE_CPU_UTILIZATION=0.8'
      ),
  )
  read_pool_auto_scale_group.add_argument(
      '--auto-scale-disable-scale-in',
      action=arg_parsers.StoreTrueFalseAction,
      help=(
          'Disables automatic read pool scale-in. When disabled, read pool auto'
          ' scaling only supports increasing the read pool node count. By'
          ' default, both automatic read pool scale-in and scale-out are'
          ' enabled.'
      ),
  )
  read_pool_auto_scale_group.add_argument(
      '--auto-scale-in-cooldown-seconds',
      type=arg_parsers.BoundedInt(lower_bound=60),
      help=(
          'The cooldown period for automatic read pool scale-in. '
          'Minimum time between scale-in events. Must be an integer value. For'
          ' example, if the value is 60, then a scale-in event will not be'
          ' triggered within 60 seconds of the last scale-in event.'
      ),
  )
  read_pool_auto_scale_group.add_argument(
      '--auto-scale-out-cooldown-seconds',
      type=arg_parsers.BoundedInt(lower_bound=60),
      help=(
          'The cooldown period for automatic read pool scale-out. '
          'Minimum time between scale-out events. Must be an integer value. For'
          ' example, if the value is 60, then a scale-out event will not be'
          ' triggered within 60 seconds of the last scale-out event.'
      ),
  )


def AddReplicationLagMaxSecondsForRecreate(parser):
  """Adds the '--replication-lag-max-seconds-for-recreate' flag to the parser for instances patch action.

  Args:
    parser: The current argparse parser to add this to.
  """
  parser.add_argument(
      '--replication-lag-max-seconds-for-recreate',
      type=arg_parsers.BoundedInt(lower_bound=300, upper_bound=31536000),
      action=arg_parsers.StoreOnceAction,
      required=False,
      help=(
          'Set a maximum replication lag for a MySQL read replica in '
          'seconds. If the replica lag exceeds the specified value, the read'
          'replica(s) will be recreated. Min value=300 seconds,'
          'Max value=31536000 seconds, default value=31536000 seconds.'
      ),
  )


def AddSslMode(parser, hidden=False):
  """Adds the '--ssl-mode' flag to the parser.

  Args:
    parser: The current argparse parser to add this to.
    hidden: if the field needs to be hidden.
  """
  help_text = 'Set the SSL mode of the instance.'
  parser.add_argument(
      '--ssl-mode',
      choices={
          'ALLOW_UNENCRYPTED_AND_ENCRYPTED': (
              'Allow non-SSL and SSL connections. For SSL connections, client'
              ' certificate will not be verified.'
          ),
          'ENCRYPTED_ONLY': 'Only allow connections encrypted with SSL/TLS.',
          'TRUSTED_CLIENT_CERTIFICATE_REQUIRED': (
              'Only allow connections encrypted with SSL/TLS and with valid'
              ' client certificates.'
          ),
      },
      required=False,
      default=None,
      help=help_text,
      hidden=hidden,
  )


def AddSqlServerSsrs(parser):
  """Adds SQL Server Reporting Services (SSRS) related flags to the parser."""
  parser.add_argument(
      '--setup-login',
      required=True,
      help=(
          'Existing login in the Cloud SQL for SQL Server instance that is'
          ' used as the setup login for SSRS setup.'
      ),
  )

  parser.add_argument(
      '--service-login',
      required=True,
      help=(
          'Existing login in the Cloud SQL for SQL Server instance that is used'
          ' as the service login for SSRS setup.'
      ),
  )

  parser.add_argument(
      '--report-database',
      required=True,
      help=(
          'Existing or new report database name in the Cloud SQL for SQL Server'
          ' instance that is used for SSRS setup.'
      ),
  )

  parser.add_argument(
      '--duration',
      default=None,
      type=arg_parsers.Duration(lower_bound='1h', upper_bound='12h'),
      required=False,
      help=(
          'Time duration, in hours, that the lease will be active to allow SSRS'
          ' setup. Default lease duration is 5 hours if this flag is not'
          ' specified.'
      ),
  )


def AddEnableGoogleMLIntegration(parser, hidden=False):
  """Adds --enable-google-ml-integration flag."""
  parser.add_argument(
      '--enable-google-ml-integration',
      required=False,
      hidden=hidden,
      help=(
          'Enable Vertex AI integration for Google Cloud SQL. '
          'You can integrate Vertex AI with Cloud SQL for MySQL and Cloud SQL '
          'for PostgreSQL instances only.'
      ),
      action=arg_parsers.StoreTrueFalseAction,
  )


def AddEnableDataplexIntegration(parser, hidden=False):
  """Adds --enable-dataplex-integration flag."""
  parser.add_argument(
      '--enable-dataplex-integration',
      required=False,
      hidden=hidden,
      help='Enable Dataplex integration for Google Cloud SQL.',
      action=arg_parsers.StoreTrueFalseAction,
  )


def AddSwitchoverDbTimeout(parser):
  parser.add_argument(
      '--db-timeout',
      default=None,
      type=arg_parsers.Duration(lower_bound='1s', upper_bound='1d'),
      required=False,
      help=(
          '(MySQL and PostgreSQL only) Cloud SQL instance operations timeout,'
          ' which is the sum of all database operations. Default value is 10'
          ' minutes and can be modified to a maximum value of 24h.'
      ),
  )


def AddServerCaMode(parser, hidden=False):
  """Adds the '--server-ca-mode' flag to the parser.

  Args:
    parser: The current argparse parser to add this to.
    hidden: if the field needs to be hidden.
  """
  help_text = 'Set the server CA mode of the instance.'
  parser.add_argument(
      '--server-ca-mode',
      choices={
          'GOOGLE_MANAGED_INTERNAL_CA': (
              'Google-managed self-signed internal CA.'
          ),
          'GOOGLE_MANAGED_CAS_CA': (
              'Google-managed regional CA part of root CA hierarchy hosted on'
              " Google Cloud's Certificate Authority Service (CAS)."
          ),
          'CUSTOMER_MANAGED_CAS_CA': (
              "Customer-managed CA hosted on Google Cloud's Certificate"
              ' Authority Service (CAS).'
          ),
      },
      required=False,
      default=None,
      help=help_text,
      hidden=hidden,
  )


def AddSwitchTransactionLogsToCloudStorage(
    parser, show_negated_in_help=False, hidden=False
):
  """Adds '--switch-transaction-logs-to-cloud-storage' flag to the parser."""
  kwargs = _GetKwargsForBoolFlag(show_negated_in_help)
  parser.add_argument(
      '--switch-transaction-logs-to-cloud-storage',
      required=False,
      help=(
          'Switches the location of the transaction logs used for PITR from'
          ' disk to Cloud Storage.'
      ),
      hidden=hidden,
      **kwargs
  )


def AddTdeFlags(parser):
  """Add the flags for importing TDE certificate files.

  Add the --certificate, --cert-path, --pvk-path, --pvk-password and
  --prompt-for-pvk-password flags to the parser

  Args:
    parser: The current argparse parser to add these database flags to.
  """
  enc_group = parser.add_group(
      mutex=False,
      required=True,
      help='Encryption info to support importing a TDE certificate file',
  )
  enc_group.add_argument(
      '--certificate',
      required=True,
      help='Name of the encryption certificate.',
  )
  enc_group.add_argument(
      '--cert-path',
      required=True,
      help=(
          'Path to the encryption certificate file in Google Cloud Storage. '
          'The URI is in the form `gs://bucketName/fileName`.'
      ),
  )
  enc_group.add_argument(
      '--pvk-path',
      required=True,
      help=(
          'Path to the encryption private key file in Google Cloud Storage. '
          'The URI is in the form `gs://bucketName/fileName`.'
      ),
  )
  password_group = enc_group.add_group(mutex=True, required=True)
  password_group.add_argument(
      '--pvk-password',
      help='The private key password associated with the certificate file.',
  )
  password_group.add_argument(
      '--prompt-for-pvk-password',
      action='store_true',
      help=(
          'Prompt for the private key password associated with the certificate '
          'file with character echo disabled. The password is all typed '
          'characters up to but not including the RETURN or ENTER key.'
      ),
  )


def AddRetainBackupsOnDelete(parser, hidden=False):
  """Adds --retain-backups-on-delete flag.

  Args:
    parser: The current argparse parser to add this to.
    hidden: if the field needs to be hidden.
  """
  parser.add_argument(
      '--retain-backups-on-delete',
      required=False,
      hidden=hidden,
      help=(
          'Retain automated/ondemand backups of the instance after the instance'
          ' is deleted.'
      ),
      action=arg_parsers.StoreTrueFalseAction,
  )


def AddEnableConnectionPooling(parser):
  """Adds --enable-connection-pooling flag.

  Args:
    parser: The current argparse parser to add this to.
  """
  parser.add_argument(
      '--enable-connection-pooling',
      required=False,
      hidden=False,
      help='Enable connection pooling for the instance.',
      action=arg_parsers.StoreTrueFalseAction,
  )


def AddConnectionPoolFlags(parser, update=False):
  """Adds the `--connection-pool-flags` flag."""
  help_ = (
      'Comma-separated list of connection pool flags to set on the instance'
      ' connection pool. Use an equals sign to separate flag name and value.'
      ' More information on available flags can be found here:'
      ' https://cloud.google.com/sql/docs/mysql/managed-connection-pooling#configuration-options'
      ' for MySQL and'
      ' https://cloud.google.com/sql/docs/postgres/managed-connection-pooling#configuration-options'
      ' for PostgreSQL. (e.g., `--connection-pool-flags'
      ' max_pool_size=1000,max_client_connections=20`)'
  )
  if update:
    help_ += (
        '\n\nThe value given for this argument *replaces* the existing list.'
    )
  parser.add_argument(
      '--connection-pool-flags',
      type=arg_parsers.ArgDict(min_length=1),
      metavar='FLAG=VALUE',
      required=False,
      help=help_,
      hidden=False,
  )


def AddClearConnectionPoolFlags(parser):
  kwargs = _GetKwargsForBoolFlag(False)
  parser.add_argument(
      '--clear-connection-pool-flags',
      required=False,
      help='This will clear the connection pool flags set on the instance.',
      **kwargs
  )


def AddConnectionPoolingPoolMode(parser):
  """Adds --connection-pooling-pool-mode flag.

  Args:
    parser: The current argparse parser to add this to.
  """
  parser.add_argument(
      '--connection-pooling-pool-mode',
      choices={
          'SESSION': 'Session mode for managed connection pooling.',
          'TRANSACTION': 'Transaction mode for managed connection pooling.',
      },
      required=False,
      default=None,
      help='The pool mode for managed connection pooling.',
      hidden=True,
      actions=actions.DeprecationAction(
          '--connection-pooling-pool-mode',
          error=(
              'This flag is deprecated. Please use --connection-pool-flags'
              ' instead.'
          ),
          removed=True,
      ),
  )


def AddConnectionPoolingPoolSize(parser):
  """Adds --connection-pooling-pool-size flag.

  Args:
    parser: The current argparse parser to add this to.
  """
  parser.add_argument(
      '--connection-pooling-pool-size',
      type=arg_parsers.BoundedInt(lower_bound=0, upper_bound=262042),
      required=False,
      default=None,
      help='The pool size for managed connection pooling.',
      hidden=True,
      actions=actions.DeprecationAction(
          '--connection-pooling-pool-size',
          error=(
              'This flag is deprecated. Please use --connection-pool-flags'
              ' instead.'
          ),
          removed=True,
      ),
  )


def AddConnectionPoolingMaxClientConnections(parser):
  """Adds --connection-pooling-max-client-connections flag.

  Args:
    parser: The current argparse parser to add this to.
  """
  parser.add_argument(
      '--connection-pooling-max-client-connections',
      type=arg_parsers.BoundedInt(lower_bound=1, upper_bound=262042),
      required=False,
      default=None,
      help='The max client connections for managed connection pooling.',
      hidden=True,
      actions=actions.DeprecationAction(
          '--connection-pooling-max-client-connections',
          error=(
              'This flag is deprecated. Please use --connection-pool-flags'
              ' instead.'
          ),
          removed=True,
      ),
  )


def AddConnectionPoolingClientIdleTimeout(parser):
  """Adds --connection-pooling-client-idle-timeout flag.

  Args:
    parser: The current argparse parser to add this to.
  """
  parser.add_argument(
      '--connection-pooling-client-idle-timeout',
      required=False,
      default=None,
      help='The client idle timeout for managed connection pooling.',
      hidden=True,
      actions=actions.DeprecationAction(
          '--connection-pooling-client-idle-timeout',
          error=(
              'This flag is deprecated. Please use --connection-pool-flags'
              ' instead.'
          ),
          removed=True,
      ),
  )


def AddConnectionPoolingServerIdleTimeout(parser):
  """Adds --connection-pooling-server-idle-timeout flag.

  Args:
    parser: The current argparse parser to add this to.
  """
  parser.add_argument(
      '--connection-pooling-server-idle-timeout',
      required=False,
      default=None,
      help='The server idle timeout for managed connection pooling.',
      hidden=True,
      actions=actions.DeprecationAction(
          '--connection-pooling-server-idle-timeout',
          error=(
              'This flag is deprecated. Please use --connection-pool-flags'
              ' instead.'
          ),
          removed=True,
      ),
  )


def AddConnectionPoolingQueryWaitTimeout(parser):
  """Adds --connection-pooling-query-wait-timeout flag.

  Args:
    parser: The current argparse parser to add this to.
  """
  parser.add_argument(
      '--connection-pooling-query-wait-timeout',
      required=False,
      default=None,
      help='The query wait timeout for managed connection pooling.',
      hidden=True,
      actions=actions.DeprecationAction(
          '--connection-pooling-query-wait-timeout',
          error=(
              'This flag is deprecated. Please use --connection-pool-flags'
              ' instead.'
          ),
          removed=True,
      ),
  )


def AddServerCaPool(parser, hidden=False):
  """Adds the '--server-ca-pool' flag to the parser.

  Args:
    parser: The current argparse parser to add this to.
    hidden: if the field needs to be hidden.
  """
  help_text = 'Set the server CA pool of the instance.'
  parser.add_argument(
      '--server-ca-pool',
      required=False,
      default=None,
      help=help_text,
      hidden=hidden,
  )


def AddInstanceType(parser):
  """Adds --instance-type flag.

  Args:
    parser: The current argparse parser to add this to.
  """
  parser.add_argument(
      '--instance-type',
      choices={
          'CLOUD_SQL_INSTANCE': 'A primary instance.',
          'READ_REPLICA_INSTANCE': 'A read replica instance.',
          'READ_POOL_INSTANCE': 'A read pool instance.',
      },
      required=False,
      default=None,
      help='The type of the instance.',
      hidden=False,
  )


def AddNodeCount(parser):
  """Adds --node-count flag.

  Args:
    parser: The current argparse parser to add this to.
  """
  parser.add_argument(
      '--node-count',
      required=False,
      default=None,
      type=arg_parsers.BoundedInt(lower_bound=1),
      help=(
          'The number of nodes in the pool. This option is only available for'
          ' read pools.'
      ),
      hidden=False,
  )


def AddPSCNetworkAttachmentUri(parser, hidden=False):
  """Adds the `--psc-network-attachment-uri` flag to the parser."""
  parser.add_argument(
      '--psc-network-attachment-uri',
      required=False,
      hidden=hidden,
      type=str,
      help=(
          'Full URI of the network attachment that is configured to '
          'support outbound connectivity from a Cloud SQL instance which '
          'uses Private Service Connect (PSC). '
          'For example, this would be of the form:'
          '`--psc-network-attachment-uri=projects/test-project/regions/us-central1/networkAttachments/my-na`'
      ),
  )


def AddClearPSCNetworkAttachmentUri(parser, hidden=False):
  parser.add_argument(
      '--clear-psc-network-attachment-uri',
      action='store_true',
      hidden=hidden,
      help="""Disable outbound connectivity from a Cloud SQL instance which uses Private Service Connect (PSC).""",
  )


def AddEnableAcceleratedReplicaMode(parser):
  """Adds --enable-accelerated-replica-mode flag."""
  parser.add_argument(
      '--enable-accelerated-replica-mode',
      required=False,
      help=(
          'Accelerated replica mode improves the write performance of'
          ' the replica instance by reducing its durability. In case of'
          ' replica instance crash, it will be recreated from the primary'
          ' instance.'
      ),
      action=arg_parsers.StoreTrueFalseAction,
      hidden=True,
  )


def AddUncMappings(parser, hidden=True):
  """Adds --unc-mappings argument."""
  parser.add_argument(
      '--unc-mappings',
      type=arg_parsers.ArgList(min_length=1),
      required=False,
      metavar='SERVER-NAME=GCS-PATH:MODE',
      help=(
          'A comma-separated list of UNC mapping to add to the SQL Server'
          ' instance. The input should be in a format of'
          ' server-name=gcs-path:mode where mode should be snapshot_read or'
          ' snapshot_write Example:'
          ' \\serverA\123456=gs://bucket/folder:snapshot_read'
      ),
      hidden=hidden,
      action=arg_parsers.FlattenAction(),
  )


def AddClearUncMappings(parser, hidden=True):
  parser.add_argument(
      '--clear-unc-mappings',
      action='store_true',
      hidden=hidden,
      help="""Clear the UNC mappings for the SQL Server instance.""",
  )


def AddSqlServerEntraId(parser, hidden=False):
  """Adds the `--entra-id-tenant-id` and `--entra-id-application-id` flags to the parser."""

  entraid_group = parser.add_group(
      required=False,
      help='Entraid configuration for the SQL Server instance.',
      hidden=hidden,
  )

  entraid_group.add_argument(
      '--entra-id-tenant-id',
      required=True,
      help='Set the Entraid tenant ID. ',
      hidden=hidden,
  )

  entraid_group.add_argument(
      '--entra-id-application-id',
      required=True,
      help='Set the Entraid application ID. ',
      hidden=hidden,
  )


def AddClearEntraIdConfig(parser, hidden=False):
  parser.add_argument(
      '--clear-entra-id-config',
      action='store_true',
      hidden=hidden,
      help="""Clears the Entra ID config for the SQL Server instance.""",
  )


def AddPerformanceCaptureConfig(parser, hidden=False):
  """Adds --performance-capture-config argument."""
  parser.add_argument(
      '--performance-capture-config',
      type=arg_parsers.ArgDict(min_length=1),
      required=False,
      metavar='KEY=VALUE',
      help=(
          'A comma-separated list of performance capture settings to add to the'
          ' MySQL instance. The input should be in a format of key=value.'
          ' Available case-sensitive keys are: enabled (boolean),'
          ' probing-interval-seconds (integer), probe-threshold (integer),'
          ' running-threads-threshold (integer),'
          ' seconds-behind-source-threshold (integer),'
          ' transaction-duration-threshold (integer),'
          ' cpu-utilization-threshold-percent (integer),'
          ' memory-usage-threshold-percent (integer),'
          ' transaction-lock-wait-threshold-count (integer),'
          ' semaphore-wait-threshold-count (integer),'
          ' history-list-length-threshold-count (integer),'
          ' transaction-kill-threshold-seconds (integer),'
          ' transaction-kill-type (string),'
          ' transaction-kill-excluded-user-hosts (string, semicolon-separated'
          ' list) Example: --performance-capture-config'
          ' enabled=true,probe-threshold=5,'
          ' cpu-utilization-threshold-percent=80,'
          ' transaction-kill-excluded-user-hosts=user1@host1;user2@%,'
          ' transaction-kill-type=READ_ONLY_TRANSACTIONS'
      ),
      hidden=hidden,
  )


def AddDatabaseRoles(parser, required=False):
  """Add the flag to specify database roles for the user.

  Args:
    parser: The current argparse parser to add this to.
    required: Whether the flag is required.
  """
  parser.add_argument(
      '--database-roles',
      required=required,
      default=[],
      metavar='ROLE',
      type=arg_parsers.ArgList(),
      help="""\
        A comma-separated list of database roles to be assigned to the user.
        This option is only available for MySQL and PostgreSQL instances. You
        can include predefined Cloud SQL roles, like cloudsqlsuperuser, or your
        own custom roles. Custom roles must be created in the database before
        you can assign them. You can create roles using the CREATE ROLE
        statement for both MySQL and PostgreSQL.
      """,
  )


def AddRevokeExistingRoles(parser):
  """Add the flag to revoke existing database roles for the user.

  Args:
    parser: The current argparse parser to add this to.
  """
  parser.add_argument(
      '--revoke-existing-roles',
      required=False,
      action='store_true',
      help="""\
        A boolean flag for revoking existing database roles from the user.
        This option is only available for MySQL and PostgreSQL instances.
      """,
  )


# Flags for instance settings that can be overridden during restore to new
# instance and cross project PITR.
def AddSourceInstanceOverrideArgs(
    parser: parser_extensions.Namespace
) -> None:
  """Add flags that can be overridden from the source instance.

  These flags are used when creating a new Cloud SQL instance via a backup
  restore or PITR operation.

  Args:
    parser: The current argparse parser to add this to.
  """
  parser.display_info.AddFormat(GetInstanceListFormat())
  psc_setup_group = parser.add_group()

  # go/keep-sorted start
  AddActivationPolicy(parser)
  AddActiveDirectoryDNSServers(parser)
  AddActiveDirectoryDomain(parser)
  AddActiveDirectoryMode(parser)
  AddActiveDirectoryOrganizationalUnit(parser)
  AddActiveDirectorySecretManagerKey(parser)
  AddAllowedPscProjects(psc_setup_group)
  AddAssignIp(parser)
  AddAuthorizedNetworks(parser)
  AddAvailabilityType(parser)
  AddBackup(parser)
  AddBackupLocation(parser, allow_empty=False)
  AddBackupStartTime(parser)
  AddCPU(parser)
  AddClearActiveDirectory(parser)
  AddClearDiskEncryption(parser)
  AddClearNetwork(parser)
  AddConnectorEnforcement(parser)
  AddDatabaseVersion(
      parser,
      restrict_choices=False,
      support_default_version=False,
      additional_help_text=(
          ' Note for restore to new instance major version upgrades are not'
          ' supported. Only minor version upgrades are allowed.'
      ),
  )
  AddDeletionProtection(parser)
  AddDenyMaintenancePeriodEndDate(parser)
  AddDenyMaintenancePeriodStartDate(parser)
  AddDenyMaintenancePeriodTime(parser)
  AddEdition(parser)
  AddEnableBinLog(parser)
  AddEnableConfidentialStorage(parser)
  AddEnableDataCache(parser, hidden=True)
  AddEnableDataplexIntegration(parser, hidden=True)
  AddEnableGoogleMLIntegration(parser, hidden=True)
  AddEnableGooglePrivatePath(
      parser, show_negated_in_help=False
  )
  AddEnablePointInTimeRecovery(parser)
  AddEnablePrivateServiceConnect(psc_setup_group)
  AddFinalBackup(parser)
  AddFinalbackupRetentionDays(parser)
  AddInsightsConfigEnhancedQueryInsightsEnabled(parser)
  AddInsightsConfigQueryInsightsEnabled(parser)
  AddInsightsConfigQueryPlansPerMinute(parser)
  AddInsightsConfigQueryStringLength(parser)
  AddInsightsConfigRecordApplicationTags(parser)
  AddInsightsConfigRecordClientAddress(parser)
  AddInstanceCollation(parser)
  AddLocationGroup(parser, specify_default_region=False)
  AddMaintenanceReleaseChannel(parser)
  AddMaintenanceWindowDay(parser)
  AddMaintenanceWindowHour(parser)
  AddMemory(parser)
  AddNetwork(parser)
  AddPscAutoConnections(parser, hidden=True)
  AddReadPoolAutoScaleConfig(parser, hidden=True)
  AddRequireSsl(parser)
  AddRetainBackupsOnDelete(parser, hidden=True)
  AddRetainedBackupsCount(parser)
  AddRetainedTransactionLogDays(parser)
  AddServerCaMode(parser, hidden=True)
  AddSqlServerAudit(parser)
  AddSqlServerTimeZone(parser)
  AddSslMode(parser)
  AddStorageAutoIncrease(parser)
  AddStorageProvisionedIops(parser)
  AddStorageProvisionedThroughput(parser)
  AddStorageSize(parser)
  AddStorageType(parser)
  AddTags(parser, hidden=True)
  AddTier(parser)
  AddTimeout(
      parser, constants.INSTANCE_CREATION_TIMEOUT_SECONDS
  )
  ClearActiveDirectoryDNSServers(parser)
  kms_flag_overrides = {
      'kms-key': '--disk-encryption-key',
      'kms-keyring': '--disk-encryption-key-keyring',
      'kms-location': '--disk-encryption-key-location',
      'kms-project': '--disk-encryption-key-project',
  }
  kms_resource_args.AddKmsKeyResourceArg(
      parser, 'instance', flag_overrides=kms_flag_overrides
  )
  # go/keep-sorted end


def AddEnableConfidentialStorage(parser, hidden=True):
  """Adds --enable-confidential-storage flag."""
  parser.add_argument(
      '--enable-confidential-storage',
      required=False,
      hidden=hidden,
      help='Enable Confidential Storage.',
      action=arg_parsers.StoreTrueFalseAction,
  )


def AddDataApiAccess(parser):
  """Adds --data-api-access flag."""
  parser.add_argument(
      '--data-api-access',
      choices={
          'DATA_API_ACCESS_UNSPECIFIED': (
              'Unspecified mode, effectively the same as `DISALLOW_DATA_API`.'
          ),
          'DISALLOW_DATA_API': (
              'Disallow using ExecuteSql API to connect to the instance.'
          ),
          'ALLOW_DATA_API': (
              'Allow using ExecuteSql API to connect to the instance. For'
              ' Private IP instances, this will allow authorized users to'
              ' access the instance from the public internet using ExecuteSql'
              ' API.'
          ),
      },
      required=False,
      default=None,
      help='Controls connectivity to the instance using ExecuteSql API.',
  )


def AddServerCertificateRotationMode(parser, hidden=False):
  """Adds the '--server-certificate-rotation-mode' flag to the parser.

  Args:
    parser: The current argparse parser to add this to.
    hidden: if the field needs to be hidden.
  """
  help_text = 'Set the server certificate rotation mode of the instance.'
  parser.add_argument(
      '--server-certificate-rotation-mode',
      choices={
          'NO_AUTOMATIC_ROTATION': (
              'No automatic server certificate rotation. Server certificates'
              ' must be rotated manually.'
          ),
          'AUTOMATIC_ROTATION_DURING_MAINTENANCE': (
              'Automatic server certificate rotation during Cloud SQL'
              ' scheduled maintenance or self-service maintenance updates.'
              ' Requires `server_ca_mode` to be `GOOGLE_MANAGED_CAS_CA` or'
              ' `CUSTOMER_MANAGED_CAS_CA`.'
          ),
      },
      required=False,
      default=None,
      help=help_text,
      hidden=hidden,
  )


def GetInstanceClearOverrides(args: parser_extensions.Namespace) -> list[str]:
  """Returns a list of source instance settings that should be cleared when creating the new target instance.

  Args:
    args: argparse.Namespace, The arguments that this command was invoked with.

  Returns:
    An array with the database instance fields that should be cleared.
  """
  cleared_fields = []

  if args.IsKnownAndSpecified('clear_network'):
    cleared_fields.append('settings.ip_configuration.private_network')

  if args.clear_active_directory_dns_servers:
    cleared_fields.append('settings.active_directory_config.dns_servers')

  if args.IsKnownAndSpecified('clear_disk_encryption'):
    cleared_fields.append('disk_encryption_config')

  if args.clear_active_directory:
    cleared_fields.append('settings.active_directory_config')

  return cleared_fields
