text: # -*- coding: utf-8 -*-
  
  from typing import Union
  from cdpy.common import CdpSdkBase, Squelch, CdpError, CdpWarning
  
  
  class CdpyEnvironments(CdpSdkBase):
      def __init__(self, *args, **kwargs):
          super().__init__(*args, **kwargs)
  
      def get_credential_prerequisites(self, cloud: str):
          return self.sdk.call(svc='environments', func='get_credential_prerequisites', cloudPlatform=cloud.upper())
  
      def describe_proxy_config(self, name):
          resp = self.list_proxy_configs(name)
          return self.sdk.first_item_if_exists(resp)
  
      def create_proxy_config(self, name, host=None, port=None, protocol=None, description=None,
                              user=None, password=None):
          return self.sdk.call(
              svc='environments', func='create_proxy_config', ret_field='proxyConfig',
              proxyConfigName=name, host=host, port=port,
              protocol=protocol, description=description, user=user, password=password
          )
  
      def delete_proxy_config(self, name):
          return self.sdk.call(
              svc='environments', func='delete_proxy_config', ret_field='credentials', squelch=[Squelch('NOT_FOUND')],
              proxyConfigName=name
          )
  
      def list_proxy_configs(self, name=None):
          return self.sdk.call(
              svc='environments', func='list_proxy_configs', ret_field='proxyConfigs', squelch=[
                  Squelch('NOT_FOUND', default=list())
              ],
              proxyConfigName=name
          )
  
      def get_id_broker_mapping_sync(self, name):
          return self.sdk.call(
              svc='environments', func='get_id_broker_mappings_sync_status', squelch=[
                  Squelch('NOT_FOUND'), Squelch('INVALID_ARGUMENT')
              ],
              environmentName=name
          )
  
      def get_id_broker_mappings(self, name):
          return self.sdk.call(
              svc='environments', func='get_id_broker_mappings', squelch=[Squelch('NOT_FOUND')],
              environmentName=name
          )
  
      def describe_environment(self, name):
          resp = self.sdk.call(
              svc='environments', func='describe_environment', ret_field='environment', ret_error=True,
              environmentName=name
          )
          if isinstance(resp, CdpError):
              if resp.error_code == 'NOT_FOUND':
                  # Describe will fail in certain fault scenarios early in Environment creation
                  # We helpfully provide the summary listing as a backup set of information
                  # If the environment truly does not exist, then this will give the same response
                  self.sdk.throw_warning(CdpWarning(str(resp.violations)))
                  return self.summarize_environment(name)
              self.sdk.throw_error(resp)
          return resp
  
      def describe_all_environments(self):
          envs_listing = self.list_environments()
          if envs_listing is not None:
              return [self.describe_environment(env['environmentName']) for env in envs_listing]
          else:
              return list()
  
      def summarize_environment(self, name):
          result = self.list_environments()
          if result:
              for env in result:
                  if env['environmentName'] == name:
                      return env
          return None
  
      def gather_idbroker_mappings(self, name):
          results = dict()
          mappings = self.get_id_broker_mappings(name)
          if mappings is not None:
              results.update(**mappings)
          broker_sync_status = self.get_id_broker_mapping_sync(name)
          if broker_sync_status is not None:
              results.update(syncStatus=broker_sync_status)
          return results
  
      def list_environments(self):
          return self.sdk.call(
              svc='environments', func='list_environments', ret_field='environments', squelch=[
                  Squelch('NOT_FOUND', default=list(), warning='No Environments found in CDP Tenant')]
          )
  
      def create_aws_environment(self, **kwargs):
          # TODO: Rework with named kwargs
          resp = self.sdk.call(
              svc='environments', func='create_aws_environment', ret_field='environment', ret_error=True,
              **kwargs
          )
          if isinstance(resp, CdpError):
              if resp.error_code == 'INVALID_ARGUMENT':
                  if 'constraintViolations' not in str(resp.violations):
                      resp.update(message="Received violation warning:\n%s" % self.sdk.dumps(str(resp.violations)))
                      self.sdk.throw_warning(CdpWarning(str(resp.violations)))
              self.sdk.throw_error(resp)
          return resp
  
      def create_azure_environment(self, **kwargs):
          # TODO: Rework with named kwargs
          resp = self.sdk.call(
              svc='environments', func='create_azure_environment', ret_field='environment', ret_error=True,
              **kwargs
          )
          if isinstance(resp, CdpError):
              if resp.error_code == 'INVALID_ARGUMENT':
                  if 'constraintViolations' not in str(resp.violations):
                      resp.update(message="Received violation warning:\n%s" % self.sdk.dumps(str(resp.violations)))
                      self.sdk.throw_warning(CdpWarning(str(resp.violations)))
              self.sdk.throw_error(resp)
          return resp
  
      def create_gcp_environment(self, **kwargs):
          # TODO: Rework with named kwargs
          resp = self.sdk.call(
              svc='environments', func='create_gcp_environment', ret_field='environment', ret_error=True,
              **kwargs
          )
          if isinstance(resp, CdpError):
              if resp.error_code == 'INVALID_ARGUMENT':
                  if 'constraintViolations' not in str(resp.violations):
                      resp.update(message="Received violation warning:\n%s" % self.sdk.dumps(str(resp.violations)))
                      self.sdk.throw_warning(CdpWarning(str(resp.violations)))
              self.sdk.throw_error(resp)
          return resp
  
      def stop_environment(self, name):
          return self.sdk.call(
              svc='environments', func='stop_environment', ret_field='environment', squelch=[
                  Squelch(field='error_code', value='CONFLICT', default=self.describe_environment(name),
                          warning='Environment %s is already scheduled to stop' % name)
              ],
              environmentName=name
          )
  
      def delete_environment(self, name, cascade=False, force=False):
          if cascade:
              self.sdk.throw_warning(CdpWarning('Cascading Environment deletions currently not supported'))
          if force:
              self.sdk.throw_warning(CdpWarning('Forced Environment deletions currently not supported'))
          return self.sdk.call(
              svc='environments', func='delete_environment', ret_field='environment',
              environmentName=name
          )
  
      def start_environment(self, name):
          return self.sdk.call(
              svc='environments', func='start_environment', ret_field='environment', squelch=[
                  Squelch(field='error_code', value='CONFLICT', default=self.describe_environment(name),
                          warning='Environment %s is already scheduled to start' % name)
              ],
              environmentName=name
          )
  
      def set_password(self, password, environment_names=None):
          payload = dict(password=password)
          if environment_names is not None:
              if not isinstance(environment_names, list):
                  environment_names = [environment_names]
              environment_crns = []
              for env in environment_names:
                  r = self.describe_environment(env)
                  if r is not None:
                      environment_crns.append(r['crn'])
                  else:
                      self.sdk.throw_error(CdpError("Environment not found"))
              payload.update(environmentCRNs=environment_crns)
          return self.sdk.call(
              svc='environments', func='set_password', squelch=[
                  Squelch(field='error_code', value='CONFLICT', default=None,
                          warning='Password Update Conflict')
              ],
              **payload
          )
  
      def sync_current_user(self):
          return self.sdk.call(svc='environments', func='sync_user')
  
      def sync_users(self, environments=None):
          if isinstance(environments, list):
              if len(environments) == 0:
                  environments = None
          elif isinstance(environments, str):
              environments = [environments]
          else:
              self.sdk.throw_error(CdpError("environments must be a list of one or more strings or a string"))
          resp = self.sdk.call(
              svc='environments', func='sync_all_users', ret_error=True,
              environmentNames=environments
          )
          if isinstance(resp, CdpError):
              if resp.error_code == 'CONFLICT':
                  operation_match = self.sdk.regex_search(self.sdk.OPERATION_REGEX, str(resp.violations))
                  if operation_match is not None:
                      existing_op_id = operation_match.group(1)
                      if not self.sdk.strict_errors:
                          self.sdk.throw_warning(
                              CdpWarning('Sync All Users Operation already running, tracking existing job {0}'
                                         .format(existing_op_id)))
                          return self.get_sync_status(existing_op_id)
              self.sdk.throw_error(resp)
          return resp
  
      def get_sync_status(self, operation):
          return self.sdk.call(
              svc='environments', func='sync_status', squelch=[
                  Squelch(field='error_code', value='NOT_FOUND', default=None,
                          warning='No User Sync Operation found matching %s' % operation)
              ],
              operationId=operation
          )
  
      def get_keytab(self, actor, environment):
          return self.sdk.call(
              svc='environments', func='get_keytab', ret_field='contents',
              actorCrn=actor,
              environmentName=environment
          )
  
      def list_credentials(self, name=None):
          return self.sdk.call(
              svc='environments', func='list_credentials', ret_field='credentials', squelch=[
                  Squelch('NOT_FOUND', default=list())],
              credentialName=name
          )
  
      def describe_credential(self, name):
          resp = self.list_credentials(name)
          # Return singular None if credential not found, rather than default list()
          return self.sdk.first_item_if_exists(resp) if resp else None
  
      def delete_credential(self, name):
          return self.sdk.call(
              svc='environments', func='delete_credential', ret_field='credentials', squelch=[Squelch('NOT_FOUND')],
              credentialName=name
          )
  
      def create_aws_credential(self, name, role, description, retries=3, delay=2):
          resp = self.sdk.call(
              svc='environments', func='create_aws_credential', ret_error=True, squelch=[
                  Squelch(field='violations', value='Credential already exists with name',
                          warning='Credential with this name already exists', default=None)],
              credentialName=name,
              roleArn=role,
              description=description
          )
          if isinstance(resp, CdpError):
              if retries > 0:
                  consistency_violations = [
                      'Unable to verify credential', 'sts:AssumeRole', 'You are not authorized'
                  ]
                  if any(x in str(resp.violations) for x in consistency_violations):
                      retries = retries - 1
                      self.sdk.throw_warning(
                          CdpWarning('Got likely AWS IAM eventual consistency error [%s], %d retries left...'
                                     % (str(resp.violations), retries))
                      )
                      self.sdk.sleep(delay)
                      return self.create_aws_credential(name, role, description, retries, delay)
              else:
                  self.sdk.throw_error(resp)
          return resp
  
      def create_azure_credential(self, name, subscription, tenant, application, secret, retries=3, delay=5):
          resp = self.sdk.call(
              svc='environments', func='create_azure_credential', ret_error=True, squelch=[
                  Squelch(field='violations', value='Credential already exists with name',
                          warning='Credential with this name already exists', default=None)],
              credentialName=name,
              subscriptionId=subscription,
              tenantId=tenant,
              appBased={'applicationId': application, 'secretKey': secret}
          )
          if isinstance(resp, CdpError):
              if retries > 0:
                  consistency_violations = [
                      'You may have sent your authentication request to the wrong tenant'
                  ]
                  if any(x in str(resp.violations) for x in consistency_violations):
                      retries = retries - 1
                      self.sdk.throw_warning(
                          CdpWarning('Got likely Azure eventual consistency error [%s], %d retries left...'
                                     % (str(resp.violations), retries))
                      )
                      self.sdk.sleep(delay)
                      return self.create_azure_credential(name, subscription, tenant, application, secret, retries, delay)
              else:
                  self.sdk.throw_error(resp)
          return resp
  
      def create_gcp_credential(self, name, key_file):
          return self.sdk.call(
              svc='environments', func='create_gcp_credential', squelch=[
                  Squelch(field='violations', value='Credential already exists with name',
                          warning='Credential with this name already exists', default=None)],
              credentialName=name,
              credentialKey=self.sdk.read_file(key_file)
          )
  
      def get_root_cert(self, environment):
          return self.sdk.call(
              svc='environments', func='get_root_certificate', ret_field='contents',
              environmentName=environment
          )
  
      def set_telemetry(self, name, workload_analytics=None, logs_collection=None):
          return self.sdk.call(
              svc='environments', func='set_telemetry_features',
              environmentName=name,
              workloadAnalytics=workload_analytics,
              reportDeploymentLogs=logs_collection
          )
  
      def resolve_environment_crn(self, env: Union[str, None]):
          """Ensures a given env string is either the environment crn or None"""
          if isinstance(env, str):
              if env.startswith('crn:'):
                  return env
              else:
                  env_desc = self.describe_environment(env)
                  return env_desc['crn'] if env_desc else None
          else:
              return None
  